aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/corelib
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@digia.com>2014-01-09 17:50:40 +0100
committerJoerg Bornemann <joerg.bornemann@digia.com>2014-01-10 18:11:22 +0100
commit81af9acaa295a574c1cb5e6714725197dac7f530 (patch)
treecc8c94467f49a7d267e5249f624874feecc7eed4 /src/lib/corelib
parent2fe25eb3f20ffb4e58cb559f2bcb9950c963290a (diff)
Move Qt profile setup into a dedicated library.
Otherwise all changes to the implementation will have to be duplicated in IDEs. Change-Id: I61e6d4fa1ee9b724eb5d9de9f233dc915a6c8bc3 Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
Diffstat (limited to 'src/lib/corelib')
-rw-r--r--src/lib/corelib/api/api.pri27
-rw-r--r--src/lib/corelib/api/changeset.cpp387
-rw-r--r--src/lib/corelib/api/changeset.h130
-rw-r--r--src/lib/corelib/api/internaljobs.cpp429
-rw-r--r--src/lib/corelib/api/internaljobs.h224
-rw-r--r--src/lib/corelib/api/jobs.cpp320
-rw-r--r--src/lib/corelib/api/jobs.h148
-rw-r--r--src/lib/corelib/api/project.cpp1027
-rw-r--r--src/lib/corelib/api/project.h143
-rw-r--r--src/lib/corelib/api/projectdata.cpp674
-rw-r--r--src/lib/corelib/api/projectdata.h217
-rw-r--r--src/lib/corelib/api/projectdata_p.h108
-rw-r--r--src/lib/corelib/api/projectfileupdater.cpp475
-rw-r--r--src/lib/corelib/api/projectfileupdater.h126
-rw-r--r--src/lib/corelib/api/propertymap_p.h47
-rw-r--r--src/lib/corelib/api/qmljsrewriter.cpp718
-rw-r--r--src/lib/corelib/api/qmljsrewriter.h120
-rw-r--r--src/lib/corelib/api/runenvironment.cpp176
-rw-r--r--src/lib/corelib/api/runenvironment.h71
-rw-r--r--src/lib/corelib/buildgraph/abstractcommandexecutor.cpp66
-rw-r--r--src/lib/corelib/buildgraph/abstractcommandexecutor.h85
-rw-r--r--src/lib/corelib/buildgraph/artifact.cpp115
-rw-r--r--src/lib/corelib/buildgraph/artifact.h135
-rw-r--r--src/lib/corelib/buildgraph/artifactcleaner.cpp209
-rw-r--r--src/lib/corelib/buildgraph/artifactcleaner.h62
-rw-r--r--src/lib/corelib/buildgraph/artifactlist.cpp58
-rw-r--r--src/lib/corelib/buildgraph/artifactlist.h112
-rw-r--r--src/lib/corelib/buildgraph/artifactvisitor.cpp67
-rw-r--r--src/lib/corelib/buildgraph/artifactvisitor.h60
-rw-r--r--src/lib/corelib/buildgraph/automoc.cpp343
-rw-r--r--src/lib/corelib/buildgraph/automoc.h95
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.cpp498
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.h95
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.pri65
-rw-r--r--src/lib/corelib/buildgraph/buildgraphloader.cpp826
-rw-r--r--src/lib/corelib/buildgraph/buildgraphloader.h124
-rw-r--r--src/lib/corelib/buildgraph/command.cpp287
-rw-r--r--src/lib/corelib/buildgraph/command.h142
-rw-r--r--src/lib/corelib/buildgraph/cycledetector.cpp93
-rw-r--r--src/lib/corelib/buildgraph/cycledetector.h63
-rw-r--r--src/lib/corelib/buildgraph/executor.cpp905
-rw-r--r--src/lib/corelib/buildgraph/executor.h148
-rw-r--r--src/lib/corelib/buildgraph/executorjob.cpp156
-rw-r--r--src/lib/corelib/buildgraph/executorjob.h88
-rw-r--r--src/lib/corelib/buildgraph/filedependency.cpp92
-rw-r--r--src/lib/corelib/buildgraph/filedependency.h77
-rw-r--r--src/lib/corelib/buildgraph/forward_decls.h51
-rw-r--r--src/lib/corelib/buildgraph/inputartifactscanner.cpp369
-rw-r--r--src/lib/corelib/buildgraph/inputartifactscanner.h124
-rw-r--r--src/lib/corelib/buildgraph/jscommandexecutor.cpp190
-rw-r--r--src/lib/corelib/buildgraph/jscommandexecutor.h71
-rw-r--r--src/lib/corelib/buildgraph/processcommandexecutor.cpp346
-rw-r--r--src/lib/corelib/buildgraph/processcommandexecutor.h89
-rw-r--r--src/lib/corelib/buildgraph/productbuilddata.cpp60
-rw-r--r--src/lib/corelib/buildgraph/productbuilddata.h63
-rw-r--r--src/lib/corelib/buildgraph/productinstaller.cpp189
-rw-r--r--src/lib/corelib/buildgraph/productinstaller.h71
-rw-r--r--src/lib/corelib/buildgraph/projectbuilddata.cpp408
-rw-r--r--src/lib/corelib/buildgraph/projectbuilddata.h110
-rw-r--r--src/lib/corelib/buildgraph/rulegraph.cpp174
-rw-r--r--src/lib/corelib/buildgraph/rulegraph.h73
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.cpp342
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.h77
-rw-r--r--src/lib/corelib/buildgraph/rulesevaluationcontext.cpp116
-rw-r--r--src/lib/corelib/buildgraph/rulesevaluationcontext.h86
-rw-r--r--src/lib/corelib/buildgraph/scanresultcache.cpp61
-rw-r--r--src/lib/corelib/buildgraph/scanresultcache.h87
-rw-r--r--src/lib/corelib/buildgraph/timestampsupdater.cpp84
-rw-r--r--src/lib/corelib/buildgraph/timestampsupdater.h50
-rw-r--r--src/lib/corelib/buildgraph/transformer.cpp273
-rw-r--r--src/lib/corelib/buildgraph/transformer.h87
-rw-r--r--src/lib/corelib/buildgraph/tst_buildgraph.cpp116
-rw-r--r--src/lib/corelib/buildgraph/tst_buildgraph.h67
-rw-r--r--src/lib/corelib/corelib.pro31
-rw-r--r--src/lib/corelib/corelib.qbs341
-rw-r--r--src/lib/corelib/jsextensions/domxml.cpp370
-rw-r--r--src/lib/corelib/jsextensions/domxml.h118
-rw-r--r--src/lib/corelib/jsextensions/file.cpp134
-rw-r--r--src/lib/corelib/jsextensions/file.h44
-rw-r--r--src/lib/corelib/jsextensions/jsextensions.cpp65
-rw-r--r--src/lib/corelib/jsextensions/jsextensions.h59
-rw-r--r--src/lib/corelib/jsextensions/jsextensions.pri17
-rw-r--r--src/lib/corelib/jsextensions/moduleproperties.cpp154
-rw-r--r--src/lib/corelib/jsextensions/moduleproperties.h62
-rw-r--r--src/lib/corelib/jsextensions/process.cpp223
-rw-r--r--src/lib/corelib/jsextensions/process.h91
-rw-r--r--src/lib/corelib/jsextensions/textfile.cpp185
-rw-r--r--src/lib/corelib/jsextensions/textfile.h74
-rw-r--r--src/lib/corelib/language/artifactproperties.cpp65
-rw-r--r--src/lib/corelib/language/artifactproperties.h69
-rw-r--r--src/lib/corelib/language/asttools.cpp58
-rw-r--r--src/lib/corelib/language/asttools.h47
-rw-r--r--src/lib/corelib/language/builtindeclarations.cpp401
-rw-r--r--src/lib/corelib/language/builtindeclarations.h78
-rw-r--r--src/lib/corelib/language/builtinvalue.cpp47
-rw-r--r--src/lib/corelib/language/builtinvalue.h65
-rw-r--r--src/lib/corelib/language/evaluationdata.h71
-rw-r--r--src/lib/corelib/language/evaluator.cpp221
-rw-r--r--src/lib/corelib/language/evaluator.h88
-rw-r--r--src/lib/corelib/language/evaluatorscriptclass.cpp577
-rw-r--r--src/lib/corelib/language/evaluatorscriptclass.h98
-rw-r--r--src/lib/corelib/language/filecontext.cpp52
-rw-r--r--src/lib/corelib/language/filecontext.h87
-rw-r--r--src/lib/corelib/language/filetags.cpp106
-rw-r--r--src/lib/corelib/language/filetags.h79
-rw-r--r--src/lib/corelib/language/forward_decls.h120
-rw-r--r--src/lib/corelib/language/functiondeclaration.h61
-rw-r--r--src/lib/corelib/language/identifiersearch.cpp69
-rw-r--r--src/lib/corelib/language/identifiersearch.h59
-rw-r--r--src/lib/corelib/language/importversion.cpp77
-rw-r--r--src/lib/corelib/language/importversion.h58
-rw-r--r--src/lib/corelib/language/item.cpp158
-rw-r--r--src/lib/corelib/language/item.h282
-rw-r--r--src/lib/corelib/language/itemdeclaration.cpp59
-rw-r--r--src/lib/corelib/language/itemdeclaration.h67
-rw-r--r--src/lib/corelib/language/itemobserver.h50
-rw-r--r--src/lib/corelib/language/itempool.cpp54
-rw-r--r--src/lib/corelib/language/itempool.h60
-rw-r--r--src/lib/corelib/language/itemreader.cpp199
-rw-r--r--src/lib/corelib/language/itemreader.h110
-rw-r--r--src/lib/corelib/language/itemreaderastvisitor.cpp643
-rw-r--r--src/lib/corelib/language/itemreaderastvisitor.h93
-rw-r--r--src/lib/corelib/language/jsimports.h65
-rw-r--r--src/lib/corelib/language/language.cpp1124
-rw-r--r--src/lib/corelib/language/language.h464
-rw-r--r--src/lib/corelib/language/language.pri70
-rw-r--r--src/lib/corelib/language/loader.cpp122
-rw-r--r--src/lib/corelib/language/loader.h72
-rw-r--r--src/lib/corelib/language/moduleloader.cpp1143
-rw-r--r--src/lib/corelib/language/moduleloader.h213
-rw-r--r--src/lib/corelib/language/preparescriptobserver.cpp62
-rw-r--r--src/lib/corelib/language/preparescriptobserver.h57
-rw-r--r--src/lib/corelib/language/projectresolver.cpp999
-rw-r--r--src/lib/corelib/language/projectresolver.h142
-rw-r--r--src/lib/corelib/language/property.h83
-rw-r--r--src/lib/corelib/language/propertydeclaration.cpp77
-rw-r--r--src/lib/corelib/language/propertydeclaration.h84
-rw-r--r--src/lib/corelib/language/propertymapinternal.cpp103
-rw-r--r--src/lib/corelib/language/propertymapinternal.h64
-rw-r--r--src/lib/corelib/language/scriptengine.cpp314
-rw-r--r--src/lib/corelib/language/scriptengine.h139
-rw-r--r--src/lib/corelib/language/scriptpropertyobserver.h48
-rw-r--r--src/lib/corelib/language/testdata/Banana1
-rw-r--r--src/lib/corelib/language/testdata/aboutdialog.cpp0
-rw-r--r--src/lib/corelib/language/testdata/baseproperty.qbs7
-rw-r--r--src/lib/corelib/language/testdata/baseproperty_base.qbs4
-rw-r--r--src/lib/corelib/language/testdata/buildconfigstringlistsyntax.qbs3
-rw-r--r--src/lib/corelib/language/testdata/builtinFunctionInSearchPathsProperty.qbs8
-rw-r--r--src/lib/corelib/language/testdata/canonicalArchitecture.qbs3
-rw-r--r--src/lib/corelib/language/testdata/conditionaldepends.qbs67
-rw-r--r--src/lib/corelib/language/testdata/conditionaldepends_base.qbs10
-rw-r--r--src/lib/corelib/language/testdata/drawline.asm0
-rw-r--r--src/lib/corelib/language/testdata/environmentvariable.qbs3
-rw-r--r--src/lib/corelib/language/testdata/erroneous/importloop1.qbs5
-rw-r--r--src/lib/corelib/language/testdata/erroneous/importloop2.qbs5
-rw-r--r--src/lib/corelib/language/testdata/erroneous/invalid_child_item_type.qbs6
-rw-r--r--src/lib/corelib/language/testdata/erroneous/invalid_file.qbs5
-rw-r--r--src/lib/corelib/language/testdata/erroneous/invalid_property_type.qbs5
-rw-r--r--src/lib/corelib/language/testdata/erroneous/invalid_stringlist_element.qbs3
-rw-r--r--src/lib/corelib/language/testdata/erroneous/main.cpp1
-rw-r--r--src/lib/corelib/language/testdata/erroneous/multiple_exports.qbs4
-rw-r--r--src/lib/corelib/language/testdata/erroneous/multiple_properties_in_subproject.qbs8
-rw-r--r--src/lib/corelib/language/testdata/erroneous/nonexistentouter.qbs7
-rw-r--r--src/lib/corelib/language/testdata/erroneous/references_cycle.qbs6
-rw-r--r--src/lib/corelib/language/testdata/erroneous/references_cycle2.qbs6
-rw-r--r--src/lib/corelib/language/testdata/erroneous/references_cycle3.qbs6
-rw-r--r--src/lib/corelib/language/testdata/erroneous/reserved_name_in_import.qbs4
-rw-r--r--src/lib/corelib/language/testdata/erroneous/submodule_syntax.qbs3
-rw-r--r--src/lib/corelib/language/testdata/erroneous/subproject_cycle.qbs8
-rw-r--r--src/lib/corelib/language/testdata/erroneous/subproject_cycle2.qbs8
-rw-r--r--src/lib/corelib/language/testdata/erroneous/subproject_cycle3.qbs8
-rw-r--r--src/lib/corelib/language/testdata/erroneous/throw_in_property_binding.qbs7
-rw-r--r--src/lib/corelib/language/testdata/erroneous/undeclared_item.qbs6
-rw-r--r--src/lib/corelib/language/testdata/erroneous/undeclared_property.qbs6
-rw-r--r--src/lib/corelib/language/testdata/erroneous/unknown_item_type.qbs3
-rw-r--r--src/lib/corelib/language/testdata/erroneous/unknown_module.qbs3
-rw-r--r--src/lib/corelib/language/testdata/exports.qbs49
-rw-r--r--src/lib/corelib/language/testdata/exports_product.qbs7
-rw-r--r--src/lib/corelib/language/testdata/filecontextproperties.qbs5
-rw-r--r--src/lib/corelib/language/testdata/filetags.qbs73
-rw-r--r--src/lib/corelib/language/testdata/getNativeSetting.qbs23
-rw-r--r--src/lib/corelib/language/testdata/groupconditions.qbs53
-rw-r--r--src/lib/corelib/language/testdata/groupname.qbs20
-rw-r--r--src/lib/corelib/language/testdata/homeDirectory.qbs18
-rw-r--r--src/lib/corelib/language/testdata/idusage.qbs20
-rw-r--r--src/lib/corelib/language/testdata/idusagebase.qbs5
-rw-r--r--src/lib/corelib/language/testdata/invalidBindingInDisabledItem.qbs16
-rw-r--r--src/lib/corelib/language/testdata/jsextensions.js64
-rw-r--r--src/lib/corelib/language/testdata/jsimportsinmultiplescopes.js12
-rw-r--r--src/lib/corelib/language/testdata/jsimportsinmultiplescopes.qbs7
-rw-r--r--src/lib/corelib/language/testdata/main.cpp0
-rw-r--r--src/lib/corelib/language/testdata/moduleproperties.qbs21
-rw-r--r--src/lib/corelib/language/testdata/modules.qbs34
-rw-r--r--src/lib/corelib/language/testdata/modules/dummy/dummy.qbs8
-rw-r--r--src/lib/corelib/language/testdata/modules/dummy/dummy_base.qbs4
-rw-r--r--src/lib/corelib/language/testdata/modules/dummy2/dummy2.qbs7
-rw-r--r--src/lib/corelib/language/testdata/modules/dummyqt/core/dummycore.qbs14
-rw-r--r--src/lib/corelib/language/testdata/modules/dummyqt/gui/dummygui.qbs9
-rw-r--r--src/lib/corelib/language/testdata/modules/dummyqt/network/dummynetwork.qbs9
-rw-r--r--src/lib/corelib/language/testdata/modules/scopemod/scopemod.qbs12
-rw-r--r--src/lib/corelib/language/testdata/modulescope.qbs14
-rw-r--r--src/lib/corelib/language/testdata/modulescope_base.qbs6
-rw-r--r--src/lib/corelib/language/testdata/narf0
-rw-r--r--src/lib/corelib/language/testdata/narf.zort0
-rw-r--r--src/lib/corelib/language/testdata/nativesettings.ini1
-rw-r--r--src/lib/corelib/language/testdata/outerInGroup.qbs14
-rw-r--r--src/lib/corelib/language/testdata/pathproperties.qbs9
-rw-r--r--src/lib/corelib/language/testdata/productconditions.qbs19
-rw-r--r--src/lib/corelib/language/testdata/productdirectories.qbs5
-rw-r--r--src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs19
-rw-r--r--src/lib/corelib/language/testdata/propertiesblocks.qbs150
-rw-r--r--src/lib/corelib/language/testdata/propertiesblocks_base.qbs11
-rw-r--r--src/lib/corelib/language/testdata/subdir/pathproperties_base.qbs4
-rw-r--r--src/lib/corelib/language/testdata/zort0
-rw-r--r--src/lib/corelib/language/tst_language.cpp1439
-rw-r--r--src/lib/corelib/language/tst_language.h114
-rw-r--r--src/lib/corelib/language/value.cpp91
-rw-r--r--src/lib/corelib/language/value.h168
-rw-r--r--src/lib/corelib/logging/ilogsink.cpp118
-rw-r--r--src/lib/corelib/logging/ilogsink.h81
-rw-r--r--src/lib/corelib/logging/logger.cpp271
-rw-r--r--src/lib/corelib/logging/logger.h137
-rw-r--r--src/lib/corelib/logging/logging.pri14
-rw-r--r--src/lib/corelib/logging/translator.h47
-rw-r--r--src/lib/corelib/parser/parser.pri21
-rw-r--r--src/lib/corelib/parser/qmlerror.cpp282
-rw-r--r--src/lib/corelib/parser/qmlerror.h76
-rw-r--r--src/lib/corelib/parser/qmljs.g3002
-rw-r--r--src/lib/corelib/parser/qmljsast.cpp915
-rw-r--r--src/lib/corelib/parser/qmljsast_p.h2623
-rw-r--r--src/lib/corelib/parser/qmljsastfwd_p.h172
-rw-r--r--src/lib/corelib/parser/qmljsastvisitor.cpp44
-rw-r--r--src/lib/corelib/parser/qmljsastvisitor_p.h315
-rw-r--r--src/lib/corelib/parser/qmljsengine_p.cpp151
-rw-r--r--src/lib/corelib/parser/qmljsengine_p.h115
-rw-r--r--src/lib/corelib/parser/qmljsglobal_p.h52
-rw-r--r--src/lib/corelib/parser/qmljsgrammar.cpp1001
-rw-r--r--src/lib/corelib/parser/qmljsgrammar_p.h201
-rw-r--r--src/lib/corelib/parser/qmljskeywords_p.h852
-rw-r--r--src/lib/corelib/parser/qmljslexer.cpp1141
-rw-r--r--src/lib/corelib/parser/qmljslexer_p.h233
-rw-r--r--src/lib/corelib/parser/qmljsmemorypool_p.h157
-rw-r--r--src/lib/corelib/parser/qmljsparser.cpp1810
-rw-r--r--src/lib/corelib/parser/qmljsparser_p.h230
-rw-r--r--src/lib/corelib/qbs.h45
-rw-r--r--src/lib/corelib/tools/buildoptions.cpp240
-rw-r--r--src/lib/corelib/tools/buildoptions.h79
-rw-r--r--src/lib/corelib/tools/cleanoptions.cpp158
-rw-r--r--src/lib/corelib/tools/cleanoptions.h67
-rw-r--r--src/lib/corelib/tools/codelocation.cpp140
-rw-r--r--src/lib/corelib/tools/codelocation.h72
-rw-r--r--src/lib/corelib/tools/error.cpp196
-rw-r--r--src/lib/corelib/tools/error.h94
-rw-r--r--src/lib/corelib/tools/fileinfo.cpp477
-rw-r--r--src/lib/corelib/tools/fileinfo.h95
-rw-r--r--src/lib/corelib/tools/filetime.h128
-rw-r--r--src/lib/corelib/tools/filetime_unix.cpp73
-rw-r--r--src/lib/corelib/tools/filetime_win.cpp101
-rw-r--r--src/lib/corelib/tools/hostosinfo.h196
-rw-r--r--src/lib/corelib/tools/id.cpp324
-rw-r--r--src/lib/corelib/tools/id.h87
-rw-r--r--src/lib/corelib/tools/installoptions.cpp203
-rw-r--r--src/lib/corelib/tools/installoptions.h73
-rw-r--r--src/lib/corelib/tools/persistence.cpp217
-rw-r--r--src/lib/corelib/tools/persistence.h197
-rw-r--r--src/lib/corelib/tools/persistentobject.h48
-rw-r--r--src/lib/corelib/tools/preferences.cpp133
-rw-r--r--src/lib/corelib/tools/preferences.h63
-rw-r--r--src/lib/corelib/tools/processresult.cpp121
-rw-r--r--src/lib/corelib/tools/processresult.h72
-rw-r--r--src/lib/corelib/tools/processresult_p.h56
-rw-r--r--src/lib/corelib/tools/profile.cpp224
-rw-r--r--src/lib/corelib/tools/profile.h81
-rw-r--r--src/lib/corelib/tools/progressobserver.cpp95
-rw-r--r--src/lib/corelib/tools/progressobserver.h62
-rw-r--r--src/lib/corelib/tools/propertyfinder.cpp107
-rw-r--r--src/lib/corelib/tools/propertyfinder.h62
-rw-r--r--src/lib/corelib/tools/qbs_export.h44
-rw-r--r--src/lib/corelib/tools/qbsassert.cpp50
-rw-r--r--src/lib/corelib/tools/qbsassert.h59
-rw-r--r--src/lib/corelib/tools/qttools.cpp40
-rw-r--r--src/lib/corelib/tools/qttools.h40
-rw-r--r--src/lib/corelib/tools/scannerpluginmanager.cpp115
-rw-r--r--src/lib/corelib/tools/scannerpluginmanager.h66
-rw-r--r--src/lib/corelib/tools/scripttools.cpp130
-rw-r--r--src/lib/corelib/tools/scripttools.h94
-rw-r--r--src/lib/corelib/tools/settings.cpp184
-rw-r--r--src/lib/corelib/tools/settings.h72
-rw-r--r--src/lib/corelib/tools/setupprojectparameters.cpp434
-rw-r--r--src/lib/corelib/tools/setupprojectparameters.h101
-rw-r--r--src/lib/corelib/tools/tools.pri79
-rw-r--r--src/lib/corelib/tools/tst_tools.cpp178
-rw-r--r--src/lib/corelib/tools/tst_tools.h56
-rw-r--r--src/lib/corelib/tools/weakpointer.h60
-rw-r--r--src/lib/corelib/use_corelib.pri47
-rw-r--r--src/lib/corelib/use_installed_corelib.pri38
296 files changed, 49425 insertions, 0 deletions
diff --git a/src/lib/corelib/api/api.pri b/src/lib/corelib/api/api.pri
new file mode 100644
index 000000000..8a0c93237
--- /dev/null
+++ b/src/lib/corelib/api/api.pri
@@ -0,0 +1,27 @@
+HEADERS += \
+ $$PWD/changeset.h \
+ $$PWD/internaljobs.h \
+ $$PWD/projectdata.h \
+ $$PWD/projectfileupdater.h \
+ $$PWD/runenvironment.h \
+ $$PWD/jobs.h \
+ $$PWD/project.h \
+ $$PWD/propertymap_p.h \
+ $$PWD/projectdata_p.h \
+ $$PWD/qmljsrewriter.h
+
+SOURCES += \
+ $$PWD/changeset.cpp \
+ $$PWD/internaljobs.cpp \
+ $$PWD/projectfileupdater.cpp \
+ $$PWD/runenvironment.cpp \
+ $$PWD/projectdata.cpp \
+ $$PWD/jobs.cpp \
+ $$PWD/project.cpp \
+ $$PWD/qmljsrewriter.cpp
+
+!qbs_no_dev_install {
+ api_headers.files = $$PWD/projectdata.h $$PWD/runenvironment.h $$PWD/jobs.h $$PWD/project.h
+ api_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/api
+ INSTALLS += api_headers
+}
diff --git a/src/lib/corelib/api/changeset.cpp b/src/lib/corelib/api/changeset.cpp
new file mode 100644
index 000000000..bbde289a6
--- /dev/null
+++ b/src/lib/corelib/api/changeset.cpp
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** 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 "changeset.h"
+
+#include <QTextCursor>
+
+namespace QbsQmlJS {
+
+ChangeSet::ChangeSet()
+ : m_string(0), m_cursor(0), m_error(false)
+{
+}
+
+ChangeSet::ChangeSet(const QList<EditOp> &operations)
+ : m_string(0), m_cursor(0), m_operationList(operations), m_error(false)
+{
+}
+
+static bool overlaps(int posA, int lengthA, int posB, int lengthB) {
+ if (lengthB > 0) {
+ return
+ // right edge of B contained in A
+ (posA < posB + lengthB && posA + lengthA >= posB + lengthB)
+ // left edge of B contained in A
+ || (posA <= posB && posA + lengthA > posB)
+ // A contained in B
+ || (posB < posA && posB + lengthB > posA + lengthA);
+ } else {
+ return (posB > posA && posB < posA + lengthA);
+ }
+}
+
+bool ChangeSet::hasOverlap(int pos, int length)
+{
+ QListIterator<EditOp> i(m_operationList);
+ while (i.hasNext()) {
+ const EditOp &cmd = i.next();
+
+ switch (cmd.type) {
+ case EditOp::Replace:
+ if (overlaps(pos, length, cmd.pos1, cmd.length1))
+ return true;
+ break;
+
+ case EditOp::Move:
+ if (overlaps(pos, length, cmd.pos1, cmd.length1))
+ return true;
+ if (cmd.pos2 > pos && cmd.pos2 < pos + length)
+ return true;
+ break;
+
+ case EditOp::Insert:
+ if (cmd.pos1 > pos && cmd.pos1 < pos + length)
+ return true;
+ break;
+
+ case EditOp::Remove:
+ if (overlaps(pos, length, cmd.pos1, cmd.length1))
+ return true;
+ break;
+
+ case EditOp::Flip:
+ if (overlaps(pos, length, cmd.pos1, cmd.length1))
+ return true;
+ if (overlaps(pos, length, cmd.pos2, cmd.length2))
+ return true;
+ break;
+
+ case EditOp::Copy:
+ if (overlaps(pos, length, cmd.pos1, cmd.length1))
+ return true;
+ if (cmd.pos2 > pos && cmd.pos2 < pos + length)
+ return true;
+ break;
+
+ case EditOp::Unset:
+ break;
+ }
+ }
+
+ return false;
+}
+
+bool ChangeSet::isEmpty() const
+{
+ return m_operationList.isEmpty();
+}
+
+QList<ChangeSet::EditOp> ChangeSet::operationList() const
+{
+ return m_operationList;
+}
+
+void ChangeSet::clear()
+{
+ m_string = 0;
+ m_cursor = 0;
+ m_operationList.clear();
+ m_error = false;
+}
+
+bool ChangeSet::replace_helper(int pos, int length, const QString &replacement)
+{
+ if (hasOverlap(pos, length))
+ m_error = true;
+
+ EditOp cmd(EditOp::Replace);
+ cmd.pos1 = pos;
+ cmd.length1 = length;
+ cmd.text = replacement;
+ m_operationList += cmd;
+
+ return !m_error;
+}
+
+bool ChangeSet::move_helper(int pos, int length, int to)
+{
+ if (hasOverlap(pos, length)
+ || hasOverlap(to, 0)
+ || overlaps(pos, length, to, 0))
+ m_error = true;
+
+ EditOp cmd(EditOp::Move);
+ cmd.pos1 = pos;
+ cmd.length1 = length;
+ cmd.pos2 = to;
+ m_operationList += cmd;
+
+ return !m_error;
+}
+
+bool ChangeSet::insert(int pos, const QString &text)
+{
+ Q_ASSERT(pos >= 0);
+
+ if (hasOverlap(pos, 0))
+ m_error = true;
+
+ EditOp cmd(EditOp::Insert);
+ cmd.pos1 = pos;
+ cmd.text = text;
+ m_operationList += cmd;
+
+ return !m_error;
+}
+
+bool ChangeSet::replace(const Range &range, const QString &replacement)
+{ return replace(range.start, range.end, replacement); }
+
+bool ChangeSet::remove(const Range &range)
+{ return remove(range.start, range.end); }
+
+bool ChangeSet::move(const Range &range, int to)
+{ return move(range.start, range.end, to); }
+
+bool ChangeSet::flip(const Range &range1, const Range &range2)
+{ return flip(range1.start, range1.end, range2.start, range2.end); }
+
+bool ChangeSet::copy(const Range &range, int to)
+{ return copy(range.start, range.end, to); }
+
+bool ChangeSet::replace(int start, int end, const QString &replacement)
+{ return replace_helper(start, end - start, replacement); }
+
+bool ChangeSet::remove(int start, int end)
+{ return remove_helper(start, end - start); }
+
+bool ChangeSet::move(int start, int end, int to)
+{ return move_helper(start, end - start, to); }
+
+bool ChangeSet::flip(int start1, int end1, int start2, int end2)
+{ return flip_helper(start1, end1 - start1, start2, end2 - start2); }
+
+bool ChangeSet::copy(int start, int end, int to)
+{ return copy_helper(start, end - start, to); }
+
+bool ChangeSet::remove_helper(int pos, int length)
+{
+ if (hasOverlap(pos, length))
+ m_error = true;
+
+ EditOp cmd(EditOp::Remove);
+ cmd.pos1 = pos;
+ cmd.length1 = length;
+ m_operationList += cmd;
+
+ return !m_error;
+}
+
+bool ChangeSet::flip_helper(int pos1, int length1, int pos2, int length2)
+{
+ if (hasOverlap(pos1, length1)
+ || hasOverlap(pos2, length2)
+ || overlaps(pos1, length1, pos2, length2))
+ m_error = true;
+
+ EditOp cmd(EditOp::Flip);
+ cmd.pos1 = pos1;
+ cmd.length1 = length1;
+ cmd.pos2 = pos2;
+ cmd.length2 = length2;
+ m_operationList += cmd;
+
+ return !m_error;
+}
+
+bool ChangeSet::copy_helper(int pos, int length, int to)
+{
+ if (hasOverlap(pos, length)
+ || hasOverlap(to, 0)
+ || overlaps(pos, length, to, 0))
+ m_error = true;
+
+ EditOp cmd(EditOp::Copy);
+ cmd.pos1 = pos;
+ cmd.length1 = length;
+ cmd.pos2 = to;
+ m_operationList += cmd;
+
+ return !m_error;
+}
+
+void ChangeSet::doReplace(const EditOp &replace_helper, QList<EditOp> *replaceList)
+{
+ Q_ASSERT(replace_helper.type == EditOp::Replace);
+
+ {
+ QMutableListIterator<EditOp> i(*replaceList);
+ while (i.hasNext()) {
+ EditOp &c = i.next();
+ if (replace_helper.pos1 <= c.pos1)
+ c.pos1 += replace_helper.text.size();
+ if (replace_helper.pos1 < c.pos1)
+ c.pos1 -= replace_helper.length1;
+ }
+ }
+
+ if (m_string) {
+ m_string->replace(replace_helper.pos1, replace_helper.length1, replace_helper.text);
+ } else if (m_cursor) {
+ m_cursor->setPosition(replace_helper.pos1);
+ m_cursor->setPosition(replace_helper.pos1 + replace_helper.length1, QTextCursor::KeepAnchor);
+ m_cursor->insertText(replace_helper.text);
+ }
+}
+
+void ChangeSet::convertToReplace(const EditOp &op, QList<EditOp> *replaceList)
+{
+ EditOp replace1(EditOp::Replace);
+ EditOp replace2(EditOp::Replace);
+
+ switch (op.type) {
+ case EditOp::Replace:
+ replaceList->append(op);
+ break;
+
+ case EditOp::Move:
+ replace1.pos1 = op.pos1;
+ replace1.length1 = op.length1;
+ replaceList->append(replace1);
+
+ replace2.pos1 = op.pos2;
+ replace2.text = textAt(op.pos1, op.length1);
+ replaceList->append(replace2);
+ break;
+
+ case EditOp::Insert:
+ replace1.pos1 = op.pos1;
+ replace1.text = op.text;
+ replaceList->append(replace1);
+ break;
+
+ case EditOp::Remove:
+ replace1.pos1 = op.pos1;
+ replace1.length1 = op.length1;
+ replaceList->append(replace1);
+ break;
+
+ case EditOp::Flip:
+ replace1.pos1 = op.pos1;
+ replace1.length1 = op.length1;
+ replace1.text = textAt(op.pos2, op.length2);
+ replaceList->append(replace1);
+
+ replace2.pos1 = op.pos2;
+ replace2.length1 = op.length2;
+ replace2.text = textAt(op.pos1, op.length1);
+ replaceList->append(replace2);
+ break;
+
+ case EditOp::Copy:
+ replace1.pos1 = op.pos2;
+ replace1.text = textAt(op.pos1, op.length1);
+ replaceList->append(replace1);
+ break;
+
+ case EditOp::Unset:
+ break;
+ }
+}
+
+bool ChangeSet::hadErrors()
+{
+ return m_error;
+}
+
+void ChangeSet::apply(QString *s)
+{
+ m_string = s;
+ apply_helper();
+ m_string = 0;
+}
+
+void ChangeSet::apply(QTextCursor *textCursor)
+{
+ m_cursor = textCursor;
+ apply_helper();
+ m_cursor = 0;
+}
+
+QString ChangeSet::textAt(int pos, int length)
+{
+ if (m_string) {
+ return m_string->mid(pos, length);
+ } else if (m_cursor) {
+ m_cursor->setPosition(pos);
+ m_cursor->setPosition(pos + length, QTextCursor::KeepAnchor);
+ return m_cursor->selectedText();
+ }
+ return QString();
+}
+
+void ChangeSet::apply_helper()
+{
+ // convert all ops to replace
+ QList<EditOp> replaceList;
+ {
+ while (!m_operationList.isEmpty()) {
+ const EditOp cmd(m_operationList.first());
+ m_operationList.removeFirst();
+ convertToReplace(cmd, &replaceList);
+ }
+ }
+
+ // execute replaces
+ if (m_cursor)
+ m_cursor->beginEditBlock();
+
+ while (!replaceList.isEmpty()) {
+ const EditOp cmd(replaceList.first());
+ replaceList.removeFirst();
+ doReplace(cmd, &replaceList);
+ }
+
+ if (m_cursor)
+ m_cursor->endEditBlock();
+}
+
+} // namespace Internal {
+
diff --git a/src/lib/corelib/api/changeset.h b/src/lib/corelib/api/changeset.h
new file mode 100644
index 000000000..b1674d392
--- /dev/null
+++ b/src/lib/corelib/api/changeset.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_CHANGESET_H
+#define QBS_CHANGESET_H
+
+#include <QString>
+#include <QList>
+
+QT_FORWARD_DECLARE_CLASS(QTextCursor)
+
+namespace QbsQmlJS {
+
+class ChangeSet
+{
+public:
+ struct EditOp {
+ enum Type
+ {
+ Unset,
+ Replace,
+ Move,
+ Insert,
+ Remove,
+ Flip,
+ Copy
+ };
+
+ EditOp(): type(Unset), pos1(0), pos2(0), length1(0), length2(0) {}
+ EditOp(Type t): type(t), pos1(0), pos2(0), length1(0), length2(0) {}
+
+ Type type;
+ int pos1;
+ int pos2;
+ int length1;
+ int length2;
+ QString text;
+ };
+
+ struct Range {
+ Range()
+ : start(0), end(0) {}
+
+ Range(int start, int end)
+ : start(start), end(end) {}
+
+ int start;
+ int end;
+ };
+
+public:
+ ChangeSet();
+ ChangeSet(const QList<EditOp> &operations);
+
+ bool isEmpty() const;
+
+ QList<EditOp> operationList() const;
+
+ void clear();
+
+ bool replace(const Range &range, const QString &replacement);
+ bool remove(const Range &range);
+ bool move(const Range &range, int to);
+ bool flip(const Range &range1, const Range &range2);
+ bool copy(const Range &range, int to);
+ bool replace(int start, int end, const QString &replacement);
+ bool remove(int start, int end);
+ bool move(int start, int end, int to);
+ bool flip(int start1, int end1, int start2, int end2);
+ bool copy(int start, int end, int to);
+ bool insert(int pos, const QString &text);
+
+ bool hadErrors();
+
+ void apply(QString *s);
+ void apply(QTextCursor *textCursor);
+
+private:
+ // length-based API.
+ bool replace_helper(int pos, int length, const QString &replacement);
+ bool move_helper(int pos, int length, int to);
+ bool remove_helper(int pos, int length);
+ bool flip_helper(int pos1, int length1, int pos2, int length2);
+ bool copy_helper(int pos, int length, int to);
+
+ bool hasOverlap(int pos, int length);
+ QString textAt(int pos, int length);
+
+ void doReplace(const EditOp &replace, QList<EditOp> *replaceList);
+ void convertToReplace(const EditOp &op, QList<EditOp> *replaceList);
+
+ void apply_helper();
+
+private:
+ QString *m_string;
+ QTextCursor *m_cursor;
+
+ QList<EditOp> m_operationList;
+ bool m_error;
+};
+
+} // namespace QbsQmlJS
+
+#endif // Include guard.
diff --git a/src/lib/corelib/api/internaljobs.cpp b/src/lib/corelib/api/internaljobs.cpp
new file mode 100644
index 000000000..a28bee72e
--- /dev/null
+++ b/src/lib/corelib/api/internaljobs.cpp
@@ -0,0 +1,429 @@
+/****************************************************************************
+**
+** 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 "internaljobs.h"
+
+#include "jobs.h"
+
+#include <buildgraph/artifactcleaner.h>
+#include <buildgraph/buildgraphloader.h>
+#include <buildgraph/productbuilddata.h>
+#include <buildgraph/projectbuilddata.h>
+#include <buildgraph/executor.h>
+#include <buildgraph/productinstaller.h>
+#include <buildgraph/rulesevaluationcontext.h>
+#include <language/language.h>
+#include <language/loader.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/progressobserver.h>
+#include <tools/preferences.h>
+#include <tools/qbsassert.h>
+
+#include <QEventLoop>
+#include <QScopedPointer>
+#include <QTimer>
+
+namespace qbs {
+namespace Internal {
+
+static void unlockBuildGraph(const TopLevelProjectPtr &project)
+{
+ QBS_ASSERT(project->locked, return);
+ project->locked = false;
+}
+
+class JobObserver : public ProgressObserver
+{
+public:
+ JobObserver(InternalJob *job) : m_canceled(false), m_job(job), m_timedLogger(0) { }
+ ~JobObserver() { delete m_timedLogger; }
+
+ void cancel() { m_canceled = true; }
+
+private:
+ void initialize(const QString &task, int maximum)
+ {
+ QBS_ASSERT(!m_timedLogger, delete m_timedLogger);
+ m_timedLogger = new TimedActivityLogger(m_job->logger(), task, QString(),
+ m_job->timed() ? LoggerInfo : LoggerDebug, m_job->timed());
+ m_value = 0;
+ m_maximum = maximum;
+ m_canceled = false;
+ emit m_job->newTaskStarted(task, maximum, m_job);
+ }
+
+ void setMaximum(int maximum)
+ {
+ m_maximum = maximum;
+ emit m_job->totalEffortChanged(maximum, m_job);
+ }
+
+ void setProgressValue(int value)
+ {
+ //QBS_ASSERT(value >= m_value, qDebug("old value = %d, new value = %d", m_value, value));
+ //QBS_ASSERT(value <= m_maximum, qDebug("value = %d, maximum = %d", value, m_maximum));
+ m_value = value;
+ if (value == m_maximum) {
+ delete m_timedLogger;
+ m_timedLogger = 0;
+ }
+ emit m_job->taskProgress(value, m_job);
+ }
+
+ int progressValue() { return m_value; }
+ int maximum() const { return m_maximum; }
+ bool canceled() const { return m_canceled; }
+
+ int m_value;
+ int m_maximum;
+ bool m_canceled;
+ InternalJob * const m_job;
+ TimedActivityLogger *m_timedLogger;
+};
+
+
+InternalJob::InternalJob(const Logger &logger, QObject *parent)
+ : QObject(parent)
+ , m_observer(new JobObserver(this))
+ , m_ownsObserver(true)
+ , m_logger(logger)
+ , m_timed(false)
+{
+}
+
+InternalJob::~InternalJob()
+{
+ if (m_ownsObserver)
+ delete m_observer;
+}
+
+void InternalJob::cancel()
+{
+ m_observer->cancel();
+}
+
+void InternalJob::shareObserverWith(InternalJob *otherJob)
+{
+ if (m_ownsObserver) {
+ delete m_observer;
+ m_ownsObserver = false;
+ }
+ m_observer = otherJob->m_observer;
+}
+
+void InternalJob::storeBuildGraph(const TopLevelProjectConstPtr &project)
+{
+ try {
+ project->store(logger());
+ } catch (const ErrorInfo &error) {
+ logger().printWarning(error);
+ }
+}
+
+
+/**
+ * Construct a new thread wrapper for a synchronous job.
+ * This object takes over ownership of the synchronous job.
+ */
+InternalJobThreadWrapper::InternalJobThreadWrapper(InternalJob *synchronousJob, QObject *parent)
+ : InternalJob(synchronousJob->logger(), parent)
+ , m_job(synchronousJob)
+ , m_running(false)
+{
+ synchronousJob->shareObserverWith(this);
+ m_job->moveToThread(&m_thread);
+ connect(m_job, SIGNAL(finished(Internal::InternalJob*)), SLOT(handleFinished()));
+ connect(m_job, SIGNAL(newTaskStarted(QString,int,Internal::InternalJob*)),
+ SIGNAL(newTaskStarted(QString,int,Internal::InternalJob*)));
+ connect(m_job, SIGNAL(taskProgress(int,Internal::InternalJob*)),
+ SIGNAL(taskProgress(int,Internal::InternalJob*)));
+ connect(m_job, SIGNAL(totalEffortChanged(int,Internal::InternalJob*)),
+ SIGNAL(totalEffortChanged(int,Internal::InternalJob*)));
+ m_job->connect(this, SIGNAL(startRequested()), SLOT(start()));
+}
+
+InternalJobThreadWrapper::~InternalJobThreadWrapper()
+{
+ if (m_running) {
+ QEventLoop loop;
+ loop.connect(m_job, SIGNAL(finished(Internal::InternalJob*)), SLOT(quit()));
+ cancel();
+ loop.exec();
+ }
+ m_thread.quit();
+ m_thread.wait();
+ delete m_job;
+}
+
+void InternalJobThreadWrapper::start()
+{
+ setTimed(m_job->timed());
+ m_thread.start();
+ m_running = true;
+ emit startRequested();
+}
+
+void InternalJobThreadWrapper::handleFinished()
+{
+ m_running = false;
+ setError(m_job->error());
+ emit finished(this);
+}
+
+
+InternalSetupProjectJob::InternalSetupProjectJob(const Logger &logger)
+ : InternalJob(logger)
+{
+}
+
+InternalSetupProjectJob::~InternalSetupProjectJob()
+{
+}
+
+void InternalSetupProjectJob::init(const SetupProjectParameters &parameters)
+{
+ m_parameters = parameters;
+ setTimed(parameters.logElapsedTime());
+}
+
+void InternalSetupProjectJob::reportError(const ErrorInfo &error)
+{
+ setError(error);
+ emit finished(this);
+}
+
+TopLevelProjectPtr InternalSetupProjectJob::project() const
+{
+ return m_project;
+}
+
+void InternalSetupProjectJob::start()
+{
+ try {
+ execute();
+ } catch (const ErrorInfo &error) {
+ m_project.clear();
+ setError(error);
+ }
+ emit finished(this);
+}
+
+void InternalSetupProjectJob::execute()
+{
+ RulesEvaluationContextPtr evalContext(new RulesEvaluationContext(logger()));
+ evalContext->setObserver(observer());
+
+ switch (m_parameters.restoreBehavior()) {
+ case SetupProjectParameters::ResolveOnly:
+ resolveProjectFromScratch(evalContext->engine());
+ resolveBuildDataFromScratch(evalContext);
+ setupPlatformEnvironment();
+ break;
+ case SetupProjectParameters::RestoreOnly:
+ m_project = restoreProject(evalContext).loadedProject;
+ break;
+ case SetupProjectParameters::RestoreAndTrackChanges: {
+ const BuildGraphLoadResult loadResult = restoreProject(evalContext);
+ m_project = loadResult.newlyResolvedProject;
+ if (!m_project)
+ m_project = loadResult.loadedProject;
+ if (!m_project) {
+ resolveProjectFromScratch(evalContext->engine());
+ resolveBuildDataFromScratch(evalContext);
+ } else {
+ QBS_CHECK(m_project->buildData);
+ }
+ setupPlatformEnvironment();
+ break;
+ }
+ }
+
+ if (!m_parameters.dryRun())
+ storeBuildGraph(m_project);
+
+ // The evalutation context cannot be re-used for building, which runs in a different thread.
+ m_project->buildData->evaluationContext.clear();
+}
+
+void InternalSetupProjectJob::resolveProjectFromScratch(ScriptEngine *engine)
+{
+ Loader loader(engine, logger());
+ loader.setSearchPaths(m_parameters.searchPaths());
+ loader.setProgressObserver(observer());
+ m_project = loader.loadProject(m_parameters);
+ QBS_CHECK(m_project);
+}
+
+void InternalSetupProjectJob::resolveBuildDataFromScratch(const RulesEvaluationContextPtr &evalContext)
+{
+ TimedActivityLogger resolveLogger(logger(), QLatin1String("Resolving build project"));
+ BuildDataResolver(logger()).resolveBuildData(m_project, evalContext);
+}
+
+void InternalSetupProjectJob::setupPlatformEnvironment()
+{
+ const QVariantMap platformEnvironment
+ = m_parameters.buildConfiguration().value(QLatin1String("environment")).toMap();
+ m_project->platformEnvironment = platformEnvironment;
+}
+
+BuildGraphLoadResult InternalSetupProjectJob::restoreProject(const RulesEvaluationContextPtr &evalContext)
+{
+ BuildGraphLoader bgLoader(m_parameters.environment(), logger());
+ const BuildGraphLoadResult loadResult = bgLoader.load(m_parameters, evalContext);
+ return loadResult;
+}
+
+BuildGraphTouchingJob::BuildGraphTouchingJob(const Logger &logger, QObject *parent)
+ : InternalJob(logger, parent), m_dryRun(false)
+{
+}
+
+BuildGraphTouchingJob::~BuildGraphTouchingJob()
+{
+}
+
+void BuildGraphTouchingJob::setup(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, bool dryRun)
+{
+ m_project = project;
+ m_products = products;
+ m_dryRun = dryRun;
+}
+
+void BuildGraphTouchingJob::storeBuildGraph()
+{
+ if (!m_dryRun && !error().isInternalError())
+ InternalJob::storeBuildGraph(m_project);
+}
+
+InternalBuildJob::InternalBuildJob(const Logger &logger, QObject *parent)
+ : BuildGraphTouchingJob(logger, parent), m_executor(0)
+{
+}
+
+void InternalBuildJob::build(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const BuildOptions &buildOptions)
+{
+ setup(project, products, buildOptions.dryRun());
+ setTimed(buildOptions.logElapsedTime());
+
+ m_executor = new Executor(logger());
+ m_executor->setProject(project);
+ m_executor->setProducts(products);
+ m_executor->setBuildOptions(buildOptions);
+ m_executor->setProgressObserver(observer());
+
+ QThread * const executorThread = new QThread(this);
+ m_executor->moveToThread(executorThread);
+ connect(m_executor, SIGNAL(reportCommandDescription(QString,QString)),
+ this, SIGNAL(reportCommandDescription(QString,QString)));
+ connect(m_executor, SIGNAL(reportProcessResult(qbs::ProcessResult)),
+ this, SIGNAL(reportProcessResult(qbs::ProcessResult)));
+
+ connect(executorThread, SIGNAL(started()), m_executor, SLOT(build()));
+ connect(m_executor, SIGNAL(finished()), SLOT(handleFinished()));
+ connect(m_executor, SIGNAL(destroyed()), executorThread, SLOT(quit()));
+ connect(executorThread, SIGNAL(finished()), this, SLOT(emitFinished()));
+ executorThread->start();
+}
+
+void InternalBuildJob::handleFinished()
+{
+ setError(m_executor->error());
+ project()->buildData->evaluationContext.clear();
+ storeBuildGraph();
+ m_executor->deleteLater();
+}
+
+void InternalBuildJob::emitFinished()
+{
+ unlockBuildGraph(project());
+ emit finished(this);
+}
+
+InternalCleanJob::InternalCleanJob(const Logger &logger, QObject *parent)
+ : BuildGraphTouchingJob(logger, parent)
+{
+}
+
+void InternalCleanJob::init(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const CleanOptions &options)
+{
+ setup(project, products, options.dryRun());
+ setTimed(options.logElapsedTime());
+ m_options = options;
+}
+
+void InternalCleanJob::start()
+{
+ try {
+ ArtifactCleaner cleaner(logger(), observer());
+ cleaner.cleanup(project(), products(), m_options);
+ } catch (const ErrorInfo &error) {
+ setError(error);
+ }
+ storeBuildGraph();
+ unlockBuildGraph(project());
+ emit finished(this);
+}
+
+
+InternalInstallJob::InternalInstallJob(const Logger &logger)
+ : InternalJob(logger)
+{
+}
+
+InternalInstallJob::~InternalInstallJob()
+{
+}
+
+void InternalInstallJob::init(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const InstallOptions &options)
+{
+ m_project = project;
+ m_products = products;
+ m_options = options;
+ setTimed(options.logElapsedTime());
+}
+
+void InternalInstallJob::start()
+{
+ try {
+ ProductInstaller(m_project, m_products, m_options, observer(), logger()).install();
+ } catch (const ErrorInfo &error) {
+ setError(error);
+ }
+ unlockBuildGraph(m_project);
+ emit finished(this);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/api/internaljobs.h b/src/lib/corelib/api/internaljobs.h
new file mode 100644
index 000000000..ef112662f
--- /dev/null
+++ b/src/lib/corelib/api/internaljobs.h
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_INTERNALJOBS_H
+#define QBS_INTERNALJOBS_H
+
+#include <buildgraph/forward_decls.h>
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+#include <tools/buildoptions.h>
+#include <tools/cleanoptions.h>
+#include <tools/installoptions.h>
+#include <tools/error.h>
+#include <tools/setupprojectparameters.h>
+
+#include <QList>
+#include <QObject>
+#include <QThread>
+
+namespace qbs {
+class ProcessResult;
+class Settings;
+
+namespace Internal {
+class BuildGraphLoadResult;
+class Executor;
+class JobObserver;
+class ScriptEngine;
+
+class InternalJob : public QObject
+{
+ Q_OBJECT
+ friend class JobObserver;
+public:
+ ~InternalJob();
+
+ void cancel();
+ ErrorInfo error() const { return m_error; }
+ void setError(const ErrorInfo &error) { m_error = error; }
+
+ Logger logger() const { return m_logger; }
+ bool timed() const { return m_timed; }
+ void shareObserverWith(InternalJob *otherJob);
+
+protected:
+ explicit InternalJob(const Logger &logger, QObject *parent = 0);
+
+ JobObserver *observer() const { return m_observer; }
+ void setTimed(bool timed) { m_timed = timed; }
+ void storeBuildGraph(const TopLevelProjectConstPtr &project);
+
+signals:
+ void finished(Internal::InternalJob *job);
+ void newTaskStarted(const QString &description, int totalEffort, Internal::InternalJob *job);
+ void totalEffortChanged(int totalEffort, Internal::InternalJob *job);
+ void taskProgress(int value, Internal::InternalJob *job);
+
+private:
+ ErrorInfo m_error;
+ JobObserver *m_observer;
+ bool m_ownsObserver;
+ Logger m_logger;
+ bool m_timed;
+};
+
+
+class InternalJobThreadWrapper : public InternalJob
+{
+ Q_OBJECT
+public:
+ InternalJobThreadWrapper(InternalJob *synchronousJob, QObject *parent = 0);
+ ~InternalJobThreadWrapper();
+
+ void start();
+ InternalJob *synchronousJob() const { return m_job; }
+
+signals:
+ void startRequested();
+
+private slots:
+ void handleFinished();
+
+private:
+ QThread m_thread;
+ InternalJob *m_job;
+ bool m_running;
+};
+
+class InternalSetupProjectJob : public InternalJob
+{
+ Q_OBJECT
+public:
+ InternalSetupProjectJob(const Logger &logger);
+ ~InternalSetupProjectJob();
+
+ void init(const SetupProjectParameters &parameters);
+ void reportError(const ErrorInfo &error);
+
+ TopLevelProjectPtr project() const;
+
+private slots:
+ void start();
+
+private:
+ void resolveProjectFromScratch(Internal::ScriptEngine *engine);
+ void resolveBuildDataFromScratch(const RulesEvaluationContextPtr &evalContext);
+ void setupPlatformEnvironment();
+ BuildGraphLoadResult restoreProject(const RulesEvaluationContextPtr &evalContext);
+ void execute();
+
+ TopLevelProjectPtr m_project;
+ SetupProjectParameters m_parameters;
+};
+
+
+class BuildGraphTouchingJob : public InternalJob
+{
+ Q_OBJECT
+public:
+ const QList<ResolvedProductPtr> &products() const { return m_products; }
+ const TopLevelProjectPtr &project() const { return m_project; }
+
+signals:
+ void reportCommandDescription(const QString &highlight, const QString &message);
+ void reportProcessResult(const qbs::ProcessResult &result);
+
+protected:
+ BuildGraphTouchingJob(const Logger &logger, QObject *parent = 0);
+ ~BuildGraphTouchingJob();
+
+ void setup(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ bool dryRun);
+ void storeBuildGraph();
+
+private:
+ TopLevelProjectPtr m_project;
+ QList<ResolvedProductPtr> m_products;
+ bool m_dryRun;
+};
+
+
+class InternalBuildJob : public BuildGraphTouchingJob
+{
+ Q_OBJECT
+public:
+ InternalBuildJob(const Logger &logger, QObject *parent = 0);
+
+ void build(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ const BuildOptions &buildOptions);
+
+private slots:
+ void handleFinished();
+ void emitFinished();
+
+private:
+ Executor *m_executor;
+};
+
+
+class InternalCleanJob : public BuildGraphTouchingJob
+{
+ Q_OBJECT
+public:
+ InternalCleanJob(const Logger &logger, QObject *parent = 0);
+
+ void init(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ const CleanOptions &options);
+
+private slots:
+ void start();
+
+private:
+ CleanOptions m_options;
+};
+
+
+class InternalInstallJob : public InternalJob
+{
+ Q_OBJECT
+public:
+ InternalInstallJob(const Logger &logger);
+ ~InternalInstallJob();
+
+ void init(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ const InstallOptions &options);
+
+private slots:
+ void start();
+
+private:
+ TopLevelProjectPtr m_project;
+ QList<ResolvedProductPtr> m_products;
+ InstallOptions m_options;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_INTERNALJOBS_H
diff --git a/src/lib/corelib/api/jobs.cpp b/src/lib/corelib/api/jobs.cpp
new file mode 100644
index 000000000..d7ac324cb
--- /dev/null
+++ b/src/lib/corelib/api/jobs.cpp
@@ -0,0 +1,320 @@
+/****************************************************************************
+**
+** 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 "jobs.h"
+
+#include "internaljobs.h"
+#include "project.h"
+#include <language/language.h>
+#include <tools/qbsassert.h>
+
+#include <QMetaObject>
+
+namespace qbs {
+using namespace Internal;
+
+/*!
+ * \class AbstractJob
+ * \brief The \c AbstractJob class represents an operation relating to a \c Project.
+ * Concrete child classes of \c AbstractJob are created by factory functions in the \c Project
+ * class. The respective objects represent an operation that is started automatically
+ * and is considered "running" until the \c finished() signal has been emitted. Afterwards,
+ * callers can find out whether the operation was successful by calling \c hasError(). While
+ * the operation is going on, progress information is being provided via \c taskStarted() and
+ * \c taskProgress.
+ * Note that though a job is being started automatically by its factory function, you are guaranteed
+ * to recevieve all signals it emits if you connect to it right after getting the object from the
+ * creating function.
+ * \sa Project
+ */
+
+/*!
+ * \enum AbstractJob::State
+ * This enum type specifies which states a job can be in.
+ * \value StateRunning The respective operation is ongoing.
+ * \value StateCanceling The job has been requested to cancel via \c AbstractJob::cancel(),
+ * but the \c AbstractJob::finished() signal has not been emitted yet.
+ * \value StateFinished The operation has finished and the \c AbstractJob::finished() signal
+ * has been emitted.
+ */
+
+ /*!
+ * \fn AbstractJob::State AbstractJob::state() const
+ * \brief Returns the current state of the operation.
+ */
+
+ /*!
+ * \fn bool AbstractJob::hasError() const
+ * \brief Returns true if the operation has finished with an error, otherwise returns false.
+ * This function should not be called before the \c finished() signal has been emitted.
+ */
+
+/*!
+ * \fn void AbstractJob::taskStarted(const QString &description, int maximumProgressValue, qbs::AbstractJob *job)
+ * \brief Indicates that a new task has been started.
+ * The \a description parameter is a string intended for presentation to a user.
+ * The \a maximumProgressValue parameter indicates the maximum value to which subsequent values of
+ * \c taskProgress() will go.
+ * This signal is typically emitted exactly once for a job that finishes successfully. However,
+ * operations might emit it several times if they are made up of subtasks whose overall effort
+ * cannot be determined in advance.
+ * \sa AbstractJob::taskProgress()
+ */
+
+/*!
+ * \fn void taskProgress(int newProgressValue, qbs::AbstractJob *job)
+ * \brief Indicates progress in executing the operation.
+ * The \a newProgressValue parameter represents the current progress. It is always greater than
+ * zero, strictly increasing and goes up to the \c maximumProgressValue argument of the last
+ * call to \c taskStarted().
+ * \sa AbstractJob::taskStarted()
+ */
+
+ /*!
+ * \fn void finished(bool success, qbs::AbstractJob *job)
+ * \brief Indicates that the operation has finished.
+ * Check the \a success parameter to find out whether everything went fine or an error occurred.
+ */
+
+AbstractJob::AbstractJob(InternalJob *internalJob, QObject *parent)
+ : QObject(parent), m_internalJob(internalJob)
+{
+ m_internalJob->setParent(this);
+ connect(m_internalJob, SIGNAL(newTaskStarted(QString,int,Internal::InternalJob*)),
+ SLOT(handleTaskStarted(QString,int)), Qt::QueuedConnection);
+ connect(m_internalJob, SIGNAL(totalEffortChanged(int,Internal::InternalJob*)),
+ SLOT(handleTotalEffortChanged(int)));
+ connect(m_internalJob, SIGNAL(taskProgress(int,Internal::InternalJob*)),
+ SLOT(handleTaskProgress(int)), Qt::QueuedConnection);
+ connect(m_internalJob, SIGNAL(finished(Internal::InternalJob *)), SLOT(handleFinished()));
+ m_state = StateRunning;
+}
+
+bool AbstractJob::lockBuildGraph(const TopLevelProjectPtr &project)
+{
+ // The API is not thread-safe, so we don't need a mutex here, as the API requests come in
+ // synchronously.
+ if (project->locked) {
+ internalJob()->setError(tr("Cannot start a job while another one is in process."));
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection, Q_ARG(bool, false),
+ Q_ARG(qbs::AbstractJob *, this));
+ return false;
+ }
+ project->locked = true;
+ return true;
+}
+
+/*!
+ * \brief Destroys the object, canceling the operation if necessary.
+ */
+AbstractJob::~AbstractJob()
+{
+ m_internalJob->disconnect(this);
+ cancel();
+}
+
+/*!
+ * \brief Returns the error which caused this operation to fail, if it did fail.
+ */
+ErrorInfo AbstractJob::error() const
+{
+ return internalJob()->error();
+}
+
+/*!
+ * \brief Cancels this job.
+ * Note that the job might not finish immediately. If you need to make sure it has actually
+ * finished, wait for the \c finished() signal.
+ * \sa AbstractJob::finished(AbstractJob *);
+ */
+void AbstractJob::cancel()
+{
+ if (m_state != StateRunning)
+ return;
+ m_state = StateCanceling;
+ internalJob()->cancel();
+}
+
+void AbstractJob::handleTaskStarted(const QString &description, int maximumProgressValue)
+{
+ emit taskStarted(description, maximumProgressValue, this);
+}
+
+void AbstractJob::handleTotalEffortChanged(int totalEffort)
+{
+ emit totalEffortChanged(totalEffort, this);
+}
+
+void AbstractJob::handleTaskProgress(int newProgressValue)
+{
+ emit taskProgress(newProgressValue, this);
+}
+
+void AbstractJob::handleFinished()
+{
+ QBS_ASSERT(m_state != StateFinished, return);
+ m_state = StateFinished;
+ emit finished(!error().hasError(), this);
+}
+
+
+/*!
+ * \class SetupProjectJob
+ * \brief The \c SetupProjectJob class represents an operation that reads a qbs project file and
+ * creates a \c Project object from it.
+ * Note that this job can emit the \c taskStarted() signal more than once.
+ * \sa AbstractJob::taskStarted()
+ */
+
+SetupProjectJob::SetupProjectJob(const Logger &logger, QObject *parent)
+ : AbstractJob(new InternalJobThreadWrapper(new InternalSetupProjectJob(logger)), parent)
+{
+}
+
+/*!
+ * \brief Returns the project resulting from this operation.
+ * Note that the result is undefined if the job did not finish successfully.
+ * \sa AbstractJob::hasError()
+ */
+Project SetupProjectJob::project() const
+{
+ const InternalJobThreadWrapper * const wrapper
+ = qobject_cast<InternalJobThreadWrapper *>(internalJob());
+ const InternalSetupProjectJob * const job
+ = qobject_cast<InternalSetupProjectJob *>(wrapper->synchronousJob());
+ return Project(job->project(), job->logger());
+}
+
+void SetupProjectJob::resolve(const SetupProjectParameters &parameters)
+{
+ InternalJobThreadWrapper * const wrapper
+ = qobject_cast<InternalJobThreadWrapper *>(internalJob());
+ InternalSetupProjectJob * const job
+ = qobject_cast<InternalSetupProjectJob *>(wrapper->synchronousJob());
+ job->init(parameters);
+ wrapper->start();
+}
+
+void SetupProjectJob::reportError(const ErrorInfo &error)
+{
+ InternalJobThreadWrapper * const wrapper
+ = qobject_cast<InternalJobThreadWrapper *>(internalJob());
+ InternalSetupProjectJob * const job
+ = qobject_cast<InternalSetupProjectJob *>(wrapper->synchronousJob());
+ job->reportError(error);
+}
+
+/*!
+ * \class ProcessResult
+ * \brief The \c ProcessResult class represents the result of one external program run by Qbs.
+ *
+ * The \c ProcessResult class represents all the information on one external program that was
+ * run by Qbs. It includes the command line used to start the program, the working directory
+ * as well as output and exit codes.
+ */
+
+/*!
+ * \class BuildJob
+ * \brief The \c BuildJob class represents a build operation.
+ */
+
+/*!
+ * \fn void BuildJob::reportCommandDescription(const QString &highlight, const QString &message)
+ * \brief Signals that a new command is being worked on.
+ * The \a highlight parameter is used to decide on the colors and font styles to be used to
+ * print the message.
+ * The \a message parameter is the localized message to print.
+ */
+
+/*!
+ * \fn void BuildJob::reportProcessResult(const qbs::ProcessResult &result)
+ * \brief Signals that an external command has finished.
+ * The \a result parameter contains all details on the process that was run by Qbs.
+ */
+
+BuildJob::BuildJob(const Logger &logger, QObject *parent)
+ : AbstractJob(new InternalBuildJob(logger), parent)
+{
+ InternalBuildJob *job = static_cast<InternalBuildJob *>(internalJob());
+ connect(job, SIGNAL(reportCommandDescription(QString,QString)),
+ this, SIGNAL(reportCommandDescription(QString,QString)));
+ connect(job, SIGNAL(reportProcessResult(qbs::ProcessResult)),
+ this, SIGNAL(reportProcessResult(qbs::ProcessResult)));
+}
+
+void BuildJob::build(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ const BuildOptions &options)
+{
+ if (!lockBuildGraph(project))
+ return;
+ qobject_cast<InternalBuildJob *>(internalJob())->build(project, products, options);
+}
+
+
+/*!
+ * \class CleanJob
+ * \brief The \c CleanJob class represents an operation removing build artifacts.
+ */
+
+CleanJob::CleanJob(const Logger &logger, QObject *parent)
+ : AbstractJob(new InternalJobThreadWrapper(new InternalCleanJob(logger)), parent)
+{
+}
+
+void CleanJob::clean(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ const qbs::CleanOptions &options)
+{
+ if (!lockBuildGraph(project))
+ return;
+ InternalJobThreadWrapper * wrapper = qobject_cast<InternalJobThreadWrapper *>(internalJob());
+ qobject_cast<InternalCleanJob *>(wrapper->synchronousJob())->init(project, products, options);
+ wrapper->start();
+}
+
+/*!
+ * \class InstallJob
+ * \brief The \c InstallJob class represents an operation installing files.
+ */
+
+InstallJob::InstallJob(const Logger &logger, QObject *parent)
+ : AbstractJob(new InternalJobThreadWrapper(new InternalInstallJob(logger)), parent)
+{
+}
+
+void InstallJob::install(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const InstallOptions &options)
+{
+ if (!lockBuildGraph(project))
+ return;
+ InternalJobThreadWrapper *wrapper = qobject_cast<InternalJobThreadWrapper *>(internalJob());
+ InternalInstallJob *installJob = qobject_cast<InternalInstallJob *>(wrapper->synchronousJob());
+ installJob->init(project, products, options);
+ wrapper->start();
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/api/jobs.h b/src/lib/corelib/api/jobs.h
new file mode 100644
index 000000000..42c743876
--- /dev/null
+++ b/src/lib/corelib/api/jobs.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_JOBS_H
+#define QBS_JOBS_H
+
+#include "../language/forward_decls.h"
+#include "../tools/error.h"
+#include "../tools/qbs_export.h"
+
+#include <QObject>
+#include <QProcess>
+#include <QVariantMap>
+
+namespace qbs {
+class BuildOptions;
+class CleanOptions;
+class InstallOptions;
+class ProcessResult;
+class SetupProjectParameters;
+namespace Internal {
+class InternalJob;
+class Logger;
+class ProjectPrivate;
+} // namespace Internal
+
+class Project;
+
+class QBS_EXPORT AbstractJob : public QObject
+{
+ Q_OBJECT
+public:
+ ~AbstractJob();
+
+ enum State { StateRunning, StateCanceling, StateFinished };
+ State state() const { return m_state; }
+
+ ErrorInfo error() const;
+
+ void cancel();
+
+protected:
+ AbstractJob(Internal::InternalJob *internalJob, QObject *parent);
+ Internal::InternalJob *internalJob() const { return m_internalJob; }
+
+ bool lockBuildGraph(const Internal::TopLevelProjectPtr &project);
+
+signals:
+ void taskStarted(const QString &description, int maximumProgressValue, qbs::AbstractJob *job);
+ void totalEffortChanged(int totalEffort, qbs::AbstractJob *job);
+ void taskProgress(int newProgressValue, qbs::AbstractJob *job);
+ void finished(bool success, qbs::AbstractJob *job);
+
+private slots:
+ void handleTaskStarted(const QString &description, int maximumProgressValue);
+ void handleTotalEffortChanged(int totalEffort);
+ void handleTaskProgress(int newProgressValue);
+ void handleFinished();
+
+private:
+ Internal::InternalJob * const m_internalJob;
+ State m_state;
+};
+
+
+class QBS_EXPORT SetupProjectJob : public AbstractJob
+{
+ Q_OBJECT
+ friend class Project;
+public:
+ Project project() const;
+
+private:
+ SetupProjectJob(const Internal::Logger &logger, QObject *parent);
+
+ void resolve(const SetupProjectParameters &parameters);
+ void reportError(const ErrorInfo &error);
+};
+
+class QBS_EXPORT BuildJob : public AbstractJob
+{
+ Q_OBJECT
+ friend class Internal::ProjectPrivate;
+
+signals:
+ void reportCommandDescription(const QString &highlight, const QString &message);
+ void reportProcessResult(const qbs::ProcessResult &result);
+
+private:
+ BuildJob(const Internal::Logger &logger, QObject *parent);
+
+ void build(const Internal::TopLevelProjectPtr &project,
+ const QList<qbs::Internal::ResolvedProductPtr> &products,
+ const BuildOptions &options);
+};
+
+
+class QBS_EXPORT CleanJob : public AbstractJob
+{
+ Q_OBJECT
+ friend class Internal::ProjectPrivate;
+
+private:
+ CleanJob(const Internal::Logger &logger, QObject *parent);
+
+ void clean(const Internal::TopLevelProjectPtr &project,
+ const QList<Internal::ResolvedProductPtr> &products, const CleanOptions &options);
+};
+
+class QBS_EXPORT InstallJob : public AbstractJob
+{
+ Q_OBJECT
+ friend class Internal::ProjectPrivate;
+private:
+ InstallJob(const Internal::Logger &logger, QObject *parent);
+
+ void install(const Internal::TopLevelProjectPtr &project,
+ const QList<Internal::ResolvedProductPtr> &products, const InstallOptions &options);
+};
+
+} // namespace qbs
+
+#endif // QBS_JOBS_H
diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp
new file mode 100644
index 000000000..6826884e6
--- /dev/null
+++ b/src/lib/corelib/api/project.cpp
@@ -0,0 +1,1027 @@
+/****************************************************************************
+**
+** 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 "project.h"
+
+#include "internaljobs.h"
+#include "jobs.h"
+#include "projectdata.h"
+#include "projectdata_p.h"
+#include "projectfileupdater.h"
+#include "propertymap_p.h"
+#include "runenvironment.h"
+#include <buildgraph/artifact.h>
+#include <buildgraph/buildgraph.h>
+#include <buildgraph/productbuilddata.h>
+#include <buildgraph/productinstaller.h>
+#include <buildgraph/projectbuilddata.h>
+#include <buildgraph/rulesevaluationcontext.h>
+#include <buildgraph/timestampsupdater.h>
+#include <buildgraph/transformer.h>
+#include <language/language.h>
+#include <language/projectresolver.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/cleanoptions.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/installoptions.h>
+#include <tools/preferences.h>
+#include <tools/processresult.h>
+#include <tools/propertyfinder.h>
+#include <tools/scannerpluginmanager.h>
+#include <tools/scripttools.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/qbsassert.h>
+
+#include <QDir>
+#include <QMutex>
+#include <QMutexLocker>
+#include <QSharedData>
+
+namespace qbs {
+namespace Internal {
+
+static bool pluginsLoaded = false;
+static QMutex pluginsLoadedMutex;
+
+static void loadPlugins(const QStringList &_pluginPaths, const Logger &logger)
+{
+ QMutexLocker locker(&pluginsLoadedMutex);
+ if (pluginsLoaded)
+ return;
+
+ QStringList pluginPaths;
+ foreach (const QString &pluginPath, _pluginPaths) {
+ if (!FileInfo::exists(pluginPath)) {
+ logger.qbsWarning() << Tr::tr("Plugin path '%1' does not exist.")
+ .arg(QDir::toNativeSeparators(pluginPath));
+ } else {
+ pluginPaths << pluginPath;
+ }
+ }
+ ScannerPluginManager::instance()->loadPlugins(pluginPaths, logger);
+
+ qRegisterMetaType<ErrorInfo>("qbs::ErrorInfo");
+ qRegisterMetaType<ProcessResult>("qbs::ProcessResult");
+ qRegisterMetaType<InternalJob *>("Internal::InternalJob *");
+ pluginsLoaded = true;
+}
+
+
+class ProjectPrivate : public QSharedData
+{
+public:
+ ProjectPrivate(const TopLevelProjectPtr &internalProject, const Logger &logger)
+ : internalProject(internalProject), logger(logger), m_projectDataRetrieved(false)
+ {
+ }
+
+ ProjectData projectData();
+ BuildJob *buildProducts(const QList<ResolvedProductPtr> &products, const BuildOptions &options,
+ bool needsDepencencyResolving,
+ QObject *jobOwner);
+ CleanJob *cleanProducts(const QList<ResolvedProductPtr> &products, const CleanOptions &options,
+ QObject *jobOwner);
+ InstallJob *installProducts(const QList<ResolvedProductPtr> &products,
+ const InstallOptions &options, bool needsDepencencyResolving,
+ QObject *jobOwner);
+ QList<ResolvedProductPtr> internalProducts(const QList<ProductData> &products) const;
+ QList<ResolvedProductPtr> allEnabledInternalProducts() const;
+ ResolvedProductPtr internalProduct(const ProductData &product) const;
+ ProductData findProductData(const QString &productName) const;
+ GroupData findGroupData(const ProductData &product, const QString &groupName) const;
+
+ GroupData createGroupDataFromGroup(const GroupPtr &resolvedGroup);
+
+ struct GroupUpdateContext {
+ ResolvedProductPtr resolvedProduct;
+ GroupPtr resolvedGroup;
+ ProductData currentProduct;
+ GroupData currentGroup;
+ };
+
+ struct FileListUpdateContext {
+ GroupUpdateContext groupContext;
+ QStringList absoluteFilePaths;
+ QStringList relativeFilePaths;
+ };
+
+ GroupUpdateContext getGroupContext(const ProductData &product, const GroupData &group);
+ FileListUpdateContext getFileListContext(const ProductData &product, const GroupData &group,
+ const QStringList &filePaths);
+
+ void addGroup(const ProductData &product, const QString &groupName);
+ void addFiles(const ProductData &product, const GroupData &group, const QStringList &filePaths);
+ void removeFiles(const ProductData &product, const GroupData &group,
+ const QStringList &filePaths);
+ void removeGroup(const ProductData &product, const GroupData &group);
+ void removeFilesFromBuildGraph(const ResolvedProductConstPtr &product,
+ const QList<SourceArtifactPtr> &files);
+ void updateInternalCodeLocations(const ResolvedProjectPtr &project,
+ const CodeLocation &changeLocation, int lineOffset);
+ void updateExternalCodeLocations(const ProjectData &project,
+ const CodeLocation &changeLocation, int lineOffset);
+ void prepareChangeToProject();
+
+ const TopLevelProjectPtr internalProject;
+ Logger logger;
+
+private:
+ void retrieveProjectData(ProjectData &projectData,
+ const ResolvedProjectConstPtr &internalProject);
+
+ ProjectData m_projectData;
+ bool m_projectDataRetrieved;
+};
+
+ProjectData ProjectPrivate::projectData()
+{
+ if (!m_projectDataRetrieved) {
+ retrieveProjectData(m_projectData, internalProject);
+ m_projectData.d->buildDir = internalProject->buildDirectory;
+ m_projectDataRetrieved = true;
+ }
+ return m_projectData;
+}
+
+static void addDependencies(QList<ResolvedProductPtr> &products)
+{
+ for (int i = 0; i < products.count(); ++i) {
+ const ResolvedProductPtr &product = products.at(i);
+ foreach (const ResolvedProductPtr &dependency, product->dependencies) {
+ if (!products.contains(dependency))
+ products << dependency;
+ }
+ }
+}
+
+BuildJob *ProjectPrivate::buildProducts(const QList<ResolvedProductPtr> &products,
+ const BuildOptions &options, bool needsDepencencyResolving,
+ QObject *jobOwner)
+{
+ QList<ResolvedProductPtr> productsToBuild = products;
+ if (needsDepencencyResolving)
+ addDependencies(productsToBuild);
+
+ BuildJob * const job = new BuildJob(logger, jobOwner);
+ job->build(internalProject, productsToBuild, options);
+ return job;
+}
+
+CleanJob *ProjectPrivate::cleanProducts(const QList<ResolvedProductPtr> &products,
+ const CleanOptions &options, QObject *jobOwner)
+{
+ CleanJob * const job = new CleanJob(logger, jobOwner);
+ job->clean(internalProject, products, options);
+ return job;
+}
+
+InstallJob *ProjectPrivate::installProducts(const QList<ResolvedProductPtr> &products,
+ const InstallOptions &options, bool needsDepencencyResolving, QObject *jobOwner)
+{
+ QList<ResolvedProductPtr> productsToInstall = products;
+ if (needsDepencencyResolving)
+ addDependencies(productsToInstall);
+ InstallJob * const job = new InstallJob(logger, jobOwner);
+ job->install(internalProject, productsToInstall, options);
+ return job;
+}
+
+QList<ResolvedProductPtr> ProjectPrivate::internalProducts(const QList<ProductData> &products) const
+{
+ QList<ResolvedProductPtr> internalProducts;
+ foreach (const ProductData &product, products) {
+ if (product.isEnabled())
+ internalProducts << internalProduct(product);
+ }
+ return internalProducts;
+}
+
+static QList<ResolvedProductPtr> enabledInternalProducts(const ResolvedProjectConstPtr &project)
+{
+ QList<ResolvedProductPtr> products;
+ foreach (const ResolvedProductPtr &p, project->products) {
+ if (p->enabled)
+ products << p;
+ }
+ foreach (const ResolvedProjectConstPtr &subProject, project->subProjects)
+ products << enabledInternalProducts(subProject);
+ return products;
+}
+
+QList<ResolvedProductPtr> ProjectPrivate::allEnabledInternalProducts() const
+{
+ return enabledInternalProducts(internalProject);
+}
+
+static ResolvedProductPtr internalProductForProject(const ResolvedProjectConstPtr &project,
+ const ProductData &product)
+{
+ foreach (const ResolvedProductPtr &resolvedProduct, project->products) {
+ if (product.name() == resolvedProduct->name)
+ return resolvedProduct;
+ }
+ foreach (const ResolvedProjectConstPtr &subProject, project->subProjects) {
+ const ResolvedProductPtr &p = internalProductForProject(subProject, product);
+ if (p)
+ return p;
+ }
+ return ResolvedProductPtr();
+}
+
+ResolvedProductPtr ProjectPrivate::internalProduct(const ProductData &product) const
+{
+ return internalProductForProject(internalProject, product);
+}
+
+ProductData ProjectPrivate::findProductData(const QString &productName) const
+{
+ foreach (const ProductData &p, m_projectData.allProducts()) {
+ if (p.name() == productName)
+ return p;
+ }
+ return ProductData();
+}
+
+GroupData ProjectPrivate::findGroupData(const ProductData &product, const QString &groupName) const
+{
+ foreach (const GroupData &g, product.groups()) {
+ if (g.name() == groupName)
+ return g;
+ }
+ return GroupData();
+}
+
+GroupData ProjectPrivate::createGroupDataFromGroup(const GroupPtr &resolvedGroup)
+{
+ GroupData group;
+ group.d->name = resolvedGroup->name;
+ group.d->location = resolvedGroup->location;
+ foreach (const SourceArtifactConstPtr &sa, resolvedGroup->files)
+ group.d->filePaths << sa->absoluteFilePath;
+ if (resolvedGroup->wildcards) {
+ foreach (const SourceArtifactConstPtr &sa, resolvedGroup->wildcards->files)
+ group.d->expandedWildcards << sa->absoluteFilePath;
+ }
+ qSort(group.d->filePaths);
+ qSort(group.d->expandedWildcards);
+ group.d->properties.d->m_map = resolvedGroup->properties;
+ group.d->isEnabled = resolvedGroup->enabled;
+ group.d->isValid = true;
+ return group;
+}
+
+void ProjectPrivate::addGroup(const ProductData &product, const QString &groupName)
+{
+ if (groupName.isEmpty())
+ throw ErrorInfo(Tr::tr("Group has an empty name."));
+ if (!product.isValid())
+ throw ErrorInfo(Tr::tr("Product is invalid."));
+ const ResolvedProductPtr resolvedProduct = internalProduct(product);
+ if (!resolvedProduct)
+ throw ErrorInfo(Tr::tr("Product '%1' does not exist.").arg(product.name()));
+
+ // Guard against calls with outdated product data.
+ const ProductData currentProduct= findProductData(product.name());
+ QBS_CHECK(currentProduct.isValid());
+
+ foreach (const GroupPtr &resolvedGroup, resolvedProduct->groups) {
+ if (resolvedGroup->name == groupName) {
+ throw ErrorInfo(Tr::tr("Group '%1' already exists in product '%2'.")
+ .arg(groupName, product.name()), resolvedGroup->location);
+ }
+ }
+
+ ProjectFileGroupInserter groupInserter(currentProduct, groupName);
+ groupInserter.apply();
+
+ m_projectData.d.detach(); // The data we already gave out must stay as it is.
+
+ updateInternalCodeLocations(internalProject, groupInserter.itemPosition(),
+ groupInserter.lineOffset());
+ updateExternalCodeLocations(m_projectData, groupInserter.itemPosition(),
+ groupInserter.lineOffset());
+
+ GroupPtr resolvedGroup = ResolvedGroup::create();
+ resolvedGroup->location = groupInserter.itemPosition();
+ resolvedGroup->enabled = true;
+ resolvedGroup->name = groupName;
+ resolvedGroup->properties = resolvedProduct->properties;
+ resolvedGroup->overrideTags = false;
+ resolvedProduct->groups << resolvedGroup;
+ foreach (const ProductData &newProduct, m_projectData.allProducts()) {
+ if (newProduct.name() == product.name()) {
+ newProduct.d->groups << createGroupDataFromGroup(resolvedGroup);
+ qSort(newProduct.d->groups);
+ break;
+ }
+ }
+}
+
+ProjectPrivate::GroupUpdateContext ProjectPrivate::getGroupContext(const ProductData &product,
+ const GroupData &group)
+{
+ GroupUpdateContext context;
+ if (!product.isValid())
+ throw ErrorInfo(Tr::tr("Product is invalid."));
+ context.resolvedProduct = internalProduct(product);
+ if (!context.resolvedProduct)
+ throw ErrorInfo(Tr::tr("Product '%1' does not exist.").arg(product.name()));
+
+ context.currentProduct = findProductData(product.name());
+ QBS_CHECK(context.currentProduct.isValid());
+
+ const QString groupName = group.isValid() ? group.name() : product.name();
+ foreach (const GroupPtr &g, context.resolvedProduct->groups) {
+ if (g->name == groupName) {
+ context.resolvedGroup = g;
+ break;
+ }
+ }
+ if (!context.resolvedGroup)
+ throw ErrorInfo(Tr::tr("Group '%1' does not exist.").arg(groupName));
+ context.currentGroup = findGroupData(context.currentProduct, groupName);
+ QBS_CHECK(context.currentGroup.isValid());
+ return context;
+}
+
+ProjectPrivate::FileListUpdateContext ProjectPrivate::getFileListContext(const ProductData &product,
+ const GroupData &group, const QStringList &filePaths)
+{
+ FileListUpdateContext filesContext;
+ GroupUpdateContext &groupContext = filesContext.groupContext;
+ groupContext = getGroupContext(product, group);
+
+ if (filePaths.isEmpty())
+ throw ErrorInfo(Tr::tr("No files supplied."));
+
+ if (!groupContext.resolvedGroup->prefix.isEmpty() &&
+ !groupContext.resolvedGroup->prefix.endsWith(QLatin1Char('/'))) {
+ throw ErrorInfo(Tr::tr("Group has non-directory prefix."));
+ }
+ QString baseDirPath = QFileInfo(product.location().fileName()).dir().absolutePath()
+ + QLatin1Char('/') + groupContext.resolvedGroup->prefix;
+ QDir baseDir(baseDirPath);
+ foreach (const QString &filePath, filePaths) {
+ const QString absPath = QDir::cleanPath(FileInfo::resolvePath(baseDirPath, filePath));
+ if (filesContext.absoluteFilePaths.contains(absPath))
+ throw ErrorInfo(Tr::tr("File '%1' appears more than once.").arg(absPath));
+ if (!FileInfo(absPath).exists())
+ throw ErrorInfo(Tr::tr("File '%1' does not exist.").arg(absPath));
+ filesContext.absoluteFilePaths << absPath;
+ filesContext.relativeFilePaths << baseDir.relativeFilePath(absPath);
+ }
+
+ return filesContext;
+}
+
+void ProjectPrivate::addFiles(const ProductData &product, const GroupData &group,
+ const QStringList &filePaths)
+{
+ FileListUpdateContext filesContext = getFileListContext(product, group, filePaths);
+ GroupUpdateContext &groupContext = filesContext.groupContext;
+
+ // We do not check for entries in other groups, because such doublettes might be legitimate
+ // due to conditions.
+ foreach (const QString &filePath, filesContext.absoluteFilePaths) {
+ foreach (const SourceArtifactConstPtr &sa, groupContext.resolvedGroup->files) {
+ if (sa->absoluteFilePath == filePath) {
+ throw ErrorInfo(Tr::tr("File '%1' already exists in group '%2'.")
+ .arg(filePath, group.name()));
+ }
+ }
+ }
+
+ ProjectFileFilesAdder adder(groupContext.currentProduct,
+ group.isValid() ? groupContext.currentGroup : GroupData(), filesContext.relativeFilePaths);
+ adder.apply();
+
+ m_projectData.d.detach();
+ updateInternalCodeLocations(internalProject, adder.itemPosition(), adder.lineOffset());
+ updateExternalCodeLocations(m_projectData, adder.itemPosition(), adder.lineOffset());
+
+ QList<SourceArtifactPtr> addedSourceArtifacts;
+ foreach (const QString &file, filesContext.absoluteFilePaths) {
+ const SourceArtifactPtr artifact = SourceArtifact::create();
+ artifact->absoluteFilePath = file;
+ artifact->properties = groupContext.resolvedGroup->properties;
+ artifact->fileTags = groupContext.resolvedGroup->fileTags;
+ artifact->overrideFileTags = groupContext.resolvedGroup->overrideTags;
+ ProjectResolver::applyFileTaggers(artifact, groupContext.resolvedProduct, logger);
+ addedSourceArtifacts << artifact;
+ groupContext.resolvedGroup->files << artifact;
+ }
+ if (groupContext.resolvedProduct->enabled) {
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+ foreach (const SourceArtifactConstPtr &sa, addedSourceArtifacts) {
+ Artifact * const artifact = createArtifact(groupContext.resolvedProduct, sa, logger);
+ foreach (const FileTag &ft, artifact->fileTags)
+ artifactsPerFileTag[ft] += artifact;
+ }
+ RulesEvaluationContextPtr &evalContext
+ = groupContext.resolvedProduct->topLevelProject()->buildData->evaluationContext;
+ evalContext = QSharedPointer<RulesEvaluationContext>(new RulesEvaluationContext(logger));
+ RulesApplicator(groupContext.resolvedProduct, artifactsPerFileTag, logger).applyAllRules();
+ addTargetArtifacts(groupContext.resolvedProduct, artifactsPerFileTag, logger);
+ evalContext.clear();
+ }
+ doSanityChecks(internalProject, logger);
+ groupContext.currentGroup.d->filePaths << filesContext.absoluteFilePaths;
+ qSort(groupContext.currentGroup.d->filePaths);
+}
+
+void ProjectPrivate::removeFiles(const ProductData &product, const GroupData &group,
+ const QStringList &filePaths)
+{
+ FileListUpdateContext filesContext = getFileListContext(product, group, filePaths);
+ GroupUpdateContext &groupContext = filesContext.groupContext;
+
+ QStringList filesNotFound = filesContext.absoluteFilePaths;
+ QList<SourceArtifactPtr> sourceArtifacts;
+ foreach (const SourceArtifactPtr &sa, groupContext.resolvedGroup->files) {
+ if (filesNotFound.removeOne(sa->absoluteFilePath))
+ sourceArtifacts << sa;
+ }
+ if (!filesNotFound.isEmpty()) {
+ throw ErrorInfo(Tr::tr("The following files are not known to qbs: %1")
+ .arg(filesNotFound.join(QLatin1String(", "))));
+ }
+
+ ProjectFileFilesRemover remover(groupContext.currentProduct,
+ group.isValid() ? groupContext.currentGroup
+ : GroupData(), filesContext.relativeFilePaths);
+ remover.apply();
+
+ removeFilesFromBuildGraph(groupContext.resolvedProduct, sourceArtifacts);
+ foreach (const SourceArtifactPtr &sa, sourceArtifacts) {
+ const bool removed = groupContext.resolvedGroup->files.removeOne(sa);
+ QBS_CHECK(removed);
+ }
+ doSanityChecks(internalProject, logger);
+
+ m_projectData.d.detach();
+ updateInternalCodeLocations(internalProject, remover.itemPosition(), remover.lineOffset());
+ updateExternalCodeLocations(m_projectData, remover.itemPosition(), remover.lineOffset());
+ foreach (const QString &filePath, filesContext.absoluteFilePaths) {
+ const bool removed = groupContext.currentGroup.d->filePaths.removeOne(filePath);
+ QBS_CHECK(removed);
+ }
+}
+
+void ProjectPrivate::removeGroup(const ProductData &product, const GroupData &group)
+{
+ GroupUpdateContext context = getGroupContext(product, group);
+
+ ProjectFileGroupRemover remover(context.currentProduct, context.currentGroup);
+ remover.apply();
+
+ removeFilesFromBuildGraph(context.resolvedProduct, context.resolvedGroup->allFiles());
+ bool removed = context.resolvedProduct->groups.removeOne(context.resolvedGroup);
+ QBS_CHECK(removed);
+ doSanityChecks(internalProject, logger);
+
+ m_projectData.d.detach();
+ updateInternalCodeLocations(internalProject, remover.itemPosition(), remover.lineOffset());
+ updateExternalCodeLocations(m_projectData, remover.itemPosition(), remover.lineOffset());
+ removed = context.currentProduct.d->groups.removeOne(context.currentGroup);
+ QBS_CHECK(removed);
+}
+
+void ProjectPrivate::removeFilesFromBuildGraph(const ResolvedProductConstPtr &product,
+ const QList<SourceArtifactPtr> &files)
+{
+ if (!product->enabled)
+ return;
+ QBS_CHECK(internalProject->buildData);
+ foreach (const SourceArtifactPtr &sa, files) {
+ Artifact * const artifact = lookupArtifact(product, sa->absoluteFilePath);
+ QBS_CHECK(artifact);
+ internalProject->buildData->removeArtifactAndExclusiveDependents(artifact, logger);
+ }
+ RulesEvaluationContextPtr &evalContext
+ = internalProject->buildData->evaluationContext;
+ evalContext = QSharedPointer<RulesEvaluationContext>(new RulesEvaluationContext(logger));
+ internalProject->buildData->updateNodesThatMustGetNewTransformer(logger);
+ evalContext.clear();
+}
+
+static void updateLocationIfNecessary(CodeLocation &location, const CodeLocation &changeLocation,
+ int lineOffset)
+{
+ if (location.fileName() == changeLocation.fileName()
+ && location.line() >= changeLocation.line()) {
+ location = CodeLocation(location.fileName(), location.line() + lineOffset,
+ location.column());
+ }
+}
+
+void ProjectPrivate::updateInternalCodeLocations(const ResolvedProjectPtr &project,
+ const CodeLocation &changeLocation, int lineOffset)
+{
+ if (lineOffset == 0)
+ return;
+ updateLocationIfNecessary(project->location, changeLocation, lineOffset);
+ foreach (const ResolvedProjectPtr &subProject, project->subProjects)
+ updateInternalCodeLocations(subProject, changeLocation, lineOffset);
+ foreach (const ResolvedProductPtr &product, project->products) {
+ updateLocationIfNecessary(product->location, changeLocation, lineOffset);
+ foreach (const GroupPtr &group, product->groups)
+ updateLocationIfNecessary(group->location, changeLocation, lineOffset);
+ foreach (const RulePtr &rule, product->rules) {
+ updateLocationIfNecessary(rule->script->location, changeLocation, lineOffset);
+ foreach (const RuleArtifactPtr &artifact, rule->artifacts) {
+ for (int i = 0; i < artifact->bindings.count(); ++i) {
+ updateLocationIfNecessary(artifact->bindings[i].location, changeLocation,
+ lineOffset);
+ }
+ }
+ }
+ foreach (const ResolvedTransformerConstPtr &transformer, product->transformers)
+ updateLocationIfNecessary(transformer->transform->location, changeLocation, lineOffset);
+ foreach (const ResolvedModuleConstPtr &module, product->modules) {
+ updateLocationIfNecessary(module->setupBuildEnvironmentScript->location,
+ changeLocation, lineOffset);
+ updateLocationIfNecessary(module->setupRunEnvironmentScript->location,
+ changeLocation, lineOffset);
+ }
+ }
+}
+
+void ProjectPrivate::updateExternalCodeLocations(const ProjectData &project,
+ const CodeLocation &changeLocation, int lineOffset)
+{
+ if (lineOffset == 0)
+ return;
+ updateLocationIfNecessary(project.d->location, changeLocation, lineOffset);
+ foreach (const ProjectData &subProject, project.subProjects())
+ updateExternalCodeLocations(subProject, changeLocation, lineOffset);
+ foreach (const ProductData &product, project.products()) {
+ updateLocationIfNecessary(product.d->location, changeLocation, lineOffset);
+ foreach (const GroupData &group, product.groups())
+ updateLocationIfNecessary(group.d->location, changeLocation, lineOffset);
+ }
+}
+
+void ProjectPrivate::prepareChangeToProject()
+{
+ if (internalProject->locked)
+ throw ErrorInfo(Tr::tr("A job is currently in process."));
+ if (!m_projectDataRetrieved)
+ retrieveProjectData(m_projectData, internalProject);
+}
+
+void ProjectPrivate::retrieveProjectData(ProjectData &projectData,
+ const ResolvedProjectConstPtr &internalProject)
+{
+ projectData.d->name = internalProject->name;
+ projectData.d->location = internalProject->location;
+ projectData.d->enabled = internalProject->enabled;
+ foreach (const ResolvedProductConstPtr &resolvedProduct, internalProject->products) {
+ ProductData product;
+ product.d->name = resolvedProduct->name;
+ product.d->location = resolvedProduct->location;
+ product.d->isEnabled = resolvedProduct->enabled;
+ foreach (const GroupPtr &resolvedGroup, resolvedProduct->groups)
+ product.d->groups << createGroupDataFromGroup(resolvedGroup);
+ if (resolvedProduct->enabled) {
+ QBS_CHECK(resolvedProduct->buildData);
+ foreach (const Artifact * const a, resolvedProduct->buildData->targetArtifacts) {
+ TargetArtifact ta;
+ ta.d->filePath = a->filePath();
+ ta.d->fileTags = a->fileTags.toStringList();
+ ta.d->properties.d->m_map = a->properties;
+ ta.d->isValid = true;
+ product.d->targetArtifacts << ta;
+ }
+ }
+ qSort(product.d->groups);
+ qSort(product.d->targetArtifacts);
+ product.d->isValid = true;
+ projectData.d->products << product;
+ }
+ foreach (const ResolvedProjectConstPtr &internalSubProject, internalProject->subProjects) {
+ ProjectData subProject;
+ retrieveProjectData(subProject, internalSubProject);
+ projectData.d->subProjects << subProject;
+ }
+ projectData.d->isValid = true;
+ qSort(projectData.d->products);
+ qSort(projectData.d->subProjects);
+ m_projectDataRetrieved = true;
+}
+
+} // namespace Internal
+
+using namespace Internal;
+
+ /*!
+ * \class Project
+ * \brief The \c Project class provides services related to a qbs project.
+ */
+
+Project::Project(const TopLevelProjectPtr &internalProject, const Logger &logger)
+ : d(new ProjectPrivate(internalProject, logger))
+{
+}
+
+Project::Project(const Project &other) : d(other.d)
+{
+}
+
+Project::~Project()
+{
+}
+
+/*!
+ * \brief Returns true if and only if this object was retrieved from a successful \c SetupProjectJob.
+ * \sa SetupProjectJob
+ */
+bool Project::isValid() const
+{
+ return d && d->internalProject;
+}
+
+Project &Project::operator=(const Project &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ * \brief Sets up a \c Project from a source file, possibly re-using previously stored information.
+ * The function will finish immediately, returning a \c SetupProjectJob which can be used to
+ * track the results of the operation.
+ * \note The qbs plugins will only be loaded once. As a result, the value of
+ * \c parameters.pluginPaths will only have an effect the first time this function is called.
+ * Similarly, the value of \c parameters.searchPaths will not have an effect if
+ * a stored build graph is available.
+ */
+SetupProjectJob *Project::setupProject(const SetupProjectParameters &parameters,
+ ILogSink *logSink, QObject *jobOwner)
+{
+ Logger logger(logSink);
+ SetupProjectJob * const job = new SetupProjectJob(logger, jobOwner);
+ try {
+ loadPlugins(parameters.pluginPaths(), logger);
+ job->resolve(parameters);
+ } catch (const ErrorInfo &error) {
+ // Throwing from here would complicate the API, so let's report the error the same way
+ // as all others, via AbstractJob::error().
+ job->reportError(error);
+ }
+ return job;
+}
+
+Project::Project()
+{
+}
+
+
+/*!
+ * \brief Retrieves information for this project.
+ * Call this function if you need insight into the project structure, e.g. because you want to know
+ * which products or files are in it.
+ */
+ProjectData Project::projectData() const
+{
+ return d->projectData();
+}
+
+/*!
+ * \brief Returns the file path of the executable associated with the given product.
+ * If the product is not an application, an empty string is returned.
+ * The \a installOptions parameter is used to look up the executable in case it is installable;
+ * otherwise the parameter is ignored and the returned path will point to where the file is built.
+ */
+QString Project::targetExecutable(const ProductData &product,
+ const InstallOptions &installOptions) const
+{
+ if (!product.isEnabled())
+ return QString();
+ foreach (const TargetArtifact &ta, product.targetArtifacts()) {
+ if (ta.isExecutable()) {
+ const QList<InstallableFile> &installables
+ = installableFilesForProduct(product, installOptions);
+ foreach (const InstallableFile &file, installables) {
+ if (file.sourceFilePath() == ta.filePath())
+ return file.targetFilePath();
+ }
+ return ta.filePath();
+ }
+ }
+ return QString();
+}
+
+RunEnvironment Project::getRunEnvironment(const ProductData &product,
+ const QProcessEnvironment &environment, Settings *settings) const
+{
+ QBS_CHECK(product.isEnabled());
+ const ResolvedProductPtr resolvedProduct = d->internalProduct(product);
+ return RunEnvironment(resolvedProduct, environment, settings, d->logger);
+}
+
+/*!
+ * \brief Causes all products of this project to be built, if necessary.
+ * The function will finish immediately, returning a \c BuildJob identifiying the operation.
+ */
+BuildJob *Project::buildAllProducts(const BuildOptions &options, QObject *jobOwner) const
+{
+ return d->buildProducts(d->allEnabledInternalProducts(), options, false, jobOwner);
+}
+
+/*!
+ * \brief Causes the specified list of products to be built.
+ * Use this function if you only want to build some products, not the whole project. If any of
+ * the products in \a products depend on other products, those will also be built.
+ * The function will finish immediately, returning a \c BuildJob identifiying the operation.
+ */
+BuildJob *Project::buildSomeProducts(const QList<ProductData> &products,
+ const BuildOptions &options, QObject *jobOwner) const
+{
+ return d->buildProducts(d->internalProducts(products), options, true, jobOwner);
+}
+
+/*!
+ * \brief Convenience function for \c buildSomeProducts().
+ * \sa Project::buildSomeProducts().
+ */
+BuildJob *Project::buildOneProduct(const ProductData &product, const BuildOptions &options,
+ QObject *jobOwner) const
+{
+ return buildSomeProducts(QList<ProductData>() << product, options, jobOwner);
+}
+
+/*!
+ * \brief Removes the build artifacts of all products in the project.
+ * The function will finish immediately, returning a \c CleanJob identifiying this operation.
+ * \sa Project::cleanSomeProducts()
+ */
+CleanJob *Project::cleanAllProducts(const CleanOptions &options, QObject *jobOwner) const
+{
+ return d->cleanProducts(d->allEnabledInternalProducts(), options, jobOwner);
+}
+
+/*!
+ * \brief Removes the build artifacts of the given products.
+ * The function will finish immediately, returning a \c CleanJob identifiying this operation.
+ */
+CleanJob *Project::cleanSomeProducts(const QList<ProductData> &products,
+ const CleanOptions &options, QObject *jobOwner) const
+{
+ return d->cleanProducts(d->internalProducts(products), options, jobOwner);
+}
+
+/*!
+ * \brief Convenience function for \c cleanSomeProducts().
+ * \sa Project::cleanSomeProducts().
+ */
+CleanJob *Project::cleanOneProduct(const ProductData &product, const CleanOptions &options,
+ QObject *jobOwner) const
+{
+ return cleanSomeProducts(QList<ProductData>() << product, options, jobOwner);
+}
+
+/*!
+ * \brief Installs the installable files of all products in the project.
+ * The function will finish immediately, returning an \c InstallJob identifiying this operation.
+ */
+InstallJob *Project::installAllProducts(const InstallOptions &options, QObject *jobOwner) const
+{
+ return d->installProducts(d->allEnabledInternalProducts(), options, false, jobOwner);
+}
+
+/*!
+ * \brief Installs the installable files of the given products.
+ * The function will finish immediately, returning an \c InstallJob identifiying this operation.
+ */
+InstallJob *Project::installSomeProducts(const QList<ProductData> &products,
+ const InstallOptions &options, QObject *jobOwner) const
+{
+ return d->installProducts(d->internalProducts(products), options, true, jobOwner);
+}
+
+/*!
+ * \brief Convenience function for \c installSomeProducts().
+ * \sa Project::installSomeProducts().
+ */
+InstallJob *Project::installOneProduct(const ProductData &product, const InstallOptions &options,
+ QObject *jobOwner) const
+{
+ return installSomeProducts(QList<ProductData>() << product, options, jobOwner);
+}
+
+/*!
+ * \brief All files in the product for which "qbs.install" is true.
+ * This includes source files as well as generated files.
+ */
+QList<InstallableFile> Project::installableFilesForProduct(const ProductData &product,
+ const InstallOptions &options) const
+{
+ QList<InstallableFile> installableFiles;
+ const ResolvedProductConstPtr internalProduct = d->internalProduct(product);
+ if (!internalProduct)
+ return installableFiles;
+ InstallOptions mutableOptions = options;
+ foreach (const GroupConstPtr &group, internalProduct->groups) {
+ foreach (const SourceArtifactConstPtr &artifact, group->allFiles()) {
+ InstallableFile f;
+ const QString &targetFilePath = ProductInstaller::targetFilePath(internalProduct->topLevelProject(),
+ artifact->absoluteFilePath, artifact->properties, mutableOptions,
+ &f.d->targetDirectory);
+ if (targetFilePath.isEmpty())
+ continue;
+ f.d->sourceFilePath = artifact->absoluteFilePath;
+ f.d->fileTags = artifact->fileTags.toStringList();
+ f.d->isValid = true;
+ installableFiles << f;
+ }
+ }
+ if (internalProduct->enabled) {
+ QBS_CHECK(internalProduct->buildData);
+ foreach (const Artifact * const artifact, internalProduct->buildData->artifacts) {
+ if (artifact->artifactType == Artifact::SourceFile)
+ continue;
+ InstallableFile f;
+ const QString &targetFilePath = ProductInstaller::targetFilePath(internalProduct->topLevelProject(),
+ artifact->filePath(), artifact->properties, mutableOptions,
+ &f.d->targetDirectory);
+ if (targetFilePath.isEmpty())
+ continue;
+ f.d->sourceFilePath = artifact->filePath();
+ f.d->fileTags = artifact->fileTags.toStringList();
+ f.d->isValid = true;
+ installableFiles << f;
+ }
+ }
+ qSort(installableFiles);
+ return installableFiles;
+}
+
+/*!
+ * \brief All files in the project for which "qbs.install" is true.
+ * This includes all sub-projects.
+ * \sa Project::installableFilesForProduct()
+ */
+QList<InstallableFile> Project::installableFilesForProject(const ProjectData &project,
+ const InstallOptions &options) const
+{
+ QList<InstallableFile> installableFiles;
+ foreach (const ProductData &p, project.allProducts())
+ installableFiles << installableFilesForProduct(p, options);
+ qSort(installableFiles);
+ return installableFiles;
+}
+
+/*!
+ * \brief Updates the timestamps of all build artifacts in the given products.
+ * Afterwards, the build graph will have the same state as if a successful build had been done.
+ */
+void Project::updateTimestamps(const QList<ProductData> &products)
+{
+ TimestampsUpdater().updateTimestamps(d->internalProject, d->internalProducts(products),
+ d->logger);
+}
+
+/*!
+ * \brief Finds files generated from the given file in the given product.
+ * The function returns files generated from the given file and the given product. To do so it will
+ * traverse the graph of generated files and the files generated from those files.
+ *
+ * If an empty list of tags is given, then all directly and indirectly generated files will be
+ * returned. If there are tags, then processing will stop once matching files were found.
+ */
+QStringList Project::generatedFiles(const ProductData &product, const QString &file,
+ const QStringList &tags) const
+{
+ const ResolvedProductConstPtr internalProduct = d->internalProduct(product);
+ return internalProduct->generatedFiles(file, FileTags::fromStringList(tags));
+}
+
+QVariantMap Project::projectConfiguration() const
+{
+ return d->internalProject->buildConfiguration();
+}
+
+QHash<QString, QString> Project::usedEnvironment() const
+{
+ return d->internalProject->usedEnvironment;
+}
+
+QSet<QString> Project::buildSystemFiles() const
+{
+ return d->internalProject->buildSystemFiles;
+}
+
+/*!
+ * \brief Adds a new empty group to the given product.
+ * Returns an \c ErrorInfo object for which \c hasError() is false in case of a success
+ * and true otherwise. In the latter case, the object will have a sensible description.
+ * After calling this function, it is recommended to re-fetch the project data, as other
+ * items can be affected.
+ * \sa qbs::Project::projectData()
+ */
+ErrorInfo Project::addGroup(const ProductData &product, const QString &groupName)
+{
+ try {
+ d->prepareChangeToProject();
+ d->addGroup(product, groupName);
+ return ErrorInfo();
+ } catch (ErrorInfo errorInfo) {
+ errorInfo.prepend(Tr::tr("Failure adding group '%1' to product '%2'.")
+ .arg(groupName, product.name()));
+ return errorInfo;
+ }
+}
+
+/*!
+ * \brief Adds the given files to the given product.
+ * If \c group is a default-constructed object, the files will be added to the product's
+ * "files" property, otherwise to the one of \c group.
+ * The file paths can be absolute or relative to the location of \c product (including a possible
+ * prefix in the group). The project file will always contain relative paths.
+ * After calling this function, it is recommended to re-fetch the project data, as other
+ * items can be affected.
+ * \sa qbs::Project::projectData()
+ */
+ErrorInfo Project::addFiles(const ProductData &product, const GroupData &group,
+ const QStringList &filePaths)
+{
+ try {
+ d->prepareChangeToProject();
+ d->addFiles(product, group, filePaths);
+ return ErrorInfo();
+ } catch (ErrorInfo errorInfo) {
+ errorInfo.prepend(Tr::tr("Failure adding files to product."));
+ return errorInfo;
+ }
+}
+
+/*!
+ * \brief Removes the given files from the given product.
+ * If \c group is a default-constructed object, the files will be removed from the product's
+ * "files" property, otherwise from the one of \c group.
+ * The file paths can be absolute or relative to the location of \c product (including a possible
+ * prefix in the group).
+ * After calling this function, it is recommended to re-fetch the project data, as other
+ * items can be affected.
+ * \sa qbs::Project::projectData()
+ */
+ErrorInfo Project::removeFiles(const ProductData &product, const GroupData &group,
+ const QStringList &filePaths)
+{
+ try {
+ d->prepareChangeToProject();
+ d->removeFiles(product, group, filePaths);
+ return ErrorInfo();
+ } catch (ErrorInfo errorInfo) {
+ errorInfo.prepend(Tr::tr("Failure removing files from product '%1'.").arg(product.name()));
+ return errorInfo;
+ }
+}
+
+/*!
+ * \brief Removes the given group from the given product.
+ * After calling this function, it is recommended to re-fetch the project data, as other
+ * items can be affected.
+ * \sa qbs::Project::projectData()
+ */
+ErrorInfo Project::removeGroup(const ProductData &product, const GroupData &group)
+{
+ try {
+ d->prepareChangeToProject();
+ d->removeGroup(product, group);
+ return ErrorInfo();
+ } catch (ErrorInfo errorInfo) {
+ errorInfo.prepend(Tr::tr("Failure removing group '%1' from product '%2'.")
+ .arg(group.name(), product.name()));
+ return errorInfo;
+ }
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/api/project.h b/src/lib/corelib/api/project.h
new file mode 100644
index 000000000..0118977c5
--- /dev/null
+++ b/src/lib/corelib/api/project.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROJECT_H
+#define QBS_PROJECT_H
+
+#include "../language/forward_decls.h"
+#include "../tools/qbs_export.h"
+
+#include <QExplicitlySharedDataPointer>
+#include <QHash>
+#include <QList>
+#include <QSet>
+#include <QStringList>
+#include <QVariantMap>
+
+QT_BEGIN_NAMESPACE
+class QObject;
+class QProcessEnvironment;
+QT_END_NAMESPACE
+
+namespace qbs {
+class BuildJob;
+class BuildOptions;
+class CleanJob;
+class CleanOptions;
+class ErrorInfo;
+class GroupData;
+class ILogSink;
+class InstallableFile;
+class InstallJob;
+class InstallOptions;
+class ProductData;
+class ProjectData;
+class RunEnvironment;
+class Settings;
+class SetupProjectJob;
+class SetupProjectParameters;
+
+namespace Internal {
+class Logger;
+class ProjectPrivate;
+} // namespace Internal;
+
+class QBS_EXPORT Project
+{
+ friend class SetupProjectJob;
+ friend uint qHash(const Project &p);
+public:
+ static SetupProjectJob *setupProject(const SetupProjectParameters &parameters,
+ ILogSink *logSink, QObject *jobOwner);
+
+ Project();
+ Project(const Project &other);
+ Project &operator=(const Project &other);
+ ~Project();
+
+ bool isValid() const;
+ ProjectData projectData() const;
+ QString targetExecutable(const ProductData &product,
+ const InstallOptions &installoptions) const;
+ RunEnvironment getRunEnvironment(const ProductData &product,
+ const QProcessEnvironment &environment, Settings *settings) const;
+
+ BuildJob *buildAllProducts(const BuildOptions &options, QObject *jobOwner = 0) const;
+ BuildJob *buildSomeProducts(const QList<ProductData> &products, const BuildOptions &options,
+ QObject *jobOwner = 0) const;
+ BuildJob *buildOneProduct(const ProductData &product, const BuildOptions &options,
+ QObject *jobOwner = 0) const;
+
+ CleanJob *cleanAllProducts(const CleanOptions &options, QObject *jobOwner = 0) const;
+ CleanJob *cleanSomeProducts(const QList<ProductData> &products, const CleanOptions &options,
+ QObject *jobOwner = 0) const;
+ CleanJob *cleanOneProduct(const ProductData &product, const CleanOptions &options,
+ QObject *jobOwner = 0) const;
+
+ InstallJob *installAllProducts(const InstallOptions &options, QObject *jobOwner = 0) const;
+ InstallJob *installSomeProducts(const QList<ProductData> &products,
+ const InstallOptions &options, QObject *jobOwner = 0) const;
+ InstallJob *installOneProduct(const ProductData &product, const InstallOptions &options,
+ QObject *jobOwner = 0) const;
+
+ QList<InstallableFile> installableFilesForProduct(const ProductData &product,
+ const InstallOptions &options) const;
+ QList<InstallableFile> installableFilesForProject(const ProjectData &project,
+ const InstallOptions &options) const;
+
+ void updateTimestamps(const QList<ProductData> &products);
+
+ bool operator==(const Project &other) const { return d.data() == other.d.data(); }
+
+ QStringList generatedFiles(const ProductData &product, const QString &file,
+ const QStringList &tags = QStringList()) const;
+
+ QVariantMap projectConfiguration() const;
+ QHash<QString, QString> usedEnvironment() const;
+
+ QSet<QString> buildSystemFiles() const;
+
+ ErrorInfo addGroup(const ProductData &product, const QString &groupName);
+ ErrorInfo addFiles(const ProductData &product, const GroupData &group,
+ const QStringList &filePaths);
+ ErrorInfo removeFiles(const ProductData &product, const GroupData &group,
+ const QStringList &filePaths);
+ ErrorInfo removeGroup(const ProductData &product, const GroupData &group);
+
+private:
+ Project(const Internal::TopLevelProjectPtr &internalProject, const Internal::Logger &logger);
+
+ QExplicitlySharedDataPointer<Internal::ProjectPrivate> d;
+};
+
+inline bool operator!=(const Project &p1, const Project &p2) { return !(p1 == p2); }
+inline uint qHash(const Project &p) { return QT_PREPEND_NAMESPACE(qHash)(p.d.data()); }
+
+} // namespace qbs
+
+#endif // QBS_PROJECT_H
diff --git a/src/lib/corelib/api/projectdata.cpp b/src/lib/corelib/api/projectdata.cpp
new file mode 100644
index 000000000..a5cc6093e
--- /dev/null
+++ b/src/lib/corelib/api/projectdata.cpp
@@ -0,0 +1,674 @@
+/****************************************************************************
+**
+** 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 "projectdata.h"
+
+#include "projectdata_p.h"
+#include "propertymap_p.h"
+#include <language/propertymapinternal.h>
+#include <tools/fileinfo.h>
+#include <tools/propertyfinder.h>
+#include <tools/qbsassert.h>
+#include <tools/scripttools.h>
+
+namespace qbs {
+
+/*!
+ * \class GroupData
+ * \brief The \c GroupData class corresponds to the Group item in a qbs source file.
+ */
+
+GroupData::GroupData() : d(new Internal::GroupDataPrivate)
+{
+}
+
+GroupData::GroupData(const GroupData &other) : d(other.d)
+{
+}
+
+GroupData &GroupData::operator=(const GroupData &other)
+{
+ d = other.d;
+ return *this;
+}
+
+GroupData::~GroupData()
+{
+}
+
+/*!
+ * \brief Returns true if and only if the Group holds data that was initialized by Qbs.
+ */
+bool GroupData::isValid() const
+{
+ return d->isValid;
+}
+
+/*!
+ * \brief The location at which the group is defined in the respective source file.
+ */
+CodeLocation GroupData::location() const
+{
+ return d->location;
+}
+
+/*!
+ * \brief The name of the group.
+ */
+QString GroupData::name() const
+{
+ return d->name;
+}
+
+/*!
+ * \brief The files listed in the group item's "files" binding.
+ * \note These do not include expanded wildcards.
+ * \sa GroupData::expandedWildcards
+ */
+QStringList GroupData::filePaths() const
+{
+ return d->filePaths;
+}
+
+/*!
+ * \brief The list of files resulting from expanding all wildcard patterns in the group.
+ */
+QStringList GroupData::expandedWildcards() const
+{
+ return d->expandedWildcards;
+}
+
+/*!
+ * \brief The set of properties valid in this group.
+ * Typically, most of them are inherited from the respective \c Product.
+ */
+PropertyMap GroupData::properties() const
+{
+ return d->properties;
+}
+
+/*!
+ * \brief Returns true if this group is enabled in Qbs
+ * This method returns the "condition" property of the \c Group definition. If the group is enabled
+ * then the files in this group will be processed, provided the product it belongs to is also
+ * enabled.
+ *
+ * Note that a group can be enabled, even if the product it belongs to is not. In this case
+ * the files in the group will not be processed.
+ * \sa ProductData::isEnabled()
+ */
+bool GroupData::isEnabled() const
+{
+ QBS_ASSERT(isValid(), return false);
+ return d->isEnabled;
+}
+
+/*!
+ * \fn QStringList GroupData::allFilePaths() const
+ * \brief All files in this group, regardless of how whether they were given explicitly
+ * or via wildcards.
+ * \sa GroupData::filePaths
+ * \sa GroupData::expandedWildcards
+ */
+QStringList GroupData::allFilePaths() const
+{
+ return d->filePaths + d->expandedWildcards;
+}
+
+bool operator!=(const GroupData &lhs, const GroupData &rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator==(const GroupData &lhs, const GroupData &rhs)
+{
+ return lhs.name() == rhs.name()
+ && lhs.location() == rhs.location()
+ && lhs.expandedWildcards() == rhs.expandedWildcards()
+ && lhs.filePaths() == rhs.filePaths()
+ && lhs.properties() == rhs.properties()
+ && lhs.isEnabled() == rhs.isEnabled();
+}
+
+bool operator<(const GroupData &lhs, const GroupData &rhs)
+{
+ return lhs.name() < rhs.name();
+}
+
+
+/*!
+ * \class TargetArtifact
+ * \brief The \c TargetArtifact class describes a top-level build result of a product.
+ * For instance, the target artifact of a product with type "application" is an executable file.
+ */
+
+TargetArtifact::TargetArtifact() : d(new Internal::TargetArtifactPrivate)
+{
+}
+
+TargetArtifact::TargetArtifact(const TargetArtifact &other) : d(other.d)
+{
+}
+
+TargetArtifact &TargetArtifact::operator=(const TargetArtifact &other)
+{
+ d = other.d;
+ return *this;
+}
+
+TargetArtifact::~TargetArtifact()
+{
+}
+
+/*!
+ * \brief Returns true if and only if this object holds data that was initialized by Qbs.
+ */
+bool TargetArtifact::isValid() const
+{
+ return d->isValid;
+}
+
+/*!
+ * \brief The full path of this file.
+ */
+QString TargetArtifact::filePath() const
+{
+ return d->filePath;
+}
+
+/*!
+ * \brief The tags of this file.
+ * Typically, this list will contain just one element.
+ */
+QStringList TargetArtifact::fileTags() const
+{
+ return d->fileTags;
+}
+
+/*!
+ * \brief True if and only if this file is executable,
+ * either natively or through an interpreter or shell.
+ */
+bool TargetArtifact::isExecutable() const
+{
+ return d->fileTags.contains(QLatin1String("application"))
+ || d->fileTags.contains(QLatin1String("applicationbundle"))
+ || d->fileTags.contains(QLatin1String("msi"));
+}
+
+/*!
+ * \brief The properties of this file.
+ */
+PropertyMap TargetArtifact::properties() const
+{
+ return d->properties;
+}
+
+bool operator==(const TargetArtifact &ta1, const TargetArtifact &ta2)
+{
+ return ta1.filePath() == ta2.filePath()
+ && ta1.fileTags() == ta2.fileTags()
+ && ta1.properties() == ta2.properties();
+}
+
+bool operator!=(const TargetArtifact &ta1, const TargetArtifact &ta2)
+{
+ return !(ta1 == ta2);
+}
+
+bool operator<(const TargetArtifact &ta1, const TargetArtifact &ta2)
+{
+ return ta1.filePath() < ta2.filePath();
+}
+
+
+/*!
+ * \class InstallableFile
+ * \brief Describes a file that is marked for installation.
+ */
+
+InstallableFile::InstallableFile() : d(new Internal::InstallableFilePrivate)
+{
+}
+
+InstallableFile::InstallableFile(const InstallableFile &other) : d(other.d)
+{
+}
+
+InstallableFile &InstallableFile::operator=(const InstallableFile &other)
+{
+ d = other.d;
+ return *this;
+}
+
+InstallableFile::~InstallableFile()
+{
+}
+
+/*!
+ * \brief Returns true if and only if this object holds data that was initialized by Qbs.
+ */
+bool InstallableFile::isValid() const
+{
+ return d->isValid;
+}
+
+/*!
+ * \brief The location of the file from which it will be copied to \c targetFilePath()
+ * on installation.
+ */
+QString InstallableFile::sourceFilePath() const
+{
+ return d->sourceFilePath;
+}
+
+/*!
+ * \brief The directory that this file will be copied into on installation.
+ */
+QString InstallableFile::targetDirectory() const
+{
+ return d->targetDirectory;
+}
+
+/*!
+ * \brief The file path that this file will be copied to on installation.
+ * This is a convenience function.
+ * \sa InstallableFile::targetDirectory()
+ */
+QString InstallableFile::targetFilePath() const
+{
+ return d->targetDirectory + QLatin1Char('/') + Internal::FileInfo::fileName(d->sourceFilePath);
+}
+
+/*!
+ * \brief The file's tags.
+ */
+QStringList InstallableFile::fileTags() const
+{
+ return d->fileTags;
+}
+
+/*!
+ * \brief True if and only if the file is an executable.
+ */
+bool InstallableFile::isExecutable() const
+{
+ return d->fileTags.contains(QLatin1String("application"))
+ || d->fileTags.contains(QLatin1String("applicationbundle"));
+}
+
+bool operator==(const InstallableFile &file1, const InstallableFile &file2)
+{
+ return file1.sourceFilePath() == file2.sourceFilePath()
+ && file1.targetFilePath() == file2.targetFilePath()
+ && file1.fileTags() == file2.fileTags();
+}
+
+bool operator!=(const InstallableFile &file1, const InstallableFile &file2)
+{
+ return !(file1 == file2);
+}
+
+bool operator<(const InstallableFile &file1, const InstallableFile &file2)
+{
+ return file1.sourceFilePath() < file2.sourceFilePath();
+}
+
+/*!
+ * \class ProductData
+ * \brief The \c ProductData class corresponds to the Product item in a qbs source file.
+ */
+
+ProductData::ProductData() : d(new Internal::ProductDataPrivate)
+{
+}
+
+ProductData::ProductData(const ProductData &other) : d(other.d)
+{
+}
+
+ProductData &ProductData::operator=(const ProductData &other)
+{
+ d = other.d;
+ return *this;
+}
+
+ProductData::~ProductData()
+{
+}
+
+/*!
+ * \brief Returns true if and only if the Product holds data that was initialized by Qbs.
+ */
+bool ProductData::isValid() const
+{
+ return d->isValid;
+}
+
+/*!
+ * \brief The name of the product as given in the qbs source file.
+ */
+QString ProductData::name() const
+{
+ return d->name;
+}
+
+/*!
+ * \brief The location at which the product is defined in the source file.
+ */
+CodeLocation ProductData::location() const
+{
+ return d->location;
+}
+
+/*!
+ * \brief This product's target artifacts.
+ */
+QList<TargetArtifact> ProductData::targetArtifacts() const
+{
+ return d->targetArtifacts;
+}
+
+/*!
+ * \brief The list of \c GroupData in this product.
+ */
+QList<GroupData> ProductData::groups() const
+{
+ return d->groups;
+}
+
+/*!
+ * \brief Returns true if this Product is enabled in Qbs.
+ * This method returns the \c condition property of the \c Product definition. If a product is
+ * enabled, then it will be built in the current configuration.
+ * \sa GroupData::isEnabled()
+ */
+bool ProductData::isEnabled() const
+{
+ QBS_ASSERT(isValid(), return false);
+ return d->isEnabled;
+}
+
+bool operator==(const ProductData &lhs, const ProductData &rhs)
+{
+ return lhs.name() == rhs.name()
+ && lhs.location() == rhs.location()
+ && lhs.groups() == rhs.groups()
+ && lhs.targetArtifacts() == rhs.targetArtifacts()
+ && lhs.isEnabled() == rhs.isEnabled();
+}
+
+bool operator!=(const ProductData &lhs, const ProductData &rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator<(const ProductData &lhs, const ProductData &rhs)
+{
+ return lhs.name() < rhs.name();
+}
+
+/*!
+ * \class ProjectData
+ * \brief The \c ProjectData class corresponds to the \c Project item in a qbs source file.
+ */
+
+/*!
+ * \fn QList<ProductData> ProjectData::products() const
+ * \brief The products in this project.
+ */
+
+ProjectData::ProjectData() : d(new Internal::ProjectDataPrivate)
+{
+}
+
+ProjectData::ProjectData(const ProjectData &other) : d(other.d)
+{
+}
+
+ProjectData &ProjectData::operator =(const ProjectData &other)
+{
+ d = other.d;
+ return *this;
+}
+
+ProjectData::~ProjectData()
+{
+}
+
+/*!
+ * \brief Returns true if and only if the Project holds data that was initialized by Qbs.
+ */
+bool ProjectData::isValid() const
+{
+ return d->isValid;
+}
+
+/*!
+ * \brief The name of this project.
+ */
+QString ProjectData::name() const
+{
+ return d->name;
+}
+
+/*!
+ * \brief The location at which the project is defined in a qbs source file.
+ */
+CodeLocation ProjectData::location() const
+{
+ return d->location;
+}
+
+/*!
+ * \brief Whether the project is enabled.
+ * \note Disabled projects never have any products or sub-projects.
+ */
+bool ProjectData::isEnabled() const
+{
+ QBS_ASSERT(isValid(), return false);
+ return d->enabled;
+}
+
+/*!
+ * \brief The base directory under which the build artifacts of this project will be created.
+ * This is only valid for the top-level project.
+ */
+QString ProjectData::buildDirectory() const
+{
+ return d->buildDir;
+}
+
+/*!
+ * The products in this project.
+ * \note This also includes disabled products.
+ */
+QList<ProductData> ProjectData::products() const
+{
+ return d->products;
+}
+
+/*!
+ * The sub-projects of this project.
+ */
+QList<ProjectData> ProjectData::subProjects() const
+{
+ return d->subProjects;
+}
+
+/*!
+ * All products in this projects and its direct and indirect sub-projects.
+ */
+QList<ProductData> ProjectData::allProducts() const
+{
+ QList<ProductData> productList = products();
+ foreach (const ProjectData &pd, subProjects())
+ productList << pd.allProducts();
+ return productList;
+}
+
+bool operator==(const ProjectData &lhs, const ProjectData &rhs)
+{
+ return lhs.location() == rhs.location()
+ && lhs.subProjects() == rhs.subProjects()
+ && lhs.products() == rhs.products();
+}
+
+bool operator!=(const ProjectData &lhs, const ProjectData &rhs)
+{
+ return !(lhs == rhs);
+}
+
+bool operator<(const ProjectData &lhs, const ProjectData &rhs)
+{
+ return lhs.name() < rhs.name();
+}
+
+/*!
+ * \class PropertyMap
+ * \brief The \c PropertyMap class represents the properties of a group or a product.
+ */
+
+PropertyMap::PropertyMap()
+ : d(new Internal::PropertyMapPrivate)
+{
+ static Internal::PropertyMapPtr defaultInternalMap = Internal::PropertyMapInternal::create();
+ d->m_map = defaultInternalMap;
+}
+
+PropertyMap::PropertyMap(const PropertyMap &other)
+ : d(new Internal::PropertyMapPrivate(*other.d))
+{
+}
+
+PropertyMap::~PropertyMap()
+{
+ delete d;
+}
+
+PropertyMap &PropertyMap::operator =(const PropertyMap &other)
+{
+ delete d;
+ d = new Internal::PropertyMapPrivate(*other.d);
+ return *this;
+}
+
+/*!
+ * \brief Returns the names of all properties.
+ */
+QStringList PropertyMap::allProperties() const
+{
+ QStringList properties;
+ for (QVariantMap::ConstIterator it = d->m_map->value().constBegin();
+ it != d->m_map->value().constEnd(); ++it) {
+ if (!it.value().canConvert<QVariantMap>())
+ properties << it.key();
+ }
+ return properties;
+}
+
+/*!
+ * \brief Returns the value of the given property of a product or group.
+ */
+QVariant PropertyMap::getProperty(const QString &name) const
+{
+ return d->m_map->value().value(name);
+}
+
+/*!
+ * \brief Returns the values of the given module property.
+ * This function is intended for properties of list type, such as "cpp.includes".
+ * The values will be gathered both directly from the product/group as well as from the
+ * product's module dependencies.
+ */
+QVariantList PropertyMap::getModuleProperties(const QString &moduleName,
+ const QString &propertyName) const
+{
+ return Internal::PropertyFinder().propertyValues(d->m_map->value(), moduleName, propertyName);
+}
+
+/*!
+ * \brief Convenience function for \c PropertyMap::getModuleProperties.
+ */
+QStringList PropertyMap::getModulePropertiesAsStringList(const QString &moduleName,
+ const QString &propertyName) const
+{
+ const QVariantList &vl = getModuleProperties(moduleName, propertyName);
+ QStringList sl;
+ foreach (const QVariant &v, vl) {
+ QBS_ASSERT(v.canConvert<QString>(), continue);
+ sl << v.toString();
+ }
+ return sl;
+}
+
+/*!
+ * \brief Returns the value of the given module property.
+ * This function is intended for properties of "integral" type, such as "qbs.targetOS".
+ * The property will be looked up first at the product or group itself. If it is not found there,
+ * the module dependencies are searched in undefined order.
+ */
+QVariant PropertyMap::getModuleProperty(const QString &moduleName,
+ const QString &propertyName) const
+{
+ return Internal::PropertyFinder().propertyValue(d->m_map->value(), moduleName, propertyName);
+}
+
+static QString mapToString(const QVariantMap &map, const QString &prefix)
+{
+ QStringList keys(map.keys());
+ qSort(keys);
+ QString stringRep;
+ foreach (const QString &key, keys) {
+ const QVariant &val = map.value(key);
+ if (val.type() == QVariant::Map) {
+ stringRep += mapToString(val.value<QVariantMap>(), prefix + key + QLatin1Char('.'));
+ } else {
+ stringRep += QString::fromLocal8Bit("%1%2: %3\n")
+ .arg(prefix, key, Internal::toJSLiteral(val));
+ }
+ }
+ return stringRep;
+}
+
+QString PropertyMap::toString() const
+{
+ return mapToString(d->m_map->value(), QString());
+}
+
+bool operator==(const PropertyMap &pm1, const PropertyMap &pm2)
+{
+ return pm1.d->m_map->value() == pm2.d->m_map->value();
+}
+
+bool operator!=(const PropertyMap &pm1, const PropertyMap &pm2)
+{
+ return !(pm1.d->m_map->value() == pm2.d->m_map->value());
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/api/projectdata.h b/src/lib/corelib/api/projectdata.h
new file mode 100644
index 000000000..ba0016d0c
--- /dev/null
+++ b/src/lib/corelib/api/projectdata.h
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROJECTDATA_H
+#define QBS_PROJECTDATA_H
+
+#include "../tools/codelocation.h"
+#include "../tools/qbs_export.h"
+
+#include <QExplicitlySharedDataPointer>
+#include <QList>
+#include <QPair>
+#include <QString>
+#include <QStringList>
+#include <QVariantMap>
+
+namespace qbs {
+namespace Internal {
+class GroupDataPrivate;
+class InstallableFilePrivate;
+class ProductDataPrivate;
+class ProjectPrivate;
+class ProjectDataPrivate;
+class PropertyMapPrivate;
+class TargetArtifactPrivate;
+} // namespace Internal
+
+class PropertyMap;
+
+bool operator==(const PropertyMap &pm1, const PropertyMap &pm2);
+bool operator!=(const PropertyMap &pm1, const PropertyMap &pm2);
+
+class QBS_EXPORT PropertyMap
+{
+ friend class Internal::ProjectPrivate;
+ friend bool operator==(const PropertyMap &, const PropertyMap &);
+ friend bool operator!=(const PropertyMap &, const PropertyMap &);
+
+public:
+ PropertyMap();
+ PropertyMap(const PropertyMap &other);
+ ~PropertyMap();
+
+ PropertyMap &operator =(const PropertyMap &other);
+
+ QStringList allProperties() const;
+ QVariant getProperty(const QString &name) const;
+
+ QVariantList getModuleProperties(const QString &moduleName, const QString &propertyName) const;
+ QStringList getModulePropertiesAsStringList(const QString &moduleName,
+ const QString &propertyName) const;
+ QVariant getModuleProperty(const QString &moduleName, const QString &propertyName) const;
+
+ // For debugging.
+ QString toString() const;
+
+private:
+ Internal::PropertyMapPrivate *d;
+};
+
+class QBS_EXPORT GroupData
+{
+ friend class Internal::ProjectPrivate;
+public:
+ GroupData();
+ GroupData(const GroupData &other);
+ GroupData &operator=(const GroupData &other);
+ ~GroupData();
+
+ bool isValid() const;
+
+ CodeLocation location() const;
+ QString name() const;
+ QStringList filePaths() const;
+ QStringList expandedWildcards() const;
+ PropertyMap properties() const;
+ bool isEnabled() const;
+ QStringList allFilePaths() const;
+
+private:
+ QExplicitlySharedDataPointer<Internal::GroupDataPrivate> d;
+};
+
+QBS_EXPORT bool operator==(const GroupData &lhs, const GroupData &rhs);
+QBS_EXPORT bool operator!=(const GroupData &lhs, const GroupData &rhs);
+QBS_EXPORT bool operator<(const GroupData &lhs, const GroupData &rhs);
+
+class QBS_EXPORT TargetArtifact
+{
+ friend class Internal::ProjectPrivate;
+public:
+ TargetArtifact();
+ TargetArtifact(const TargetArtifact &other);
+ TargetArtifact &operator=(const TargetArtifact &other);
+ ~TargetArtifact();
+
+ bool isValid() const;
+
+ QString filePath() const;
+ QStringList fileTags() const;
+ bool isExecutable() const;
+ PropertyMap properties() const;
+
+private:
+ QExplicitlySharedDataPointer<Internal::TargetArtifactPrivate> d;
+};
+
+QBS_EXPORT bool operator==(const TargetArtifact &ta1, const TargetArtifact &ta2);
+QBS_EXPORT bool operator!=(const TargetArtifact &ta1, const TargetArtifact &ta2);
+QBS_EXPORT bool operator<(const TargetArtifact &ta1, const TargetArtifact &ta2);
+
+class QBS_EXPORT InstallableFile
+{
+ friend class Project;
+public:
+ InstallableFile();
+ InstallableFile(const InstallableFile &other);
+ InstallableFile &operator=(const InstallableFile &other);
+ ~InstallableFile();
+
+ bool isValid() const;
+
+ QString sourceFilePath() const;
+ QString targetDirectory() const;
+ QString targetFilePath() const;
+ QStringList fileTags() const;
+ bool isExecutable() const;
+
+private:
+ QExplicitlySharedDataPointer<Internal::InstallableFilePrivate> d;
+};
+
+QBS_EXPORT bool operator==(const InstallableFile &file1, const InstallableFile &file2);
+QBS_EXPORT bool operator!=(const InstallableFile &file1, const InstallableFile &file2);
+QBS_EXPORT bool operator<(const InstallableFile &file1, const InstallableFile &file2);
+
+
+class QBS_EXPORT ProductData
+{
+ friend class Internal::ProjectPrivate;
+public:
+ ProductData();
+ ProductData(const ProductData &other);
+ ProductData &operator=(const ProductData &other);
+ ~ProductData();
+
+ bool isValid() const;
+
+ QString name() const;
+ CodeLocation location() const;
+ QList<TargetArtifact> targetArtifacts() const;
+ QList<GroupData> groups() const;
+ bool isEnabled() const;
+
+private:
+ QExplicitlySharedDataPointer<Internal::ProductDataPrivate> d;
+};
+
+QBS_EXPORT bool operator==(const ProductData &lhs, const ProductData &rhs);
+QBS_EXPORT bool operator!=(const ProductData &lhs, const ProductData &rhs);
+QBS_EXPORT bool operator<(const ProductData &lhs, const ProductData &rhs);
+
+class QBS_EXPORT ProjectData
+{
+ friend class Internal::ProjectPrivate;
+public:
+ ProjectData();
+ ProjectData(const ProjectData &other);
+ ProjectData &operator=(const ProjectData &other);
+ ~ProjectData();
+
+ bool isValid() const;
+
+ QString name() const;
+ CodeLocation location() const;
+ bool isEnabled() const;
+ QString buildDirectory() const;
+ QList<ProductData> products() const;
+ QList<ProjectData> subProjects() const;
+ QList<ProductData> allProducts() const;
+
+private:
+ QExplicitlySharedDataPointer<Internal::ProjectDataPrivate> d;
+};
+
+QBS_EXPORT bool operator==(const ProjectData &lhs, const ProjectData &rhs);
+QBS_EXPORT bool operator!=(const ProjectData &lhs, const ProjectData &rhs);
+QBS_EXPORT bool operator<(const ProjectData &lhs, const ProjectData &rhs);
+
+} // namespace qbs
+
+#endif // QBS_PROJECTDATA_H
diff --git a/src/lib/corelib/api/projectdata_p.h b/src/lib/corelib/api/projectdata_p.h
new file mode 100644
index 000000000..2eda4d932
--- /dev/null
+++ b/src/lib/corelib/api/projectdata_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROJECTDATA_P_H
+#define QBS_PROJECTDATA_P_H
+
+#include "projectdata.h"
+
+#include <QSharedData>
+
+namespace qbs {
+namespace Internal {
+
+class GroupDataPrivate : public QSharedData
+{
+public:
+ GroupDataPrivate() : isValid(false)
+ { }
+
+ QString name;
+ CodeLocation location;
+ QStringList filePaths;
+ QStringList expandedWildcards;
+ PropertyMap properties;
+ bool isEnabled;
+ bool isValid;
+};
+
+class TargetArtifactPrivate : public QSharedData
+{
+public:
+ TargetArtifactPrivate() : isValid(false) {}
+
+ QString filePath;
+ QStringList fileTags;
+ PropertyMap properties;
+ bool isValid;
+};
+
+class InstallableFilePrivate: public QSharedData
+{
+public:
+ InstallableFilePrivate() : isValid(false) {}
+
+ QString sourceFilePath;
+ QString targetDirectory;
+ QStringList fileTags;
+ bool isValid;
+};
+
+class ProductDataPrivate : public QSharedData
+{
+public:
+ ProductDataPrivate() : isValid(false)
+ { }
+
+ QString name;
+ CodeLocation location;
+ QList<GroupData> groups;
+ QList<TargetArtifact> targetArtifacts;
+ bool isEnabled;
+ bool isValid;
+};
+
+class ProjectDataPrivate : public QSharedData
+{
+public:
+ ProjectDataPrivate() : isValid(false)
+ { }
+
+ QString name;
+ CodeLocation location;
+ bool enabled;
+ bool isValid;
+ QList<ProductData> products;
+ QList<ProjectData> subProjects;
+ QString buildDir;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/api/projectfileupdater.cpp b/src/lib/corelib/api/projectfileupdater.cpp
new file mode 100644
index 000000000..e7d879b28
--- /dev/null
+++ b/src/lib/corelib/api/projectfileupdater.cpp
@@ -0,0 +1,475 @@
+/****************************************************************************
+**
+** 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 "projectfileupdater.h"
+
+#include "projectdata.h"
+#include "qmljsrewriter.h"
+
+#include <language/asttools.h>
+#include <logging/translator.h>
+#include <parser/qmljsast_p.h>
+#include <parser/qmljsastvisitor_p.h>
+#include <parser/qmljsengine_p.h>
+#include <parser/qmljslexer_p.h>
+#include <parser/qmljsparser_p.h>
+#include <tools/qbsassert.h>
+
+#include <QFile>
+
+using namespace QbsQmlJS;
+using namespace AST;
+
+namespace qbs {
+namespace Internal {
+
+class ItemFinder : public Visitor
+{
+public:
+ ItemFinder(const CodeLocation &cl) : m_cl(cl), m_item(0) { }
+
+ UiObjectDefinition *item() const { return m_item; }
+
+private:
+ bool visit(UiObjectDefinition *ast)
+ {
+ if (toCodeLocation(m_cl.fileName(), ast->firstSourceLocation()) == m_cl) {
+ m_item = ast;
+ return false;
+ }
+ return true;
+ }
+
+ const CodeLocation m_cl;
+ UiObjectDefinition *m_item;
+};
+
+class FilesBindingFinder : public Visitor
+{
+public:
+ FilesBindingFinder(const UiObjectDefinition *startItem)
+ : m_startItem(startItem), m_binding(0)
+ {
+ }
+
+ UiScriptBinding *binding() const { return m_binding; }
+
+private:
+ bool visit(UiObjectDefinition *ast)
+ {
+ // We start with the direct parent of the binding, so do not descend into any
+ // other item.
+ return ast == m_startItem;
+ }
+
+ bool visit(UiScriptBinding *ast)
+ {
+ if (ast->qualifiedId->name.toString() != QLatin1String("files"))
+ return true;
+ m_binding = ast;
+ return false;
+ }
+
+ const UiObjectDefinition * const m_startItem;
+ UiScriptBinding *m_binding;
+};
+
+
+ProjectFileUpdater::ProjectFileUpdater(const QString &projectFile) : m_projectFile(projectFile)
+{
+}
+
+void ProjectFileUpdater::apply()
+{
+ QFile file(m_projectFile);
+ if (!file.open(QFile::ReadOnly)) {
+ throw ErrorInfo(Tr::tr("File '%1' cannot be opened for reading: %2")
+ .arg(m_projectFile, file.errorString()));
+ }
+ QString content = QString::fromLocal8Bit(file.readAll());
+ file.close();
+ Engine engine;
+ Lexer lexer(&engine);
+ lexer.setCode(content, 1);
+ Parser parser(&engine);
+ if (!parser.parse()) {
+ QList<DiagnosticMessage> parserMessages = parser.diagnosticMessages();
+ if (!parserMessages.isEmpty()) {
+ ErrorInfo errorInfo;
+ errorInfo.append(Tr::tr("Failure parsing project file."));
+ foreach (const DiagnosticMessage &msg, parserMessages)
+ errorInfo.append(msg.message, toCodeLocation(file.fileName(), msg.loc));
+ throw errorInfo;
+ }
+ }
+
+ doApply(content, parser.ast());
+
+ if (!file.open(QFile::WriteOnly)) {
+ throw ErrorInfo(Tr::tr("File '%1' cannot be opened for writing: %2")
+ .arg(m_projectFile, file.errorString()));
+ }
+ file.resize(0);
+ file.write(content.toLocal8Bit());
+}
+
+
+ProjectFileGroupInserter::ProjectFileGroupInserter(const ProductData &product,
+ const QString &groupName)
+ : ProjectFileUpdater(product.location().fileName())
+ , m_product(product)
+ , m_groupName(groupName)
+{
+}
+
+void ProjectFileGroupInserter::doApply(QString &fileContent, UiProgram *ast)
+{
+ ItemFinder itemFinder(m_product.location());
+ ast->accept(&itemFinder);
+ if (!itemFinder.item()) {
+ throw ErrorInfo(Tr::tr("The project file parser failed to find the product item."),
+ CodeLocation(projectFile()));
+ }
+
+ ChangeSet changeSet;
+ Rewriter rewriter(fileContent, &changeSet, QStringList());
+ QString groupItemString;
+ const int productItemIndentation
+ = itemFinder.item()->qualifiedTypeNameId->firstSourceLocation().startColumn - 1;
+ const int groupItemIndentation = productItemIndentation + 4;
+ const QString groupItemIndentationString = QString(groupItemIndentation, QLatin1Char(' '));
+ groupItemString += groupItemIndentationString + QLatin1String("Group {\n");
+ groupItemString += groupItemIndentationString + groupItemIndentationString
+ + QLatin1String("name: \"") + m_groupName + QLatin1String("\"\n");
+ groupItemString += groupItemIndentationString + groupItemIndentationString
+ + QLatin1String("files: []\n");
+ groupItemString += groupItemIndentationString + QLatin1Char('}');
+ rewriter.addObject(itemFinder.item()->initializer, groupItemString);
+
+ int lineOffset = 3 + 1; // Our text + a leading newline that is always added by the rewriter.
+ const QList<ChangeSet::EditOp> &editOps = changeSet.operationList();
+ QBS_CHECK(editOps.count() == 1);
+ const ChangeSet::EditOp &insertOp = editOps.first();
+ setLineOffset(lineOffset);
+
+ int insertionLine = fileContent.left(insertOp.pos1).count(QLatin1Char('\n'));
+ for (int i = 0; i < insertOp.text.count() && insertOp.text.at(i) == QLatin1Char('\n'); ++i)
+ ++insertionLine; // To account for newlines prepended by the rewriter.
+ ++insertionLine; // To account for zero-based indexing.
+ setItemPosition(CodeLocation(projectFile(), insertionLine,
+ groupItemIndentation + 1));
+ changeSet.apply(&fileContent);
+}
+
+static QString getNodeRepresentation(const QString &fileContent, const Node *node)
+{
+ const quint32 start = node->firstSourceLocation().offset;
+ const quint32 end = node->lastSourceLocation().end();
+ return fileContent.mid(start, end - start);
+}
+
+static const ChangeSet::EditOp &getEditOp(const ChangeSet &changeSet)
+{
+ const QList<ChangeSet::EditOp> &editOps = changeSet.operationList();
+ QBS_CHECK(editOps.count() == 1);
+ return editOps.first();
+}
+
+static int getLineOffsetForChangedBinding(const ChangeSet &changeSet, const QString &oldRhs)
+{
+ return getEditOp(changeSet).text.count(QLatin1Char('\n')) - oldRhs.count(QLatin1Char('\n'));
+}
+
+static int getBindingLine(const ChangeSet &changeSet, const QString &fileContent)
+{
+ return fileContent.left(getEditOp(changeSet).pos1 + 1).count(QLatin1Char('\n')) + 1;
+}
+
+
+ProjectFileFilesAdder::ProjectFileFilesAdder(const ProductData &product, const GroupData &group,
+ const QStringList &files)
+ : ProjectFileUpdater(product.location().fileName())
+ , m_product(product)
+ , m_group(group)
+ , m_files(files)
+{
+}
+
+void ProjectFileFilesAdder::doApply(QString &fileContent, UiProgram *ast)
+{
+ // Find the item containing the "files" binding.
+ ItemFinder itemFinder(m_group.isValid() ? m_group.location() : m_product.location());
+ ast->accept(&itemFinder);
+ if (!itemFinder.item()) {
+ throw ErrorInfo(Tr::tr("The project file parser failed to find the item."),
+ CodeLocation(projectFile()));
+ }
+
+ const int itemIndentation
+ = itemFinder.item()->qualifiedTypeNameId->firstSourceLocation().startColumn - 1;
+ const int bindingIndentation = itemIndentation + 4;
+ const int arrayElemIndentation = bindingIndentation + 4;
+ QString newFilesString;
+ foreach (const QString &relFilePath, m_files) {
+ newFilesString += QString(arrayElemIndentation, QLatin1Char(' '));
+ newFilesString += QLatin1Char('"');
+ newFilesString += relFilePath;
+ newFilesString += QLatin1Char('"');
+ newFilesString += QLatin1String(",\n");
+ }
+ newFilesString.chop(2); // Trailing comma and newline.
+
+ // Now get the binding itself.
+ FilesBindingFinder bindingFinder(itemFinder.item());
+ itemFinder.item()->accept(&bindingFinder);
+
+ ChangeSet changeSet;
+ Rewriter rewriter(fileContent, &changeSet, QStringList());
+
+ UiScriptBinding * const filesBinding = bindingFinder.binding();
+ if (filesBinding) {
+ if (filesBinding->statement->kind != Node::Kind_ExpressionStatement)
+ throw ErrorInfo(Tr::tr("JavaScript construct in source file is too complex.")); // TODO: rename, add new and concat.
+ const ExpressionStatement * const exprStatement
+ = static_cast<ExpressionStatement *>(filesBinding->statement);
+ switch (exprStatement->expression->kind) {
+ case Node::Kind_ArrayLiteral: {
+ QString filesString = QLatin1String("[\n");
+ const ElementList *elem
+ = static_cast<ArrayLiteral *>(exprStatement->expression)->elements;
+ while (elem) {
+ filesString += QString(arrayElemIndentation, QLatin1Char(' '));
+ filesString += getNodeRepresentation(fileContent, elem->expression);
+ filesString += QLatin1String(",\n");
+ elem = elem->next;
+ }
+ filesString += newFilesString;
+ filesString += QLatin1Char('\n');
+ filesString += QString(bindingIndentation, QLatin1Char(' '));
+ filesString += QLatin1Char(']');
+ rewriter.changeBinding(itemFinder.item()->initializer, QLatin1String("files"),
+ filesString, Rewriter::ScriptBinding);
+ break;
+ }
+ case Node::Kind_StringLiteral: {
+ const QString existingElement
+ = static_cast<StringLiteral *>(exprStatement->expression)->value.toString();
+ QString filesString = QLatin1String("[\n");
+ filesString += QString(arrayElemIndentation, QLatin1Char(' '));
+ filesString += QLatin1Char('"') + existingElement + QLatin1Char('"');
+ filesString += QLatin1String(",\n");
+ filesString += newFilesString;
+ filesString += QLatin1Char('\n');
+ filesString += QString(bindingIndentation, QLatin1Char(' '));
+ filesString += QLatin1Char(']');
+ rewriter.changeBinding(itemFinder.item()->initializer, QLatin1String("files"),
+ filesString, Rewriter::ScriptBinding);
+ break;
+ }
+ default: {
+ // Note that we can often do better than simply concatenating: For instance,
+ // in the case where the existing list is of the form ["a", "b"].concat(myProperty),
+ // we could keep on parsing until we find the array literal and then merge it with
+ // the new files, preventing cascading concat() calls.
+ // But this is not essential and can be implemented when we have some downtime.
+ const QString rhsRepr = getNodeRepresentation(fileContent, exprStatement->expression);
+ QString filesString = QLatin1String("[\n");
+ filesString += newFilesString;
+ filesString += QLatin1Char('\n');
+ filesString += QString(bindingIndentation, QLatin1Char(' '));
+
+ // It cannot be the other way around, since the existing right-hand side could
+ // have string type.
+ filesString += QString::fromLatin1("].concat(%1)").arg(rhsRepr);
+
+ rewriter.changeBinding(itemFinder.item()->initializer, QLatin1String("files"),
+ filesString, Rewriter::ScriptBinding);
+ }
+ }
+ } else { // Can happen for the product itself, for which the "files" binding is not mandatory.
+ newFilesString.prepend(QLatin1String("[\n"));
+ newFilesString += QLatin1Char('\n');
+ newFilesString += QString(bindingIndentation, QLatin1Char(' '));
+ newFilesString += QLatin1Char(']');
+ const QString bindingString = QString(bindingIndentation, QLatin1Char(' '))
+ + QLatin1String("files");
+ rewriter.addBinding(itemFinder.item()->initializer, bindingString, newFilesString,
+ Rewriter::ScriptBinding);
+ }
+
+ setLineOffset(getLineOffsetForChangedBinding(changeSet, getNodeRepresentation(fileContent,
+ filesBinding->statement)));
+ const int insertionLine = getBindingLine(changeSet, fileContent) + 1;
+ const int insertionColumn = (filesBinding ? arrayElemIndentation : bindingIndentation) + 1;
+ setItemPosition(CodeLocation(projectFile(), insertionLine, insertionColumn));
+ changeSet.apply(&fileContent);
+}
+
+ProjectFileFilesRemover::ProjectFileFilesRemover(const ProductData &product, const GroupData &group,
+ const QStringList &files)
+ : ProjectFileUpdater(product.location().fileName())
+ , m_product(product)
+ , m_group(group)
+ , m_files(files)
+{
+}
+
+void ProjectFileFilesRemover::doApply(QString &fileContent, UiProgram *ast)
+{
+ // Find the item containing the "files" binding.
+ ItemFinder itemFinder(m_group.isValid() ? m_group.location() : m_product.location());
+ ast->accept(&itemFinder);
+ if (!itemFinder.item()) {
+ throw ErrorInfo(Tr::tr("The project file parser failed to find the item."),
+ CodeLocation(projectFile()));
+ }
+
+ // Now get the binding itself.
+ FilesBindingFinder bindingFinder(itemFinder.item());
+ itemFinder.item()->accept(&bindingFinder);
+ if (!bindingFinder.binding()) {
+ throw ErrorInfo(Tr::tr("Could not find the 'files' binding in the project file."),
+ m_product.location());
+ }
+
+ if (bindingFinder.binding()->statement->kind != Node::Kind_ExpressionStatement)
+ throw ErrorInfo(Tr::tr("JavaScript construct in source file is too complex."));
+ const CodeLocation bindingLocation
+ = toCodeLocation(projectFile(), bindingFinder.binding()->firstSourceLocation());
+
+ ChangeSet changeSet;
+ Rewriter rewriter(fileContent, &changeSet, QStringList());
+
+ const int itemIndentation
+ = itemFinder.item()->qualifiedTypeNameId->firstSourceLocation().startColumn - 1;
+ const int bindingIndentation = itemIndentation + 4;
+ const int arrayElemIndentation = bindingIndentation + 4;
+
+ const ExpressionStatement * const exprStatement
+ = static_cast<ExpressionStatement *>(bindingFinder.binding()->statement);
+ switch (exprStatement->expression->kind) {
+ case Node::Kind_ArrayLiteral: {
+ QStringList filesToRemove = m_files;
+ QStringList newFilesList;
+ const ElementList *elem = static_cast<ArrayLiteral *>(exprStatement->expression)->elements;
+ while (elem) {
+ if (elem->expression->kind != Node::Kind_StringLiteral) {
+ throw ErrorInfo(Tr::tr("JavaScript construct in source file is too complex."),
+ bindingLocation);
+ }
+ const QString existingFile
+ = static_cast<StringLiteral *>(elem->expression)->value.toString();
+ if (!filesToRemove.removeOne(existingFile))
+ newFilesList << existingFile;
+ elem = elem->next;
+ }
+ if (!filesToRemove.isEmpty()) {
+ throw ErrorInfo(Tr::tr("The following files were not found in the 'files' list: %1")
+ .arg(filesToRemove.join(QLatin1String(", "))), bindingLocation);
+ }
+ QString filesString = QLatin1String("[\n");
+ foreach (const QString &file, newFilesList) {
+ filesString += QString(arrayElemIndentation, QLatin1Char(' '));
+ filesString += QString::fromLocal8Bit("\"%1\",\n").arg(file);
+ }
+ filesString += QString(bindingIndentation, QLatin1Char(' '));
+ filesString += QLatin1Char(']');
+ rewriter.changeBinding(itemFinder.item()->initializer, QLatin1String("files"),
+ filesString, Rewriter::ScriptBinding);
+ break;
+ }
+ case Node::Kind_StringLiteral: {
+ if (m_files.count() != 1) {
+ throw ErrorInfo(Tr::tr("Was requested to remove %1 files, but there is only "
+ "one in the list.").arg(m_files.count()), bindingLocation);
+ }
+ const QString existingFile
+ = static_cast<StringLiteral *>(exprStatement->expression)->value.toString();
+ if (existingFile != m_files.first()) {
+ throw ErrorInfo(Tr::tr("File '1' could not be found in the 'files' list."),
+ bindingLocation);
+ }
+ rewriter.changeBinding(itemFinder.item()->initializer, QLatin1String("files"),
+ QLatin1String("[]"), Rewriter::ScriptBinding);
+ break;
+ }
+ default:
+ throw ErrorInfo(Tr::tr("JavaScript construct in source file is too complex."),
+ bindingLocation);
+ }
+
+ setLineOffset(getLineOffsetForChangedBinding(changeSet,
+ getNodeRepresentation(fileContent, exprStatement->expression)));
+ const int bindingLine = getBindingLine(changeSet, fileContent);
+ const int bindingColumn = (bindingFinder.binding()
+ ? arrayElemIndentation : bindingIndentation) + 1;
+ setItemPosition(CodeLocation(projectFile(), bindingLine, bindingColumn));
+ changeSet.apply(&fileContent);
+}
+
+
+ProjectFileGroupRemover::ProjectFileGroupRemover(const ProductData &product, const GroupData &group)
+ : ProjectFileUpdater(product.location().fileName())
+ , m_product(product)
+ , m_group(group)
+{
+}
+
+void ProjectFileGroupRemover::doApply(QString &fileContent, UiProgram *ast)
+{
+ ItemFinder productFinder(m_product.location());
+ ast->accept(&productFinder);
+ if (!productFinder.item()) {
+ throw ErrorInfo(Tr::tr("The project file parser failed to find the product item."),
+ CodeLocation(projectFile()));
+ }
+
+ ItemFinder groupFinder(m_group.location());
+ productFinder.item()->accept(&groupFinder);
+ if (!groupFinder.item()) {
+ throw ErrorInfo(Tr::tr("The project file parser failed to find the group item."),
+ m_product.location());
+ }
+
+ ChangeSet changeSet;
+ Rewriter rewriter(fileContent, &changeSet, QStringList());
+ rewriter.removeObjectMember(groupFinder.item(), productFinder.item());
+
+ setItemPosition(m_group.location());
+ const QList<ChangeSet::EditOp> &editOps = changeSet.operationList();
+ QBS_CHECK(editOps.count() == 1);
+ const ChangeSet::EditOp &op = editOps.first();
+ const QString removedText = fileContent.mid(op.pos1, op.length1);
+ setLineOffset(-removedText.count(QLatin1Char('\n')));
+
+ changeSet.apply(&fileContent);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/api/projectfileupdater.h b/src/lib/corelib/api/projectfileupdater.h
new file mode 100644
index 000000000..ba125dc41
--- /dev/null
+++ b/src/lib/corelib/api/projectfileupdater.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROJECTFILEUPDATER_H
+#define QBS_PROJECTFILEUPDATER_H
+
+#include "projectdata.h"
+
+#include <tools/error.h>
+#include <tools/codelocation.h>
+
+#include <QStringList>
+
+namespace QbsQmlJS { namespace AST { class UiProgram; } }
+
+namespace qbs {
+namespace Internal {
+
+class ProjectFileUpdater
+{
+public:
+ void apply();
+
+ CodeLocation itemPosition() const { return m_itemPosition; }
+ int lineOffset() const { return m_lineOffset; }
+
+protected:
+ ProjectFileUpdater(const QString &projectFile);
+
+ QString projectFile() const { return m_projectFile; }
+
+ void setLineOffset(int offset) { m_lineOffset = offset; }
+ void setItemPosition(const CodeLocation &cl) { m_itemPosition = cl; }
+
+private:
+ virtual void doApply(QString &fileContent, QbsQmlJS::AST::UiProgram *ast) = 0;
+
+ const QString m_projectFile;
+ CodeLocation m_itemPosition;
+ int m_lineOffset;
+};
+
+
+class ProjectFileGroupInserter : public ProjectFileUpdater
+{
+public:
+ ProjectFileGroupInserter(const ProductData &product, const QString &groupName);
+
+private:
+ void doApply(QString &fileContent, QbsQmlJS::AST::UiProgram *ast);
+
+ const ProductData m_product;
+ const QString m_groupName;
+};
+
+
+class ProjectFileFilesAdder : public ProjectFileUpdater
+{
+public:
+ ProjectFileFilesAdder(const ProductData &product, const GroupData &group,
+ const QStringList &files);
+
+private:
+ void doApply(QString &fileContent, QbsQmlJS::AST::UiProgram *ast);
+
+ const ProductData m_product;
+ const GroupData m_group;
+ const QStringList m_files;
+};
+
+class ProjectFileFilesRemover : public ProjectFileUpdater
+{
+public:
+ ProjectFileFilesRemover(const ProductData &product, const GroupData &group,
+ const QStringList &files);
+
+private:
+ void doApply(QString &fileContent, QbsQmlJS::AST::UiProgram *ast);
+
+ const ProductData m_product;
+ const GroupData m_group;
+ const QStringList m_files;
+};
+
+class ProjectFileGroupRemover : public ProjectFileUpdater
+{
+public:
+ ProjectFileGroupRemover(const ProductData &product, const GroupData &group);
+
+private:
+ void doApply(QString &fileContent, QbsQmlJS::AST::UiProgram *ast);
+
+ const ProductData m_product;
+ const GroupData m_group;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+
+#endif // Include guard.
diff --git a/src/lib/corelib/api/propertymap_p.h b/src/lib/corelib/api/propertymap_p.h
new file mode 100644
index 000000000..e57e323cc
--- /dev/null
+++ b/src/lib/corelib/api/propertymap_p.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_PROPERTYMAP_P_H
+#define QBS_PROPERTYMAP_P_H
+
+#include <language/language.h>
+
+namespace qbs {
+namespace Internal {
+
+class PropertyMapPrivate
+{
+public:
+ PropertyMapPtr m_map;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PROPERTYMAP_P_H
diff --git a/src/lib/corelib/api/qmljsrewriter.cpp b/src/lib/corelib/api/qmljsrewriter.cpp
new file mode 100644
index 000000000..ecb59e778
--- /dev/null
+++ b/src/lib/corelib/api/qmljsrewriter.cpp
@@ -0,0 +1,718 @@
+/****************************************************************************
+**
+** 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 "qmljsrewriter.h"
+
+#include <parser/qmljsast_p.h>
+
+#include <QTextBlock>
+#include <QTextCursor>
+#include <QTextDocument>
+
+namespace QbsQmlJS {
+using namespace AST;
+
+static QString toString(UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Char('.'))
+{
+ QString result;
+
+ for (UiQualifiedId *iter = qualifiedId; iter; iter = iter->next) {
+ if (iter != qualifiedId)
+ result += delimiter;
+
+ result += iter->name;
+ }
+
+ return result;
+}
+
+
+Rewriter::Rewriter(const QString &originalText,
+ ChangeSet *changeSet,
+ const QStringList &propertyOrder)
+ : m_originalText(originalText)
+ , m_changeSet(changeSet)
+ , m_propertyOrder(propertyOrder)
+{
+ Q_ASSERT(changeSet);
+}
+
+Rewriter::Range Rewriter::addBinding(AST::UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &propertyValue,
+ BindingType bindingType)
+{
+ UiObjectMemberList *insertAfter = searchMemberToInsertAfter(ast->members,
+ propertyName,
+ m_propertyOrder);
+ return addBinding(ast, propertyName, propertyValue, bindingType, insertAfter);
+}
+
+Rewriter::Range Rewriter::addBinding(AST::UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &propertyValue,
+ BindingType bindingType,
+ UiObjectMemberList *insertAfter)
+{
+ SourceLocation endOfPreviousMember;
+ SourceLocation startOfNextMember;
+
+ if (insertAfter == 0 || insertAfter->member == 0) {
+ // insert as first member
+ endOfPreviousMember = ast->lbraceToken;
+
+ if (ast->members && ast->members->member)
+ startOfNextMember = ast->members->member->firstSourceLocation();
+ else
+ startOfNextMember = ast->rbraceToken;
+ } else {
+ endOfPreviousMember = insertAfter->member->lastSourceLocation();
+
+ if (insertAfter->next && insertAfter->next->member)
+ startOfNextMember = insertAfter->next->member->firstSourceLocation();
+ else
+ startOfNextMember = ast->rbraceToken;
+ }
+ const bool isOneLiner = endOfPreviousMember.startLine == startOfNextMember.startLine;
+ bool needsPreceedingSemicolon = false;
+ bool needsTrailingSemicolon = false;
+
+ if (isOneLiner) {
+ if (insertAfter == 0) { // we're inserting after an lbrace
+ if (ast->members) { // we're inserting before a member (and not the rbrace)
+ needsTrailingSemicolon = bindingType == ScriptBinding;
+ }
+ } else { // we're inserting after a member, not after the lbrace
+ if (endOfPreviousMember.isValid()) { // there already is a semicolon after the previous member
+ if (insertAfter->next && insertAfter->next->member) { // and the after us there is a member, not an rbrace, so:
+ needsTrailingSemicolon = bindingType == ScriptBinding;
+ }
+ } else { // there is no semicolon after the previous member (probably because there is an rbrace after us/it, so:
+ needsPreceedingSemicolon = true;
+ }
+ }
+ }
+
+ QString newPropertyTemplate;
+ switch (bindingType) {
+ case ArrayBinding:
+ newPropertyTemplate = QLatin1String("%1: [\n%2\n]");
+ break;
+
+ case ObjectBinding:
+ newPropertyTemplate = QLatin1String("%1: %2");
+ break;
+
+ case ScriptBinding:
+ newPropertyTemplate = QLatin1String("%1: %2");
+ break;
+
+ default:
+ Q_ASSERT(!"unknown property type");
+ }
+
+ if (isOneLiner) {
+ if (needsPreceedingSemicolon)
+ newPropertyTemplate.prepend(QLatin1Char(';'));
+ newPropertyTemplate.prepend(QLatin1Char(' '));
+ if (needsTrailingSemicolon)
+ newPropertyTemplate.append(QLatin1Char(';'));
+ } else {
+ newPropertyTemplate.prepend(QLatin1Char('\n'));
+ }
+
+ m_changeSet->insert(endOfPreviousMember.end(),
+ newPropertyTemplate.arg(propertyName, propertyValue));
+
+ return Range(endOfPreviousMember.end(), endOfPreviousMember.end());
+}
+
+UiObjectMemberList *Rewriter::searchMemberToInsertAfter(UiObjectMemberList *members,
+ const QStringList &propertyOrder)
+{
+ const int objectDefinitionInsertionPoint = propertyOrder.indexOf(QString());
+
+ UiObjectMemberList *lastObjectDef = 0;
+ UiObjectMemberList *lastNonObjectDef = 0;
+
+ for (UiObjectMemberList *iter = members; iter; iter = iter->next) {
+ UiObjectMember *member = iter->member;
+ int idx = -1;
+
+ if (cast<UiObjectDefinition*>(member))
+ lastObjectDef = iter;
+ else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ idx = propertyOrder.indexOf(toString(arrayBinding->qualifiedId));
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ idx = propertyOrder.indexOf(toString(objectBinding->qualifiedId));
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ idx = propertyOrder.indexOf(toString(scriptBinding->qualifiedId));
+ else if (cast<UiPublicMember*>(member))
+ idx = propertyOrder.indexOf(QLatin1String("property"));
+
+ if (idx < objectDefinitionInsertionPoint)
+ lastNonObjectDef = iter;
+ }
+
+ if (lastObjectDef)
+ return lastObjectDef;
+ else
+ return lastNonObjectDef;
+}
+
+UiArrayMemberList *Rewriter::searchMemberToInsertAfter(UiArrayMemberList *members,
+ const QStringList &propertyOrder)
+{
+ const int objectDefinitionInsertionPoint = propertyOrder.indexOf(QString());
+
+ UiArrayMemberList *lastObjectDef = 0;
+ UiArrayMemberList *lastNonObjectDef = 0;
+
+ for (UiArrayMemberList *iter = members; iter; iter = iter->next) {
+ UiObjectMember *member = iter->member;
+ int idx = -1;
+
+ if (cast<UiObjectDefinition*>(member))
+ lastObjectDef = iter;
+ else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ idx = propertyOrder.indexOf(toString(arrayBinding->qualifiedId));
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ idx = propertyOrder.indexOf(toString(objectBinding->qualifiedId));
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ idx = propertyOrder.indexOf(toString(scriptBinding->qualifiedId));
+ else if (cast<UiPublicMember*>(member))
+ idx = propertyOrder.indexOf(QLatin1String("property"));
+
+ if (idx < objectDefinitionInsertionPoint)
+ lastNonObjectDef = iter;
+ }
+
+ if (lastObjectDef)
+ return lastObjectDef;
+ else
+ return lastNonObjectDef;
+}
+
+UiObjectMemberList *Rewriter::searchMemberToInsertAfter(UiObjectMemberList *members,
+ const QString &propertyName,
+ const QStringList &propertyOrder)
+{
+ if (!members)
+ return 0; // empty members
+
+ QHash<QString, UiObjectMemberList *> orderedMembers;
+
+ for (UiObjectMemberList *iter = members; iter; iter = iter->next) {
+ UiObjectMember *member = iter->member;
+
+ if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ orderedMembers[toString(arrayBinding->qualifiedId)] = iter;
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ orderedMembers[toString(objectBinding->qualifiedId)] = iter;
+ else if (cast<UiObjectDefinition*>(member))
+ orderedMembers[QString::null] = iter;
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ orderedMembers[toString(scriptBinding->qualifiedId)] = iter;
+ else if (cast<UiPublicMember*>(member))
+ orderedMembers[QLatin1String("property")] = iter;
+ }
+
+ int idx = propertyOrder.indexOf(propertyName);
+ if (idx == -1)
+ idx = propertyOrder.indexOf(QString());
+ if (idx == -1)
+ idx = propertyOrder.size() - 1;
+
+ for (; idx > 0; --idx) {
+ const QString prop = propertyOrder.at(idx - 1);
+ UiObjectMemberList *candidate = orderedMembers.value(prop, 0);
+ if (candidate != 0)
+ return candidate;
+ }
+
+ return 0;
+}
+
+void Rewriter::changeBinding(UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &newValue,
+ BindingType binding)
+{
+ QString prefix, suffix;
+ int dotIdx = propertyName.indexOf(QLatin1Char('.'));
+ if (dotIdx != -1) {
+ prefix = propertyName.left(dotIdx);
+ suffix = propertyName.mid(dotIdx + 1);
+ }
+
+ for (UiObjectMemberList *members = ast->members; members; members = members->next) {
+ UiObjectMember *member = members->member;
+
+ // for non-grouped properties:
+ if (isMatchingPropertyMember(propertyName, member)) {
+ switch (binding) {
+ case ArrayBinding:
+ insertIntoArray(cast<UiArrayBinding*>(member), newValue);
+ break;
+
+ case ObjectBinding:
+ replaceMemberValue(member, newValue, false);
+ break;
+
+ case ScriptBinding:
+ replaceMemberValue(member, newValue, nextMemberOnSameLine(members));
+ break;
+
+ default:
+ Q_ASSERT(!"Unhandled QmlRefactoring::PropertyType");
+ }
+
+ break;
+ // for grouped properties:
+ } else if (!prefix.isEmpty()) {
+ if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
+ if (toString(def->qualifiedTypeNameId) == prefix)
+ changeBinding(def->initializer, suffix, newValue, binding);
+ }
+ }
+ }
+}
+
+void Rewriter::replaceMemberValue(UiObjectMember *propertyMember,
+ const QString &newValue,
+ bool needsSemicolon)
+{
+ QString replacement = newValue;
+ int startOffset = -1;
+ int endOffset = -1;
+ if (UiObjectBinding *objectBinding = AST::cast<UiObjectBinding *>(propertyMember)) {
+ startOffset = objectBinding->qualifiedTypeNameId->identifierToken.offset;
+ endOffset = objectBinding->initializer->rbraceToken.end();
+ } else if (UiScriptBinding *scriptBinding = AST::cast<UiScriptBinding *>(propertyMember)) {
+ startOffset = scriptBinding->statement->firstSourceLocation().offset;
+ endOffset = scriptBinding->statement->lastSourceLocation().end();
+ } else if (UiArrayBinding *arrayBinding = AST::cast<UiArrayBinding *>(propertyMember)) {
+ startOffset = arrayBinding->lbracketToken.offset;
+ endOffset = arrayBinding->rbracketToken.end();
+ } else if (UiPublicMember *publicMember = AST::cast<UiPublicMember*>(propertyMember)) {
+ if (publicMember->statement) {
+ startOffset = publicMember->statement->firstSourceLocation().offset;
+ if (publicMember->semicolonToken.isValid())
+ endOffset = publicMember->semicolonToken.end();
+ else
+ endOffset = publicMember->statement->lastSourceLocation().offset;
+ } else {
+ startOffset = publicMember->lastSourceLocation().end();
+ endOffset = startOffset;
+ if (publicMember->semicolonToken.isValid())
+ startOffset = publicMember->semicolonToken.offset;
+ replacement.prepend(QLatin1String(": "));
+ }
+ } else {
+ return;
+ }
+
+ if (needsSemicolon)
+ replacement += QLatin1Char(';');
+
+ m_changeSet->replace(startOffset, endOffset, replacement);
+}
+
+bool Rewriter::isMatchingPropertyMember(const QString &propertyName,
+ UiObjectMember *member)
+{
+ if (UiPublicMember *publicMember = cast<UiPublicMember*>(member))
+ return publicMember->name == propertyName;
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ return toString(objectBinding->qualifiedId) == propertyName;
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ return toString(scriptBinding->qualifiedId) == propertyName;
+ else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ return toString(arrayBinding->qualifiedId) == propertyName;
+ else
+ return false;
+}
+
+bool Rewriter::nextMemberOnSameLine(UiObjectMemberList *members)
+{
+ if (members && members->next && members->next->member)
+ return members->next->member->firstSourceLocation().startLine == members->member->lastSourceLocation().startLine;
+ else
+ return false;
+}
+
+void Rewriter::insertIntoArray(UiArrayBinding *ast, const QString &newValue)
+{
+ if (!ast)
+ return;
+
+ UiObjectMember *lastMember = 0;
+ for (UiArrayMemberList *iter = ast->members; iter; iter = iter->next) {
+ lastMember = iter->member;
+ }
+
+ if (!lastMember)
+ return;
+
+ const int insertionPoint = lastMember->lastSourceLocation().end();
+ m_changeSet->insert(insertionPoint, QLatin1String(",\n") + newValue);
+}
+
+void Rewriter::removeBindingByName(UiObjectInitializer *ast, const QString &propertyName)
+{
+ QString prefix;
+ int dotIdx = propertyName.indexOf(QLatin1Char('.'));
+ if (dotIdx != -1)
+ prefix = propertyName.left(dotIdx);
+
+ for (UiObjectMemberList *it = ast->members; it; it = it->next) {
+ UiObjectMember *member = it->member;
+
+ // run full name match (for ungrouped properties):
+ if (isMatchingPropertyMember(propertyName, member)) {
+ removeMember(member);
+ // check for grouped properties:
+ } else if (!prefix.isEmpty()) {
+ if (UiObjectDefinition *def = cast<UiObjectDefinition *>(member)) {
+ if (toString(def->qualifiedTypeNameId) == prefix)
+ removeGroupedProperty(def, propertyName);
+ }
+ }
+ }
+}
+
+void Rewriter::removeGroupedProperty(UiObjectDefinition *ast,
+ const QString &propertyName)
+{
+ int dotIdx = propertyName.indexOf(QLatin1Char('.'));
+ if (dotIdx == -1)
+ return;
+
+ const QString propName = propertyName.mid(dotIdx + 1);
+
+ UiObjectMember *wanted = 0;
+ unsigned memberCount = 0;
+ for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
+ ++memberCount;
+ UiObjectMember *member = it->member;
+
+ if (!wanted && isMatchingPropertyMember(propName, member))
+ wanted = member;
+ }
+
+ if (!wanted)
+ return;
+ if (memberCount == 1)
+ removeMember(ast);
+ else
+ removeMember(wanted);
+}
+
+void Rewriter::removeMember(UiObjectMember *member)
+{
+ int start = member->firstSourceLocation().offset;
+ int end = member->lastSourceLocation().end();
+
+ includeSurroundingWhitespace(m_originalText, start, end);
+
+ m_changeSet->remove(start, end);
+}
+
+bool Rewriter::includeSurroundingWhitespace(const QString &source, int &start, int &end)
+{
+ bool includeStartingWhitespace = true;
+ bool paragraphFound = false;
+ bool paragraphSkipped = false;
+
+ if (end >= 0) {
+ QChar c = source.at(end);
+
+ while (c.isSpace()) {
+ ++end;
+ if (c.unicode() == 10) {
+ paragraphFound = true;
+ paragraphSkipped = true;
+ break;
+ } else if (end == source.length()) {
+ break;
+ }
+
+ c = source.at(end);
+ }
+
+ includeStartingWhitespace = paragraphFound;
+ }
+
+ paragraphFound = false;
+ if (includeStartingWhitespace) {
+ while (start > 0) {
+ const QChar c = source.at(start - 1);
+
+ if (c.unicode() == 10) {
+ paragraphFound = true;
+ break;
+ }
+ if (!c.isSpace())
+ break;
+
+ --start;
+ }
+ }
+ if (!paragraphFound && paragraphSkipped) //keep the line ending
+ --end;
+
+ return paragraphFound;
+}
+
+void Rewriter::includeLeadingEmptyLine(const QString &source, int &start)
+{
+ QTextDocument doc(source);
+
+ if (start == 0)
+ return;
+
+ if (doc.characterAt(start - 1) != QChar::ParagraphSeparator)
+ return;
+
+ QTextCursor tc(&doc);
+ tc.setPosition(start);
+ const int blockNr = tc.blockNumber();
+ if (blockNr == 0)
+ return;
+
+ const QTextBlock prevBlock = tc.block().previous();
+ const QString trimmedPrevBlockText = prevBlock.text().trimmed();
+ if (!trimmedPrevBlockText.isEmpty())
+ return;
+
+ start = prevBlock.position();
+}
+
+void Rewriter::includeEmptyGroupedProperty(UiObjectDefinition *groupedProperty, UiObjectMember *memberToBeRemoved, int &start, int &end)
+{
+ if (groupedProperty->qualifiedTypeNameId && !groupedProperty->qualifiedTypeNameId->name.isEmpty()
+ && groupedProperty->qualifiedTypeNameId->name.at(0).isLower()) {
+ // grouped property
+ UiObjectMemberList *memberIter = groupedProperty->initializer->members;
+ while (memberIter) {
+ if (memberIter->member != memberToBeRemoved)
+ return;
+ memberIter = memberIter->next;
+ }
+ start = groupedProperty->firstSourceLocation().begin();
+ end = groupedProperty->lastSourceLocation().end();
+ }
+}
+
+#if 0
+UiObjectMemberList *QMLRewriter::searchMemberToInsertAfter(UiObjectMemberList *members, const QStringList &propertyOrder)
+{
+ const int objectDefinitionInsertionPoint = propertyOrder.indexOf(QString::null);
+
+ UiObjectMemberList *lastObjectDef = 0;
+ UiObjectMemberList *lastNonObjectDef = 0;
+
+ for (UiObjectMemberList *iter = members; iter; iter = iter->next) {
+ UiObjectMember *member = iter->member;
+ int idx = -1;
+
+ if (cast<UiObjectDefinition*>(member))
+ lastObjectDef = iter;
+ else if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ idx = propertyOrder.indexOf(toString(arrayBinding->qualifiedId));
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ idx = propertyOrder.indexOf(toString(objectBinding->qualifiedId));
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ idx = propertyOrder.indexOf(toString(scriptBinding->qualifiedId));
+ else if (cast<UiPublicMember*>(member))
+ idx = propertyOrder.indexOf(QLatin1String("property"));
+
+ if (idx < objectDefinitionInsertionPoint)
+ lastNonObjectDef = iter;
+ }
+
+ if (lastObjectDef)
+ return lastObjectDef;
+ else
+ return lastNonObjectDef;
+}
+
+UiObjectMemberList *QMLRewriter::searchMemberToInsertAfter(UiObjectMemberList *members, const QString &propertyName, const QStringList &propertyOrder)
+{
+ if (!members)
+ return 0; // empty members
+
+ QHash<QString, UiObjectMemberList *> orderedMembers;
+
+ for (UiObjectMemberList *iter = members; iter; iter = iter->next) {
+ UiObjectMember *member = iter->member;
+
+ if (UiArrayBinding *arrayBinding = cast<UiArrayBinding*>(member))
+ orderedMembers[toString(arrayBinding->qualifiedId)] = iter;
+ else if (UiObjectBinding *objectBinding = cast<UiObjectBinding*>(member))
+ orderedMembers[toString(objectBinding->qualifiedId)] = iter;
+ else if (cast<UiObjectDefinition*>(member))
+ orderedMembers[QString::null] = iter;
+ else if (UiScriptBinding *scriptBinding = cast<UiScriptBinding*>(member))
+ orderedMembers[toString(scriptBinding->qualifiedId)] = iter;
+ else if (cast<UiPublicMember*>(member))
+ orderedMembers[QLatin1String("property")] = iter;
+ }
+
+ int idx = propertyOrder.indexOf(propertyName);
+ if (idx == -1)
+ idx = propertyOrder.indexOf(QString());
+ if (idx == -1)
+ idx = propertyOrder.size() - 1;
+
+ for (; idx > 0; --idx) {
+ const QString prop = propertyOrder.at(idx - 1);
+ UiObjectMemberList *candidate = orderedMembers.value(prop, 0);
+ if (candidate != 0)
+ return candidate;
+ }
+
+ return 0;
+}
+
+#endif
+
+void Rewriter::appendToArrayBinding(UiArrayBinding *arrayBinding,
+ const QString &content)
+{
+ UiObjectMember *lastMember = 0;
+ for (UiArrayMemberList *iter = arrayBinding->members; iter; iter = iter->next)
+ if (iter->member)
+ lastMember = iter->member;
+
+ if (!lastMember)
+ return; // an array binding cannot be empty, so there will (or should) always be a last member.
+
+ const int insertionPoint = lastMember->lastSourceLocation().end();
+
+ m_changeSet->insert(insertionPoint, QLatin1String(",\n") + content);
+}
+
+Rewriter::Range Rewriter::addObject(UiObjectInitializer *ast, const QString &content)
+{
+ UiObjectMemberList *insertAfter = searchMemberToInsertAfter(ast->members, m_propertyOrder);
+ return addObject(ast, content, insertAfter);
+}
+
+Rewriter::Range Rewriter::addObject(UiObjectInitializer *ast, const QString &content, UiObjectMemberList *insertAfter)
+{
+ int insertionPoint;
+ QString textToInsert;
+ if (insertAfter && insertAfter->member) {
+ insertionPoint = insertAfter->member->lastSourceLocation().end();
+ textToInsert += QLatin1String("\n");
+ } else {
+ insertionPoint = ast->lbraceToken.end();
+ }
+
+ textToInsert += content;
+ m_changeSet->insert(insertionPoint, QLatin1String("\n") + textToInsert);
+
+ return Range(insertionPoint, insertionPoint);
+}
+
+Rewriter::Range Rewriter::addObject(UiArrayBinding *ast, const QString &content)
+{
+ UiArrayMemberList *insertAfter = searchMemberToInsertAfter(ast->members, m_propertyOrder);
+ return addObject(ast, content, insertAfter);
+}
+
+Rewriter::Range Rewriter::addObject(UiArrayBinding *ast, const QString &content, UiArrayMemberList *insertAfter)
+{
+ int insertionPoint;
+ QString textToInsert;
+ if (insertAfter && insertAfter->member) {
+ insertionPoint = insertAfter->member->lastSourceLocation().end();
+ textToInsert = QLatin1String(",\n") + content;
+ } else {
+ insertionPoint = ast->lbracketToken.end();
+ textToInsert += QLatin1String("\n") + content + QLatin1Char(',');
+ }
+
+ m_changeSet->insert(insertionPoint, textToInsert);
+
+ return Range(insertionPoint, insertionPoint);
+}
+
+void Rewriter::removeObjectMember(UiObjectMember *member, UiObjectMember *parent)
+{
+ int start = member->firstSourceLocation().offset;
+ int end = member->lastSourceLocation().end();
+
+ if (UiArrayBinding *parentArray = cast<UiArrayBinding *>(parent)) {
+ extendToLeadingOrTrailingComma(parentArray, member, start, end);
+ } else {
+ if (UiObjectDefinition *parentObjectDefinition = cast<UiObjectDefinition *>(parent))
+ includeEmptyGroupedProperty(parentObjectDefinition, member, start, end);
+ includeSurroundingWhitespace(m_originalText, start, end);
+ }
+
+ includeLeadingEmptyLine(m_originalText, start);
+ m_changeSet->remove(start, end);
+}
+
+void Rewriter::extendToLeadingOrTrailingComma(UiArrayBinding *parentArray,
+ UiObjectMember *member,
+ int &start,
+ int &end) const
+{
+ UiArrayMemberList *currentMember = 0;
+ for (UiArrayMemberList *it = parentArray->members; it; it = it->next) {
+ if (it->member == member) {
+ currentMember = it;
+ break;
+ }
+ }
+
+ if (!currentMember)
+ return;
+
+ if (currentMember->commaToken.isValid()) {
+ // leading comma
+ start = currentMember->commaToken.offset;
+ if (includeSurroundingWhitespace(m_originalText, start, end))
+ --end;
+ } else if (currentMember->next && currentMember->next->commaToken.isValid()) {
+ // trailing comma
+ end = currentMember->next->commaToken.end();
+ includeSurroundingWhitespace(m_originalText, start, end);
+ } else {
+ // array with 1 element, so remove the complete binding
+ start = parentArray->firstSourceLocation().offset;
+ end = parentArray->lastSourceLocation().end();
+ includeSurroundingWhitespace(m_originalText, start, end);
+ }
+}
+
+} // namespace QbsQmlJS
diff --git a/src/lib/corelib/api/qmljsrewriter.h b/src/lib/corelib/api/qmljsrewriter.h
new file mode 100644
index 000000000..09a638ab0
--- /dev/null
+++ b/src/lib/corelib/api/qmljsrewriter.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QMLJSREWRITER_H
+#define QMLJSREWRITER_H
+
+#include "changeset.h"
+
+#include <parser/qmljsastfwd_p.h>
+
+#include <QStringList>
+
+namespace QbsQmlJS {
+
+class Rewriter
+{
+public:
+ enum BindingType {
+ ScriptBinding,
+ ObjectBinding,
+ ArrayBinding
+ };
+
+ typedef ChangeSet::Range Range;
+
+public:
+ Rewriter(const QString &originalText,
+ ChangeSet *changeSet,
+ const QStringList &propertyOrder);
+
+ Range addBinding(AST::UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &propertyValue,
+ BindingType bindingType);
+
+ Range addBinding(AST::UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &propertyValue,
+ BindingType bindingType,
+ AST::UiObjectMemberList *insertAfter);
+
+ void changeBinding(AST::UiObjectInitializer *ast,
+ const QString &propertyName,
+ const QString &newValue,
+ BindingType binding);
+
+ void removeBindingByName(AST::UiObjectInitializer *ast, const QString &propertyName);
+
+ void appendToArrayBinding(AST::UiArrayBinding *arrayBinding,
+ const QString &content);
+
+ Range addObject(AST::UiObjectInitializer *ast, const QString &content);
+ Range addObject(AST::UiObjectInitializer *ast, const QString &content, AST::UiObjectMemberList *insertAfter);
+ Range addObject(AST::UiArrayBinding *ast, const QString &content);
+ Range addObject(AST::UiArrayBinding *ast, const QString &content, AST::UiArrayMemberList *insertAfter);
+
+ void removeObjectMember(AST::UiObjectMember *member, AST::UiObjectMember *parent);
+
+ static AST::UiObjectMemberList *searchMemberToInsertAfter(AST::UiObjectMemberList *members, const QStringList &propertyOrder);
+ static AST::UiArrayMemberList *searchMemberToInsertAfter(AST::UiArrayMemberList *members, const QStringList &propertyOrder);
+ static AST::UiObjectMemberList *searchMemberToInsertAfter(AST::UiObjectMemberList *members, const QString &propertyName, const QStringList &propertyOrder);
+
+ static bool includeSurroundingWhitespace(const QString &source, int &start, int &end);
+ static void includeLeadingEmptyLine(const QString &source, int &start);
+ static void includeEmptyGroupedProperty(AST::UiObjectDefinition *groupedProperty, AST::UiObjectMember *memberToBeRemoved, int &start, int &end);
+
+private:
+ void replaceMemberValue(AST::UiObjectMember *propertyMember,
+ const QString &newValue,
+ bool needsSemicolon);
+ static bool isMatchingPropertyMember(const QString &propertyName,
+ AST::UiObjectMember *member);
+ static bool nextMemberOnSameLine(AST::UiObjectMemberList *members);
+
+ void insertIntoArray(AST::UiArrayBinding* ast, const QString &newValue);
+
+ void removeMember(AST::UiObjectMember *member);
+ void removeGroupedProperty(AST::UiObjectDefinition *ast,
+ const QString &propertyName);
+
+ void extendToLeadingOrTrailingComma(AST::UiArrayBinding *parentArray,
+ AST::UiObjectMember *member,
+ int &start,
+ int &end) const;
+
+private:
+ QString m_originalText;
+ ChangeSet *m_changeSet;
+ const QStringList m_propertyOrder;
+};
+
+} // namespace QbsQmlJS
+
+#endif // QMLJSREWRITER_H
diff --git a/src/lib/corelib/api/runenvironment.cpp b/src/lib/corelib/api/runenvironment.cpp
new file mode 100644
index 000000000..43b5c5605
--- /dev/null
+++ b/src/lib/corelib/api/runenvironment.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** 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 "runenvironment.h"
+
+#include <api/projectdata.h>
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/hostosinfo.h>
+#include <tools/preferences.h>
+#include <tools/propertyfinder.h>
+
+#include <QDir>
+#include <QProcess>
+#include <QProcessEnvironment>
+#include <QScopedPointer>
+#include <QTemporaryFile>
+#include <QVariantMap>
+
+#include <stdlib.h>
+
+namespace qbs {
+using namespace Internal;
+
+class RunEnvironment::RunEnvironmentPrivate
+{
+public:
+ RunEnvironmentPrivate(const ResolvedProductPtr &product,
+ const QProcessEnvironment &environment, Settings *settings, const Logger &logger)
+ : engine(logger)
+ , resolvedProduct(product)
+ , environment(environment)
+ , settings(settings)
+ , logger(logger)
+ {
+ }
+
+ ScriptEngine engine;
+ const ResolvedProductPtr resolvedProduct;
+ const QProcessEnvironment environment;
+ Settings * const settings;
+ Logger logger;
+};
+
+RunEnvironment::RunEnvironment(const ResolvedProductPtr &product,
+ const QProcessEnvironment &environment, Settings *settings, const Logger &logger)
+ : d(new RunEnvironmentPrivate(product, environment, settings, logger))
+{
+}
+
+RunEnvironment::~RunEnvironment()
+{
+ delete d;
+}
+
+int RunEnvironment::runShell()
+{
+ d->resolvedProduct->setupBuildEnvironment(&d->engine, d->environment);
+
+ const QString productId = d->resolvedProduct->name;
+ d->logger.qbsInfo() << Tr::tr("Starting shell for target '%1'.").arg(productId);
+ const QProcessEnvironment environment = d->resolvedProduct->buildEnvironment;
+#if defined(Q_OS_LINUX)
+ clearenv();
+#endif
+ foreach (const QString &key, environment.keys())
+ qputenv(key.toLocal8Bit().constData(), environment.value(key).toLocal8Bit());
+ QString command;
+ QScopedPointer<QTemporaryFile> envFile;
+ if (HostOsInfo::isWindowsHost()) {
+ command = environment.value(QLatin1String("COMSPEC"));
+ if (command.isEmpty())
+ command = QLatin1String("cmd");
+ const QString prompt = environment.value(QLatin1String("PROMPT"));
+ command += QLatin1String(" /k prompt [qbs] ") + prompt;
+ } else {
+ const QVariantMap qbsProps = d->resolvedProduct->topLevelProject()->buildConfiguration()
+ .value(QLatin1String("qbs")).toMap();
+ const QString profileName = qbsProps.value(QLatin1String("profile")).toString();
+ command = Preferences(d->settings, profileName).shell();
+ if (command.isEmpty())
+ command = environment.value(QLatin1String("SHELL"), QLatin1String("/bin/sh"));
+
+ // Yes, we have to use this prcoedure. PS1 is not inherited from the environment.
+ const QString prompt = QLatin1String("qbs ") + productId + QLatin1String(" $ ");
+ envFile.reset(new QTemporaryFile);
+ if (envFile->open()) {
+ if (command.endsWith(QLatin1String("bash")))
+ command += " --posix"; // Teach bash some manners.
+ const QString promptLine = QLatin1String("PS1='") + prompt + QLatin1String("'\n");
+ envFile->write(promptLine.toLocal8Bit());
+ envFile->close();
+ qputenv("ENV", envFile->fileName().toLocal8Bit());
+ } else {
+ d->logger.qbsWarning() << Tr::tr("Setting custom shell prompt failed.");
+ }
+ }
+
+ // We cannot use QProcess, since it does not do stdin forwarding.
+ return system(command.toLocal8Bit().constData());
+}
+
+int RunEnvironment::runTarget(const QString &targetBin, const QStringList &arguments)
+{
+ const QStringList targetOS = PropertyFinder().propertyValue(
+ d->resolvedProduct->properties->value(),
+ QLatin1String("qbs"),
+ QLatin1String("targetOS")).toStringList();
+
+ QString targetExecutable = targetBin;
+ QStringList targetArguments = arguments;
+ const QString completeSuffix = QFileInfo(targetBin).completeSuffix();
+
+ if (targetOS.contains(QLatin1String("windows"))) {
+ if (completeSuffix == QLatin1String("msi")) {
+ targetExecutable = QLatin1String("msiexec");
+ targetArguments.prepend(QDir::toNativeSeparators(targetBin));
+ targetArguments.prepend(QLatin1String("/package"));
+ }
+
+ // Run Windows executables through Wine when not on Windows
+ if (!HostOsInfo::isWindowsHost()) {
+ targetArguments.prepend(targetExecutable);
+ targetExecutable = QLatin1String("wine");
+ }
+ }
+
+ // 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()) {
+ d->logger.qbsLog(LoggerError) << Tr::tr("File '%1' is not an executable.")
+ .arg(QDir::toNativeSeparators(targetExecutable));
+ return EXIT_FAILURE;
+ }
+
+ d->resolvedProduct->setupRunEnvironment(&d->engine, d->environment);
+
+ d->logger.qbsInfo() << Tr::tr("Starting target '%1'.").arg(QDir::toNativeSeparators(targetBin));
+ QProcess process;
+ process.setProcessEnvironment(d->resolvedProduct->runEnvironment);
+ process.setProcessChannelMode(QProcess::ForwardedChannels);
+ process.start(targetExecutable, targetArguments);
+ process.waitForFinished(-1);
+ return process.exitCode();
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/api/runenvironment.h b/src/lib/corelib/api/runenvironment.h
new file mode 100644
index 000000000..18ca65aae
--- /dev/null
+++ b/src/lib/corelib/api/runenvironment.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_RUNENVIRONMENT_H
+#define QBS_RUNENVIRONMENT_H
+
+#include <language/forward_decls.h>
+#include <tools/qbs_export.h>
+
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+class QProcessEnvironment;
+QT_END_NAMESPACE
+
+namespace qbs {
+class Settings;
+
+namespace Internal {
+class Logger;
+class ResolvedProduct;
+} // namespace Internal
+
+class QBS_EXPORT RunEnvironment
+{
+ friend class Project;
+public:
+ ~RunEnvironment();
+
+ // These can throw an Error
+ int runShell();
+ int runTarget(const QString &targetBin, const QStringList &arguments);
+
+private:
+ RunEnvironment(const Internal::ResolvedProductPtr &product,
+ const QProcessEnvironment &environment, Settings *settings,
+ const Internal::Logger &logger);
+
+ class RunEnvironmentPrivate;
+ RunEnvironmentPrivate * const d;
+};
+
+} // namespace qbs
+
+#endif // QBS_RUNENVIRONMENT_H
diff --git a/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp b/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp
new file mode 100644
index 000000000..fa419a734
--- /dev/null
+++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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 "abstractcommandexecutor.h"
+
+#include "command.h"
+
+#include <logging/translator.h>
+#include <tools/error.h>
+
+namespace qbs {
+namespace Internal {
+
+AbstractCommandExecutor::AbstractCommandExecutor(const Logger &logger, QObject *parent)
+ : QObject(parent)
+ , m_command(0)
+ , m_transformer(0)
+ , m_mainThreadScriptEngine(0)
+ , m_dryRun(false)
+ , m_logger(logger)
+{
+}
+
+void AbstractCommandExecutor::start(Transformer *transformer, const AbstractCommand *cmd)
+{
+ m_transformer = transformer;
+ m_command = cmd;
+ if (!m_command->isSilent()) {
+ if (m_command->description().isEmpty()) {
+ m_logger.printWarning(ErrorInfo(Tr::tr("Command is not marked silent, but has no "
+ "description."), m_command->codeLocation()));
+ } else {
+ emit reportCommandDescription(m_command->highlight(), m_command->description());
+ }
+ }
+ doStart();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/abstractcommandexecutor.h b/src/lib/corelib/buildgraph/abstractcommandexecutor.h
new file mode 100644
index 000000000..0abb5c137
--- /dev/null
+++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ABSTRACTCOMMANDEXECUTOR_H
+#define QBS_ABSTRACTCOMMANDEXECUTOR_H
+
+#include <logging/logger.h>
+
+#include <QObject>
+
+namespace qbs {
+class ErrorInfo;
+
+namespace Internal {
+class AbstractCommand;
+class ScriptEngine;
+class Transformer;
+
+class AbstractCommandExecutor : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AbstractCommandExecutor(const Internal::Logger &logger, QObject *parent = 0);
+
+ void setMainThreadScriptEngine(ScriptEngine *engine) { m_mainThreadScriptEngine = engine; }
+ void setDryRunEnabled(bool enabled) { m_dryRun = enabled; }
+
+ virtual void waitForFinished() = 0;
+
+public slots:
+ void start(Transformer *transformer, const AbstractCommand *cmd);
+
+signals:
+ void reportCommandDescription(const QString &highlight, const QString &message);
+ void error(const qbs::ErrorInfo &err);
+ void finished();
+
+protected:
+ const AbstractCommand *command() const { return m_command; }
+ Transformer *transformer() const { return m_transformer; }
+ ScriptEngine *scriptEngine() const { return m_mainThreadScriptEngine; }
+ bool dryRun() const { return m_dryRun; }
+ Internal::Logger logger() const { return m_logger; }
+
+private:
+ virtual void doStart() = 0;
+
+private:
+ const AbstractCommand *m_command;
+ Transformer *m_transformer;
+ ScriptEngine *m_mainThreadScriptEngine;
+ bool m_dryRun;
+ Internal::Logger m_logger;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ABSTRACTCOMMANDEXECUTOR_H
diff --git a/src/lib/corelib/buildgraph/artifact.cpp b/src/lib/corelib/buildgraph/artifact.cpp
new file mode 100644
index 000000000..73a102827
--- /dev/null
+++ b/src/lib/corelib/buildgraph/artifact.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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 "artifact.h"
+
+#include "transformer.h"
+
+#include <language/propertymapinternal.h>
+#include <tools/fileinfo.h>
+#include <tools/persistence.h>
+
+QT_BEGIN_NAMESPACE
+
+static QDataStream &operator >>(QDataStream &s, qbs::Internal::Artifact::ArtifactType &t)
+{
+ int i;
+ s >> i;
+ t = static_cast<qbs::Internal::Artifact::ArtifactType>(i);
+ return s;
+}
+
+static QDataStream &operator <<(QDataStream &s, const qbs::Internal::Artifact::ArtifactType &t)
+{
+ return s << (int)t;
+}
+
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+Artifact::Artifact()
+{
+ initialize();
+}
+
+Artifact::~Artifact()
+{
+}
+
+void Artifact::initialize()
+{
+ artifactType = Unknown;
+ buildState = Untouched;
+ inputsScanned = false;
+ timestampRetrieved = false;
+ alwaysUpdated = true;
+}
+
+void Artifact::load(PersistentPool &pool)
+{
+ FileResourceBase::load(pool);
+ pool.loadContainer(children);
+
+ // restore parents of the loaded children
+ for (ArtifactList::const_iterator it = children.constBegin(); it != children.constEnd(); ++it)
+ (*it)->parents.insert(this);
+
+ pool.loadContainer(childrenAddedByScanner);
+ pool.loadContainer(fileDependencies);
+ properties = pool.idLoadS<PropertyMapInternal>();
+ transformer = pool.idLoadS<Transformer>();
+ unsigned char c;
+ pool.stream()
+ >> fileTags
+ >> artifactType
+ >> autoMocTimestamp
+ >> c;
+ alwaysUpdated = c;
+}
+
+void Artifact::store(PersistentPool &pool) const
+{
+ FileResourceBase::store(pool);
+ // Do not store parents to avoid recursion.
+ pool.storeContainer(children);
+ pool.storeContainer(childrenAddedByScanner);
+ pool.storeContainer(fileDependencies);
+ pool.store(properties);
+ pool.store(transformer);
+ pool.stream()
+ << fileTags
+ << artifactType
+ << autoMocTimestamp
+ << static_cast<unsigned char>(alwaysUpdated);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/artifact.h b/src/lib/corelib/buildgraph/artifact.h
new file mode 100644
index 000000000..b2e9b0703
--- /dev/null
+++ b/src/lib/corelib/buildgraph/artifact.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ARTIFACT_H
+#define QBS_ARTIFACT_H
+
+#include "artifactlist.h"
+#include "filedependency.h"
+#include "forward_decls.h"
+#include <language/filetags.h>
+#include <language/forward_decls.h>
+#include <tools/filetime.h>
+#include <tools/persistentobject.h>
+#include <tools/weakpointer.h>
+
+#include <QSet>
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+class Logger;
+
+/**
+ * The Artifact class
+ *
+ * Let artifact P be the parent of artifact C. Thus C is child of P.
+ * C produces P using the transformer P.transformer.
+ *
+ *
+ */
+class Artifact : public FileResourceBase
+{
+public:
+ Artifact();
+ ~Artifact();
+
+ ArtifactList parents;
+ ArtifactList children;
+ ArtifactList childrenAddedByScanner;
+ QSet<FileDependency *> fileDependencies;
+ FileTags fileTags;
+ WeakPointer<ResolvedProduct> product;
+ TransformerPtr transformer;
+ PropertyMapPtr properties;
+
+ enum ArtifactType
+ {
+ Unknown = 1,
+ SourceFile = 2,
+ Generated = 4
+ };
+
+ enum BuildState
+ {
+ Untouched = 0,
+ Buildable,
+ Building,
+ Built
+ };
+
+ ArtifactType artifactType;
+ FileTime autoMocTimestamp;
+ BuildState buildState; // Do not serialize. Will be refreshed for every build.
+ bool inputsScanned : 1; // Do not serialize. Will be refreshed for every build.
+ bool timestampRetrieved : 1; // Do not serialize. Will be refreshed for every build.
+ bool alwaysUpdated : 1;
+
+ void initialize();
+
+private:
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+
+// debugging helper
+inline QString toString(Artifact::ArtifactType t)
+{
+ switch (t) {
+ case Artifact::SourceFile:
+ return QLatin1String("SourceFile");
+ case Artifact::Generated:
+ return QLatin1String("Generated");
+ case Artifact::Unknown:
+ default:
+ return QLatin1String("Unknown");
+ }
+}
+
+// debugging helper
+inline QString toString(Artifact::BuildState s)
+{
+ switch (s) {
+ case Artifact::Untouched:
+ return QLatin1String("Untouched");
+ case Artifact::Buildable:
+ return QLatin1String("Buildable");
+ case Artifact::Building:
+ return QLatin1String("Building");
+ case Artifact::Built:
+ return QLatin1String("Built");
+ default:
+ return QLatin1String("Unknown");
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ARTIFACT_H
diff --git a/src/lib/corelib/buildgraph/artifactcleaner.cpp b/src/lib/corelib/buildgraph/artifactcleaner.cpp
new file mode 100644
index 000000000..98fed728e
--- /dev/null
+++ b/src/lib/corelib/buildgraph/artifactcleaner.cpp
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** 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 "artifactcleaner.h"
+
+#include "artifact.h"
+#include "artifactvisitor.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "transformer.h"
+
+#include <language/language.h>
+#include <logging/translator.h>
+#include <tools/cleanoptions.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/progressobserver.h>
+#include <tools/qbsassert.h>
+
+#include <QCoreApplication>
+#include <QDir>
+#include <QDirIterator>
+#include <QFileInfo>
+#include <QSet>
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+
+static void printRemovalMessage(const QString &path, bool dryRun, const Logger &logger)
+{
+ if (dryRun)
+ logger.qbsInfo() << Tr::tr("Would remove '%1'.").arg(path);
+ else
+ logger.qbsDebug() << "Removing '" << path << "'.";
+}
+
+static void invalidateArtifactTimestamp(Artifact *artifact)
+{
+ if (artifact->timestamp().isValid()) {
+ artifact->clearTimestamp();
+ artifact->product->topLevelProject()->buildData->isDirty = true;
+ }
+}
+
+static void removeArtifactFromDisk(Artifact *artifact, bool dryRun, const Logger &logger)
+{
+ QFileInfo fileInfo(artifact->filePath());
+ if (!fileInfo.exists()) {
+ if (!dryRun)
+ invalidateArtifactTimestamp(artifact);
+ return;
+ }
+ printRemovalMessage(fileInfo.filePath(), dryRun, logger);
+ if (dryRun)
+ return;
+ invalidateArtifactTimestamp(artifact);
+ QString errorMessage;
+ if (!removeFileRecursion(fileInfo, &errorMessage))
+ throw ErrorInfo(errorMessage);
+}
+
+class CleanupVisitor : public ArtifactVisitor
+{
+public:
+ CleanupVisitor(const CleanOptions &options, const Logger &logger)
+ : ArtifactVisitor(Artifact::Generated)
+ , m_options(options)
+ , m_logger(logger)
+ , m_hasError(false)
+ {
+ }
+
+ void visitProduct(const ResolvedProductConstPtr &product)
+ {
+ m_product = product;
+ ArtifactVisitor::visitProduct(product);
+ }
+
+ const QSet<QString> &directories() const { return m_directories; }
+ bool hasError() const { return m_hasError; }
+
+private:
+ void doVisit(Artifact *artifact)
+ {
+ if (artifact->product != m_product)
+ return;
+ if (m_options.cleanType() == CleanOptions::CleanupTemporaries) {
+ QBS_CHECK(artifact->transformer);
+ foreach (Artifact * const sibling, artifact->transformer->outputs) {
+ if (artifact->product->buildData->targetArtifacts.contains(sibling))
+ return;
+ }
+ }
+ try {
+ removeArtifactFromDisk(artifact, m_options.dryRun(), m_logger);
+ } catch (const ErrorInfo &error) {
+ if (!m_options.keepGoing())
+ throw;
+ m_logger.printWarning(error);
+ m_hasError = true;
+ }
+ m_directories << artifact->dirPath();
+ }
+
+ const CleanOptions m_options;
+ Logger m_logger;
+ bool m_hasError;
+ ResolvedProductConstPtr m_product;
+ QSet<QString> m_directories;
+};
+
+ArtifactCleaner::ArtifactCleaner(const Logger &logger, ProgressObserver *observer)
+ : m_logger(logger), m_observer(observer)
+{
+}
+
+void ArtifactCleaner::cleanup(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const CleanOptions &options)
+{
+ m_hasError = false;
+
+ const QString configString = Tr::tr(" for configuration %1").arg(project->id());
+ m_observer->initialize(Tr::tr("Cleaning up%1").arg(configString), products.count() + 1);
+
+ QSet<QString> directories;
+ foreach (const ResolvedProductPtr &product, products) {
+ CleanupVisitor visitor(options, m_logger);
+ visitor.visitProduct(product);
+ directories.unite(visitor.directories());
+ if (visitor.hasError())
+ m_hasError = true;
+ m_observer->incrementProgressValue();
+ }
+
+ // Directories created during the build are not artifacts (TODO: should they be?),
+ // so we have to clean them up manually.
+ QList<QString> dirList = directories.toList();
+ for (int i = 0; i < dirList.count(); ++i) {
+ const QString &dir = dirList.at(i);
+ if (dir.startsWith(project->buildDirectory) && FileInfo(dir).exists())
+ removeEmptyDirectories(dir, options);
+ if (dir != project->buildDirectory) {
+ const QString parentDir = QDir::cleanPath(dir + "/..");
+ if (parentDir != project->buildDirectory && !dirList.contains(parentDir))
+ dirList << parentDir;
+ }
+ }
+ m_observer->incrementProgressValue();
+
+ if (m_hasError)
+ throw ErrorInfo(Tr::tr("Failed to remove some files."));
+ m_observer->setFinished();
+}
+
+void ArtifactCleaner::removeEmptyDirectories(const QString &rootDir, const CleanOptions &options,
+ bool *isEmpty)
+{
+ bool subTreeIsEmpty = true;
+ QDirIterator it(rootDir, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
+ while (it.hasNext()) {
+ it.next();
+ if (!it.fileInfo().isSymLink() && it.fileInfo().isDir())
+ removeEmptyDirectories(it.filePath(), options, &subTreeIsEmpty);
+ else
+ subTreeIsEmpty = false;
+ }
+ if (subTreeIsEmpty) {
+ printRemovalMessage(rootDir, options.dryRun(), m_logger);
+ if (!QDir::root().rmdir(rootDir)) {
+ ErrorInfo error(Tr::tr("Failure to remove empty directory '%1'.").arg(rootDir));
+ if (!options.keepGoing())
+ throw error;
+ m_logger.printWarning(error);
+ m_hasError = true;
+ subTreeIsEmpty = false;
+ }
+ }
+ if (!subTreeIsEmpty && isEmpty)
+ *isEmpty = false;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/artifactcleaner.h b/src/lib/corelib/buildgraph/artifactcleaner.h
new file mode 100644
index 000000000..760af3412
--- /dev/null
+++ b/src/lib/corelib/buildgraph/artifactcleaner.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_ARTIFACTCLEANER_H
+#define QBS_ARTIFACTCLEANER_H
+
+#include <QList>
+
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+
+namespace qbs {
+class CleanOptions;
+
+namespace Internal {
+class ProgressObserver;
+
+class ArtifactCleaner
+{
+public:
+ ArtifactCleaner(const Logger &logger, ProgressObserver *observer);
+ void cleanup(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ const CleanOptions &options);
+
+private:
+ void removeEmptyDirectories(const QString &rootDir, const CleanOptions &options,
+ bool *isEmpty = 0);
+
+ Logger m_logger;
+ bool m_hasError;
+ ProgressObserver *m_observer;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ARTIFACTCLEANER_H
diff --git a/src/lib/corelib/buildgraph/artifactlist.cpp b/src/lib/corelib/buildgraph/artifactlist.cpp
new file mode 100644
index 000000000..b435e3e60
--- /dev/null
+++ b/src/lib/corelib/buildgraph/artifactlist.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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 "artifactlist.h"
+
+namespace qbs {
+namespace Internal {
+
+ArtifactList::ArtifactList()
+{}
+
+ArtifactList::ArtifactList(const ArtifactList &other)
+ : m_data(other.m_data)
+{}
+
+ArtifactList &ArtifactList::unite(const ArtifactList &other)
+{
+ std::set<Artifact *>::const_iterator it = other.m_data.begin();
+ for (; it != other.m_data.end(); ++it)
+ m_data.insert(*it);
+ return *this;
+}
+
+void ArtifactList::remove(Artifact *artifact)
+{
+ iterator it = m_data.find(artifact);
+ if (it != m_data.end())
+ m_data.erase(it);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/artifactlist.h b/src/lib/corelib/buildgraph/artifactlist.h
new file mode 100644
index 000000000..bbe2771cb
--- /dev/null
+++ b/src/lib/corelib/buildgraph/artifactlist.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ARTIFACTLIST_H
+#define QBS_ARTIFACTLIST_H
+
+#include <set>
+#include <cstddef>
+
+namespace qbs {
+namespace Internal {
+
+class Artifact;
+
+/**
+ * List that holds a bunch of build graph artifacts.
+ * This is faster than QSet when iterating over the container.
+ */
+class ArtifactList
+{
+public:
+ ArtifactList();
+ ArtifactList(const ArtifactList &other);
+
+ ArtifactList &unite(const ArtifactList &other);
+
+ typedef std::set<Artifact *>::const_iterator const_iterator;
+ typedef std::set<Artifact *>::iterator iterator;
+ typedef Artifact * value_type;
+
+ iterator begin() { return m_data.begin(); }
+ iterator end() { return m_data.end(); }
+ const_iterator begin() const { return m_data.begin(); }
+ const_iterator end() const { return m_data.end(); }
+ const_iterator constBegin() const { return m_data.begin(); }
+ const_iterator constEnd() const { return m_data.end(); }
+
+ void insert(Artifact *artifact)
+ {
+ m_data.insert(artifact);
+ }
+
+ void operator +=(Artifact *artifact)
+ {
+ insert(artifact);
+ }
+
+ void remove(Artifact *artifact);
+
+ bool contains(Artifact *artifact) const
+ {
+ return m_data.find(artifact) != m_data.end();
+ }
+
+ void clear()
+ {
+ m_data.clear();
+ }
+
+ bool isEmpty() const
+ {
+ return m_data.empty();
+ }
+
+ int count() const
+ {
+ return (int)m_data.size();
+ }
+
+ void reserve(int)
+ {
+ // no-op
+ }
+
+ bool operator==(const ArtifactList &other) const { return m_data == other.m_data; }
+ bool operator!=(const ArtifactList &other) const { return !(*this == other); }
+
+
+private:
+ std::set<Artifact *> m_data;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ARTIFACTLIST_H
diff --git a/src/lib/corelib/buildgraph/artifactvisitor.cpp b/src/lib/corelib/buildgraph/artifactvisitor.cpp
new file mode 100644
index 000000000..3b5203e26
--- /dev/null
+++ b/src/lib/corelib/buildgraph/artifactvisitor.cpp
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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 "artifactvisitor.h"
+
+#include "artifact.h"
+#include "productbuilddata.h"
+#include <language/language.h>
+#include <tools/qbsassert.h>
+
+namespace qbs {
+namespace Internal {
+
+ArtifactVisitor::ArtifactVisitor(int artifactType) : m_artifactType(artifactType)
+{
+}
+
+void ArtifactVisitor::visitArtifact(Artifact *artifact)
+{
+ QBS_CHECK(artifact);
+ if (m_artifactType & artifact->artifactType)
+ doVisit(artifact);
+}
+
+void ArtifactVisitor::visitProduct(const ResolvedProductConstPtr &product)
+{
+ if (!product->buildData)
+ return;
+ foreach (Artifact * const artifact, product->buildData->artifacts)
+ visitArtifact(artifact);
+}
+
+void ArtifactVisitor::visitProject(const ResolvedProjectConstPtr &project)
+{
+ foreach (const ResolvedProductConstPtr &product, project->products)
+ visitProduct(product);
+ foreach (const ResolvedProjectConstPtr &subProject, project->subProjects)
+ visitProject(subProject);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/artifactvisitor.h b/src/lib/corelib/buildgraph/artifactvisitor.h
new file mode 100644
index 000000000..0c113ea1b
--- /dev/null
+++ b/src/lib/corelib/buildgraph/artifactvisitor.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_ARTIFACTVISITOR_H
+#define QBS_ARTIFACTVISITOR_H
+
+#include "forward_decls.h"
+
+#include <language/forward_decls.h>
+
+#include <QList>
+#include <QSet>
+
+namespace qbs {
+namespace Internal {
+
+class ArtifactVisitor
+{
+public:
+ ArtifactVisitor(int artifactType);
+
+ virtual void visitArtifact(Artifact *artifact);
+ virtual void visitProduct(const ResolvedProductConstPtr &product);
+ virtual void visitProject(const ResolvedProjectConstPtr &project);
+
+private:
+ virtual void doVisit(Artifact *artifact) = 0;
+
+ const int m_artifactType;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ARTIFACTVISITOR_H
diff --git a/src/lib/corelib/buildgraph/automoc.cpp b/src/lib/corelib/buildgraph/automoc.cpp
new file mode 100644
index 000000000..5dcb6ec9f
--- /dev/null
+++ b/src/lib/corelib/buildgraph/automoc.cpp
@@ -0,0 +1,343 @@
+/****************************************************************************
+**
+** 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 "automoc.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "buildgraph.h"
+#include "rulesapplicator.h"
+#include "scanresultcache.h"
+#include <buildgraph/artifact.h>
+#include <buildgraph/transformer.h>
+#include <language/language.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/scannerpluginmanager.h>
+
+namespace qbs {
+namespace Internal {
+
+AutoMoc::AutoMoc(const Logger &logger, QObject *parent)
+ : QObject(parent)
+ , m_scanResultCache(0)
+ , m_logger(logger)
+{
+}
+
+void AutoMoc::setScanResultCache(ScanResultCache *scanResultCache)
+{
+ m_scanResultCache = scanResultCache;
+}
+
+void AutoMoc::apply(const ResolvedProductPtr &product)
+{
+ if (cppScanners().isEmpty() || hppScanners().isEmpty())
+ throw ErrorInfo("C++ scanner cannot be loaded.");
+
+ Artifact *pluginMetaDataFile = 0;
+ Artifact *pchFile = 0;
+ QList<QPair<Artifact *, FileType> > artifactsToMoc;
+ QSet<QString> includedMocCppFiles;
+ const FileTime currentTime = FileTime::currentTime();
+ ArtifactList::const_iterator it = product->buildData->artifacts.begin();
+ for (; it != product->buildData->artifacts.end(); ++it) {
+ Artifact *artifact = *it;
+ if (!pchFile || !pluginMetaDataFile) {
+ foreach (const FileTag &fileTag, artifact->fileTags) {
+ if (fileTag == "cpp_pch")
+ pchFile = artifact;
+ else if (fileTag == "qt_plugin_metadata")
+ pluginMetaDataFile = artifact;
+ }
+ }
+
+ if (!pluginMetaDataFile && artifact->fileTags.contains("qt_plugin_metadata")) {
+ if (m_logger.debugEnabled()) {
+ m_logger.qbsDebug() << "[AUTOMOC] found Qt plugin metadata file "
+ << artifact->filePath();
+ }
+ pluginMetaDataFile = artifact;
+ }
+ if (artifact->artifactType != Artifact::SourceFile)
+ continue;
+ if (artifact->timestamp() < artifact->autoMocTimestamp)
+ continue;
+ artifact->autoMocTimestamp = currentTime;
+ const FileType fileType = AutoMoc::fileType(artifact);
+ if (fileType == UnknownFileType)
+ continue;
+ FileTag mocFileTag;
+ bool alreadyMocced = isVictimOfMoc(artifact, fileType, mocFileTag);
+ bool hasQObjectMacro;
+ scan(artifact, fileType, hasQObjectMacro, includedMocCppFiles);
+ if (hasQObjectMacro && !alreadyMocced)
+ artifactsToMoc += qMakePair(artifact, fileType);
+ else if (!hasQObjectMacro && alreadyMocced)
+ unmoc(artifact, mocFileTag);
+ }
+
+ Artifact *pluginHeaderFile = 0;
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+ for (int i = artifactsToMoc.count(); --i >= 0;) {
+ const QPair<Artifact *, FileType> &p = artifactsToMoc.at(i);
+ Artifact * const artifact = p.first;
+ FileType fileType = p.second;
+ foreach (const FileTag &fileTag, artifact->fileTags) {
+ if (fileTag == "moc_hpp") {
+ const QString mocFileName = generateMocFileName(artifact, fileType);
+ if (includedMocCppFiles.contains(mocFileName)) {
+ FileTag newFileTag = "moc_hpp_inc";
+ artifact->fileTags -= fileTag;
+ artifact->fileTags += newFileTag;
+ artifactsPerFileTag[newFileTag].insert(artifact);
+ continue;
+ }
+ } else if (fileTag == "moc_plugin_hpp") {
+ if (m_logger.debugEnabled()) {
+ m_logger.qbsDebug() << "[AUTOMOC] found Qt plugin header file "
+ << artifact->filePath();
+ }
+ FileTag newFileTag = "moc_hpp";
+ artifact->fileTags -= fileTag;
+ artifact->fileTags += newFileTag;
+ artifactsPerFileTag[newFileTag].insert(artifact);
+ pluginHeaderFile = artifact;
+ }
+ artifactsPerFileTag[fileTag].insert(artifact);
+ }
+ }
+
+ if (pchFile)
+ artifactsPerFileTag["cpp_pch"] += pchFile;
+ if (!artifactsPerFileTag.isEmpty()) {
+ emit reportCommandDescription(QLatin1String("automoc"),
+ Tr::tr("Applying moc rules for '%1'.")
+ .arg(product->name));
+ RulesApplicator(product, artifactsPerFileTag, m_logger).applyAllRules();
+ }
+ if (pluginHeaderFile && pluginMetaDataFile) {
+ // Make every artifact that is dependent of the header file also
+ // dependent of the plugin metadata file.
+ foreach (Artifact *outputOfHeader, pluginHeaderFile->parents)
+ loggedConnect(outputOfHeader, pluginMetaDataFile, m_logger);
+ }
+
+ product->topLevelProject()->buildData->updateNodesThatMustGetNewTransformer(m_logger);
+}
+
+QString AutoMoc::generateMocFileName(Artifact *artifact, FileType fileType)
+{
+ QString mocFileName;
+ switch (fileType) {
+ case UnknownFileType:
+ break;
+ case HppFileType:
+ mocFileName = "moc_" + FileInfo::baseName(artifact->filePath()) + ".cpp";
+ break;
+ case CppFileType:
+ mocFileName = FileInfo::baseName(artifact->filePath()) + ".moc";
+ break;
+ }
+ return mocFileName;
+}
+
+AutoMoc::FileType AutoMoc::fileType(Artifact *artifact)
+{
+ foreach (const FileTag &fileTag, artifact->fileTags)
+ if (fileTag == "hpp")
+ return HppFileType;
+ else if (fileTag == "cpp")
+ return CppFileType;
+ return UnknownFileType;
+}
+
+void AutoMoc::scan(Artifact *artifact, FileType fileType, bool &hasQObjectMacro,
+ QSet<QString> &includedMocCppFiles)
+{
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[AUTOMOC] checks " << relativeArtifactFileName(artifact);
+
+ hasQObjectMacro = false;
+
+ foreach (ScannerPlugin *scanner, fileType == HppFileType ? hppScanners() : cppScanners()) {
+ ScanResultCache::Result scanResult = m_scanResultCache->value(artifact->filePath());
+ if (!scanResult.valid) {
+ scanResult.valid = true;
+ void *opaq = scanner->open(artifact->filePath().utf16(),
+ ScanForDependenciesFlag | ScanForFileTagsFlag);
+ if (!opaq || !scanner->additionalFileTags)
+ continue;
+
+ int length = 0;
+ const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length);
+ if (szFileTagsFromScanner && length > 0) {
+ for (int i = length; --i >= 0;)
+ scanResult.additionalFileTags += szFileTagsFromScanner[i];
+ }
+
+ forever {
+ int flags = 0;
+ const char *szOutFilePath = scanner->next(opaq, &length, &flags);
+ if (szOutFilePath == 0)
+ break;
+ QString includedFilePath = QString::fromLocal8Bit(szOutFilePath, length);
+ if (includedFilePath.isEmpty())
+ continue;
+ bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG);
+ scanResult.deps += ScanResultCache::Dependency(includedFilePath, isLocalInclude);
+ }
+
+ scanner->close(opaq);
+ m_scanResultCache->insert(artifact->filePath(), scanResult);
+ }
+
+ foreach (const FileTag &tag, scanResult.additionalFileTags) {
+ artifact->fileTags.insert(tag);
+ if (tag.name().startsWith("moc")) {
+ hasQObjectMacro = true;
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[AUTOMOC] finds Q_OBJECT macro";
+ }
+ }
+
+ foreach (const ScanResultCache::Dependency &dependency, scanResult.deps) {
+ const QString &includedFilePath = dependency.filePath();
+ if (includedFilePath.startsWith("moc_") && includedFilePath.endsWith(".cpp")) {
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[AUTOMOC] finds included file: " << includedFilePath;
+ includedMocCppFiles += includedFilePath;
+ }
+ }
+ }
+}
+
+static FileTags provideMocHeaderFileTags()
+{
+ FileTags fileTags;
+ fileTags << "moc_hpp" << "moc_hpp_inc" << "moc_plugin_hpp";
+ return fileTags;
+}
+
+bool AutoMoc::isVictimOfMoc(Artifact *artifact, FileType fileType, FileTag &foundMocFileTag)
+{
+ static const FileTags mocHeaderFileTags = provideMocHeaderFileTags();
+ static const FileTag mocCppFileTag = "moc_cpp";
+ foundMocFileTag.clear();
+ switch (fileType) {
+ case UnknownFileType:
+ break;
+ case HppFileType:
+ foreach (const FileTag &fileTag, artifact->fileTags) {
+ if (mocHeaderFileTags.contains(fileTag)) {
+ foundMocFileTag = fileTag;
+ break;
+ }
+ }
+ break;
+ case CppFileType:
+ if (artifact->fileTags.contains(mocCppFileTag))
+ foundMocFileTag = mocCppFileTag;
+ break;
+ }
+ return foundMocFileTag.isValid();
+}
+
+void AutoMoc::unmoc(Artifact *artifact, const FileTag &mocFileTag)
+{
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[AUTOMOC] unmoc'ing " << relativeArtifactFileName(artifact);
+
+ artifact->fileTags.remove(mocFileTag);
+
+ Artifact *generatedMocArtifact = 0;
+ foreach (Artifact *parent, artifact->parents) {
+ foreach (const FileTag &fileTag, parent->fileTags) {
+ if (fileTag == "hpp" || fileTag == "cpp") {
+ generatedMocArtifact = parent;
+ break;
+ }
+ }
+ }
+
+ if (!generatedMocArtifact) {
+ m_logger.qbsTrace() << "[AUTOMOC] generated moc artifact could not be found";
+ return;
+ }
+
+ TopLevelProject * const project = artifact->product->topLevelProject();
+ if (mocFileTag == "moc_hpp") {
+ Artifact *mocObjArtifact = 0;
+ foreach (Artifact *parent, generatedMocArtifact->parents) {
+ foreach (const FileTag &fileTag, parent->fileTags) {
+ if (fileTag == "obj" || fileTag == "fpicobj") {
+ mocObjArtifact = parent;
+ break;
+ }
+ }
+ }
+
+ if (!mocObjArtifact) {
+ m_logger.qbsTrace() << "[AUTOMOC] generated moc obj artifact could not be found";
+ } else {
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << "[AUTOMOC] removing moc obj artifact "
+ << relativeArtifactFileName(mocObjArtifact);
+ }
+ project->buildData->removeArtifact(mocObjArtifact, m_logger);
+ delete mocObjArtifact;
+ }
+ }
+
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << "[AUTOMOC] removing generated artifact "
+ << relativeArtifactFileName(generatedMocArtifact);
+ }
+ project->buildData->removeArtifact(generatedMocArtifact, m_logger);
+ delete generatedMocArtifact;
+}
+
+const QList<ScannerPlugin *> &AutoMoc::cppScanners() const
+{
+ if (m_cppScanners.isEmpty())
+ m_cppScanners = ScannerPluginManager::scannersForFileTag("cpp");
+
+ return m_cppScanners;
+}
+
+const QList<ScannerPlugin *> &AutoMoc::hppScanners() const
+{
+ if (m_hppScanners.isEmpty())
+ m_hppScanners = ScannerPluginManager::scannersForFileTag("hpp");
+
+ return m_hppScanners;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/automoc.h b/src/lib/corelib/buildgraph/automoc.h
new file mode 100644
index 000000000..d57fc292e
--- /dev/null
+++ b/src/lib/corelib/buildgraph/automoc.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_AUTOMOC_H
+#define QBS_AUTOMOC_H
+
+#include "forward_decls.h"
+
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+
+#include <QObject>
+
+struct ScannerPlugin;
+
+namespace qbs {
+namespace Internal {
+class FileTag;
+class ScanResultCache;
+
+/**
+ * Scans cpp and hpp files for the Q_OBJECT / Q_GADGET macro and
+ * applies the corresponding rule then.
+ * Also scans the files for moc_XXX.cpp files to find out if we must
+ * compile and link a moc_XXX.cpp file or not.
+ *
+ * This whole thing is an ugly hack, I know.
+ */
+class AutoMoc : public QObject
+{
+ Q_OBJECT
+
+public:
+ AutoMoc(const Logger &logger, QObject *parent = 0);
+
+ void setScanResultCache(ScanResultCache *scanResultCache);
+ void apply(const ResolvedProductPtr &product);
+
+signals:
+ void reportCommandDescription(const QString &highlight, const QString &message);
+
+private:
+ enum FileType
+ {
+ UnknownFileType,
+ HppFileType,
+ CppFileType
+ };
+
+private:
+ static QString generateMocFileName(Artifact *artifact, FileType fileType);
+ static FileType fileType(Artifact *artifact);
+ void scan(Artifact *artifact, FileType fileType, bool &hasQObjectMacro,
+ QSet<QString> &includedMocCppFiles);
+ bool isVictimOfMoc(Artifact *artifact, FileType fileType, FileTag &foundMocFileTag);
+ void unmoc(Artifact *artifact, const FileTag &mocFileTag);
+ const QList<ScannerPlugin *> &cppScanners() const;
+ const QList<ScannerPlugin *> &hppScanners() const;
+
+ mutable QList<ScannerPlugin *> m_cppScanners;
+ mutable QList<ScannerPlugin *> m_hppScanners;
+ ScanResultCache *m_scanResultCache;
+ Logger m_logger;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_AUTOMOC_H
diff --git a/src/lib/corelib/buildgraph/buildgraph.cpp b/src/lib/corelib/buildgraph/buildgraph.cpp
new file mode 100644
index 000000000..5284de5a9
--- /dev/null
+++ b/src/lib/corelib/buildgraph/buildgraph.cpp
@@ -0,0 +1,498 @@
+/****************************************************************************
+**
+** 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 "buildgraph.h"
+
+#include "artifact.h"
+#include "cycledetector.h"
+#include "projectbuilddata.h"
+#include "productbuilddata.h"
+#include "transformer.h"
+
+#include <jsextensions/jsextensions.h>
+#include <jsextensions/moduleproperties.h>
+#include <language/language.h>
+#include <language/preparescriptobserver.h>
+#include <language/scriptengine.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+
+#include <QFile>
+
+namespace qbs {
+namespace Internal {
+
+static void setupProductScriptValue(ScriptEngine *engine, QScriptValue &productScriptValue,
+ const ResolvedProductConstPtr &product,
+ PrepareScriptObserver *observer);
+
+class DependenciesFunction
+{
+public:
+ DependenciesFunction(ScriptEngine *engine)
+ : m_engine(engine)
+ {
+ }
+
+ void init(QScriptValue &productScriptValue, const ResolvedProductConstPtr &product)
+ {
+ QScriptValue depfunc = m_engine->newFunction(&js_productDependencies);
+ setProduct(depfunc, product.data());
+ QScriptValue descriptor = m_engine->newObject();
+ descriptor.setProperty(QLatin1String("get"), depfunc);
+ descriptor.setProperty(QLatin1String("set"), m_engine->evaluate("(function(){})"));
+ descriptor.setProperty(QLatin1String("enumerable"), true);
+ m_engine->defineProperty(productScriptValue, QLatin1String("dependencies"), descriptor);
+ }
+
+private:
+ static QScriptValue js_productDependencies(QScriptContext *context, QScriptEngine *engine)
+ {
+ QScriptValue callee = context->callee();
+ const ResolvedProduct * const product = getProduct(callee);
+ QScriptValue result = engine->newArray();
+ quint32 idx = 0;
+ foreach (const ResolvedProductPtr &dependency, product->dependencies) {
+ QScriptValue obj = engine->newObject();
+ setupProductScriptValue(static_cast<ScriptEngine *>(engine), obj, dependency, 0);
+ result.setProperty(idx++, obj);
+ }
+ foreach (const ResolvedModuleConstPtr &dependency, product->modules) {
+ QScriptValue obj = engine->newObject();
+ setupModuleScriptValue(static_cast<ScriptEngine *>(engine), obj,
+ product->properties->value(), dependency->name);
+ result.setProperty(idx++, obj);
+ }
+ return result;
+ }
+
+ static QScriptValue js_moduleDependencies(QScriptContext *context, QScriptEngine *engine)
+ {
+ QScriptValue callee = context->callee();
+ const QVariantMap modulesMap = callee.data().toVariant().toMap();
+ QScriptValue result = engine->newArray();
+ quint32 idx = 0;
+ for (QVariantMap::const_iterator it = modulesMap.begin(); it != modulesMap.end(); ++it) {
+ QScriptValue obj = engine->newObject();
+ obj.setProperty(QLatin1String("name"), it.key());
+ setupModuleScriptValue(static_cast<ScriptEngine *>(engine), obj, it.value().toMap(),
+ it.key());
+ result.setProperty(idx++, obj);
+ }
+ return result;
+ }
+
+ static void setupModuleScriptValue(ScriptEngine *engine, QScriptValue &moduleScriptValue,
+ const QVariantMap &propertyMap,
+ const QString &moduleName)
+ {
+ const QVariantMap propMap
+ = propertyMap.value(QLatin1String("modules")).toMap().value(moduleName).toMap();
+ for (QVariantMap::ConstIterator it = propMap.constBegin(); it != propMap.constEnd(); ++it) {
+ const QVariant &value = it.value();
+ if (value.isValid() && it.key() != QLatin1String("modules"))
+ moduleScriptValue.setProperty(it.key(), engine->toScriptValue(value));
+ }
+ QScriptValue depfunc = engine->newFunction(&js_moduleDependencies);
+ depfunc.setData(engine->toScriptValue(propMap.value(QLatin1String("modules"))));
+ QScriptValue descriptor = engine->newObject();
+ descriptor.setProperty(QLatin1String("get"), depfunc);
+ descriptor.setProperty(QLatin1String("set"), engine->evaluate("(function(){})"));
+ descriptor.setProperty(QLatin1String("enumerable"), true);
+ engine->defineProperty(moduleScriptValue, QLatin1String("dependencies"), descriptor);
+ moduleScriptValue.setProperty(QLatin1String("type"), QLatin1String("module"));
+ }
+
+ static void setProduct(QScriptValue scriptValue, const ResolvedProduct *product)
+ {
+ QVariant v;
+ v.setValue<quintptr>(reinterpret_cast<quintptr>(product));
+ scriptValue.setData(scriptValue.engine()->newVariant(v));
+ }
+
+ static const ResolvedProduct *getProduct(const QScriptValue &scriptValue)
+ {
+ const quintptr ptr = scriptValue.data().toVariant().value<quintptr>();
+ return reinterpret_cast<const ResolvedProduct *>(ptr);
+ }
+
+ ScriptEngine *m_engine;
+};
+
+static void setupProductScriptValue(ScriptEngine *engine, QScriptValue &productScriptValue,
+ const ResolvedProductConstPtr &product,
+ PrepareScriptObserver *observer)
+{
+ ModuleProperties::init(productScriptValue, product);
+ DependenciesFunction(engine).init(productScriptValue, product);
+ if (observer)
+ observer->setProductObjectId(productScriptValue.objectId());
+ const QVariantMap &propMap = product->properties->value();
+ for (QVariantMap::ConstIterator it = propMap.constBegin(); it != propMap.constEnd(); ++it) {
+ const QVariant &value = it.value();
+ if (it.key() != QLatin1String("modules")) {
+ engine->setObservedProperty(productScriptValue, it.key(),
+ engine->toScriptValue(value), observer);
+ }
+ }
+}
+
+void setupScriptEngineForFile(ScriptEngine *engine, const ResolvedFileContextConstPtr &fileContext,
+ QScriptValue targetObject)
+{
+ engine->import(fileContext->jsImports, targetObject, targetObject);
+ JsExtensions::setupExtensions(fileContext->jsExtensions, targetObject);
+}
+
+void setupScriptEngineForProduct(ScriptEngine *engine, const ResolvedProductConstPtr &product,
+ const RuleConstPtr &rule, QScriptValue targetObject,
+ PrepareScriptObserver *observer)
+{
+ ScriptEngine::ScriptValueCache * const cache = engine->scriptValueCache();
+ if (cache->observer != observer) {
+ cache->project = 0;
+ cache->product = 0;
+ }
+
+ if (cache->project != product->project) {
+ cache->project = product->project.data();
+ cache->projectScriptValue = engine->newObject();
+ cache->projectScriptValue.setProperty(QLatin1String("filePath"),
+ product->project->location.fileName());
+ cache->projectScriptValue.setProperty(QLatin1String("path"),
+ FileInfo::path(product->project->location.fileName()));
+ const QVariantMap &projectProperties = product->project->projectProperties();
+ for (QVariantMap::const_iterator it = projectProperties.begin();
+ it != projectProperties.end(); ++it) {
+ engine->setObservedProperty(cache->projectScriptValue, it.key(),
+ engine->toScriptValue(it.value()), observer);
+ }
+ }
+ targetObject.setProperty(QLatin1String("project"), cache->projectScriptValue);
+ if (observer)
+ observer->setProjectObjectId(cache->projectScriptValue.objectId());
+
+ if (cache->product != product) {
+ cache->product = product.data();
+ {
+ QVariant v;
+ v.setValue<void*>(&product->buildEnvironment);
+ engine->setProperty("_qbs_procenv", v);
+ }
+ cache->productScriptValue = engine->newObject();
+ setupProductScriptValue(engine, cache->productScriptValue, product, observer);
+ }
+ targetObject.setProperty(QLatin1String("product"), cache->productScriptValue);
+
+ // If the Rule is in a Module, set up the 'moduleName' property
+ cache->productScriptValue.setProperty(QLatin1String("moduleName"),
+ rule->module->name.isEmpty() ? QScriptValue() : rule->module->name);
+}
+
+bool findPath(Artifact *u, Artifact *v, QList<Artifact*> &path)
+{
+ if (u == v) {
+ path.append(v);
+ return true;
+ }
+
+ for (ArtifactList::const_iterator it = u->children.begin(); it != u->children.end(); ++it) {
+ if (findPath(*it, v, path)) {
+ path.prepend(u);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * c must be built before p
+ * p ----> c
+ * p.children = c
+ * c.parents = p
+ *
+ * also: children means i depend on or i am produced by
+ * parent means "produced by me" or "depends on me"
+ */
+void connect(Artifact *p, Artifact *c)
+{
+ QBS_CHECK(p != c);
+ p->children.insert(c);
+ c->parents.insert(p);
+ p->product->topLevelProject()->buildData->isDirty = true;
+}
+
+void loggedConnect(Artifact *u, Artifact *v, const Logger &logger)
+{
+ QBS_CHECK(u != v);
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] connect '%1' -> '%2'")
+ .arg(relativeArtifactFileName(u), relativeArtifactFileName(v));
+ }
+ connect(u, v);
+}
+
+static bool existsPath(Artifact *u, Artifact *v)
+{
+ if (u == v)
+ return true;
+
+ for (ArtifactList::const_iterator it = u->children.begin(); it != u->children.end(); ++it)
+ if (existsPath(*it, v))
+ return true;
+
+ return false;
+}
+
+bool safeConnect(Artifact *u, Artifact *v, const Logger &logger)
+{
+ QBS_CHECK(u != v);
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] safeConnect: '%1' '%2'")
+ .arg(relativeArtifactFileName(u), relativeArtifactFileName(v));
+ }
+
+ if (existsPath(v, u)) {
+ QList<Artifact *> circle;
+ findPath(v, u, circle);
+ logger.qbsTrace() << "[BG] safeConnect: circle detected " << toStringList(circle);
+ return false;
+ }
+
+ connect(u, v);
+ return true;
+}
+
+void disconnect(Artifact *u, Artifact *v, const Logger &logger)
+{
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] disconnect: '%1' '%2'")
+ .arg(relativeArtifactFileName(u), relativeArtifactFileName(v));
+ }
+ u->children.remove(v);
+ u->childrenAddedByScanner.remove(v);
+ v->parents.remove(u);
+}
+
+void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger)
+{
+ if (artifact->artifactType != Artifact::Generated)
+ return;
+
+ QFile file(artifact->filePath());
+ if (!file.exists())
+ return;
+
+ logger.qbsDebug() << "removing " << artifact->fileName();
+ if (!file.remove()) {
+ logger.qbsWarning() << QString::fromLocal8Bit("Cannot remove '%1'.")
+ .arg(artifact->filePath());
+ }
+}
+
+QString relativeArtifactFileName(const Artifact *artifact)
+{
+ const QString &buildDir = artifact->product->topLevelProject()->buildDirectory;
+ QString str = artifact->filePath();
+ if (str.startsWith(buildDir))
+ str.remove(0, buildDir.count());
+ if (str.startsWith('/'))
+ str.remove(0, 1);
+ return str;
+}
+
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product,
+ const ProjectBuildData *projectBuildData, const QString &dirPath, const QString &fileName,
+ bool compareByName)
+{
+ const QList<FileResourceBase *> lookupResults
+ = projectBuildData->lookupFiles(dirPath, fileName);
+ for (QList<FileResourceBase *>::const_iterator it = lookupResults.constBegin();
+ it != lookupResults.constEnd(); ++it) {
+ Artifact *artifact = dynamic_cast<Artifact *>(*it);
+ if (artifact && (compareByName
+ ? artifact->product->name == product->name
+ : artifact->product == product))
+ return artifact;
+ }
+ return 0;
+}
+
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const QString &dirPath,
+ const QString &fileName, bool compareByName)
+{
+ return lookupArtifact(product, product->topLevelProject()->buildData.data(), dirPath, fileName,
+ compareByName);
+}
+
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const QString &filePath,
+ bool compareByName)
+{
+ QString dirPath, fileName;
+ FileInfo::splitIntoDirectoryAndFileName(filePath, &dirPath, &fileName);
+ return lookupArtifact(product, dirPath, fileName, compareByName);
+}
+
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const ProjectBuildData *buildData,
+ const QString &filePath, bool compareByName)
+{
+ QString dirPath, fileName;
+ FileInfo::splitIntoDirectoryAndFileName(filePath, &dirPath, &fileName);
+ return lookupArtifact(product, buildData, dirPath, fileName, compareByName);
+}
+
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const Artifact *artifact,
+ bool compareByName)
+{
+ return lookupArtifact(product, artifact->dirPath(), artifact->fileName(), compareByName);
+}
+
+Artifact *createArtifact(const ResolvedProductPtr &product,
+ const SourceArtifactConstPtr &sourceArtifact, const Logger &logger)
+{
+ Artifact *artifact = new Artifact;
+ artifact->artifactType = Artifact::SourceFile;
+ artifact->setFilePath(sourceArtifact->absoluteFilePath);
+ artifact->fileTags = sourceArtifact->fileTags;
+ artifact->properties = sourceArtifact->properties;
+ insertArtifact(product, artifact, logger);
+ return artifact;
+}
+
+void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const Logger &logger)
+{
+ QBS_CHECK(!artifact->product);
+ QBS_CHECK(!artifact->filePath().isEmpty());
+ QBS_CHECK(!product->buildData->artifacts.contains(artifact));
+#ifdef QT_DEBUG
+ foreach (const ResolvedProductConstPtr &otherProduct, product->project->products) {
+ if (lookupArtifact(otherProduct, artifact->filePath())) {
+ if (artifact->artifactType == Artifact::Generated) {
+ QString pl;
+ pl.append(QString(" - %1 \n").arg(product->name));
+ foreach (const ResolvedProductConstPtr &p, product->project->products) {
+ if (lookupArtifact(p, artifact->filePath()))
+ pl.append(QString(" - %1 \n").arg(p->name));
+ }
+ throw ErrorInfo(QString ("BUG: already inserted in this project: %1\n%2")
+ .arg(artifact->filePath()).arg(pl), CodeLocation(), true);
+ }
+ }
+ }
+#endif
+ product->buildData->artifacts.insert(artifact);
+ artifact->product = product;
+ product->topLevelProject()->buildData->insertIntoLookupTable(artifact);
+ product->topLevelProject()->buildData->isDirty = true;
+
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] insert artifact '%1'")
+ .arg(artifact->filePath());
+ }
+}
+
+static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, const Logger &logger)
+{
+ logger.qbsDebug() << "Sanity checking product '" << product->name << "'";
+ CycleDetector cycleDetector(logger);
+ cycleDetector.visitProduct(product);
+ const ProductBuildData * const buildData = product->buildData.data();
+ if (logger.traceEnabled())
+ logger.qbsTrace() << "enabled: " << product->enabled << "; build data: " << buildData;
+ QBS_CHECK(!!product->enabled == !!buildData);
+ if (!product->enabled)
+ return;
+ foreach (Artifact * const ta, buildData->targetArtifacts) {
+ if (logger.traceEnabled())
+ logger.qbsTrace() << "Checking target artifact '" << ta->fileName() << "'.";
+ QBS_CHECK(buildData->artifacts.contains(ta));
+ }
+ QSet<QString> filePaths;
+ foreach (Artifact * const artifact, buildData->artifacts) {
+ logger.qbsDebug() << "Sanity checking artifact '" << artifact->fileName() << "'";
+ QBS_CHECK(!filePaths.contains(artifact->filePath()));
+ filePaths << artifact->filePath();
+ QBS_CHECK(artifact->product == product);
+ foreach (const Artifact * const parent, artifact->parents)
+ QBS_CHECK(parent->children.contains(artifact));
+ foreach (const Artifact * const child, artifact->children)
+ QBS_CHECK(child->parents.contains(artifact));
+ foreach (Artifact * const child, artifact->childrenAddedByScanner)
+ QBS_CHECK(artifact->children.contains(child));
+ const TransformerConstPtr transformer = artifact->transformer;
+ if (artifact->artifactType == Artifact::SourceFile)
+ continue;
+
+ QBS_CHECK(transformer);
+ QBS_CHECK(transformer->outputs.contains(artifact));
+ ArtifactList transformerOutputChildren;
+ foreach (const Artifact * const output, transformer->outputs) {
+ QBS_CHECK(output->transformer == transformer);
+ transformerOutputChildren.unite(output->children);
+ }
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << "The transformer output children are:";
+ foreach (const Artifact * const a, transformerOutputChildren)
+ logger.qbsTrace() << "\t" << a->fileName();
+ logger.qbsTrace() << "The transformer inputs are:";
+ foreach (const Artifact * const a, transformer->inputs)
+ logger.qbsTrace() << "\t" << a->fileName();
+ }
+ QBS_CHECK(transformer->inputs.count() <= transformerOutputChildren.count());
+ foreach (Artifact * const transformerInput, transformer->inputs)
+ QBS_CHECK(transformerOutputChildren.contains(transformerInput));
+ }
+}
+
+static void doSanityChecks(const ResolvedProjectPtr &project, QSet<QString> &productNames,
+ const Logger &logger)
+{
+ logger.qbsDebug() << "Sanity checking project '" << project->name << "'";
+ foreach (const ResolvedProjectPtr &subProject, project->subProjects)
+ doSanityChecks(subProject, productNames, logger);
+
+ foreach (const ResolvedProductConstPtr &product, project->products) {
+ QBS_CHECK(product->project == project);
+ QBS_CHECK(product->topLevelProject() == project->topLevelProject());
+ doSanityChecksForProduct(product, logger);
+ QBS_CHECK(!productNames.contains(product->name));
+ productNames << product->name;
+ }
+}
+
+void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger)
+{
+ QSet<QString> productNames;
+ doSanityChecks(project, productNames, logger);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/buildgraph.h b/src/lib/corelib/buildgraph/buildgraph.h
new file mode 100644
index 000000000..5244894cb
--- /dev/null
+++ b/src/lib/corelib/buildgraph/buildgraph.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_BUILDGRAPH_H
+#define QBS_BUILDGRAPH_H
+
+#include "forward_decls.h"
+#include "rulesapplicator.h"
+
+#include <language/forward_decls.h>
+
+#include <QScriptValue>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+class Logger;
+class ScriptEngine;
+class PrepareScriptObserver;
+
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product,
+ const ProjectBuildData *projectBuildData,
+ const QString &dirPath, const QString &fileName,
+ bool compareByName = false);
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const QString &dirPath,
+ const QString &fileName, bool compareByName = false);
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const ProjectBuildData *buildData,
+ const QString &filePath, bool compareByName = false);
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const QString &filePath,
+ bool compareByName = false);
+Artifact *lookupArtifact(const ResolvedProductConstPtr &product, const Artifact *artifact,
+ bool compareByName);
+
+Artifact *createArtifact(const ResolvedProductPtr &product,
+ const SourceArtifactConstPtr &sourceArtifact, const Logger &logger);
+void insertArtifact(const ResolvedProductPtr &product, Artifact *artifact, const Logger &logger);
+void addTargetArtifacts(const ResolvedProductPtr &product,
+ ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger);
+void dumpProductBuildData(const ResolvedProductConstPtr &product);
+
+
+bool findPath(Artifact *u, Artifact *v, QList<Artifact*> &path);
+void connect(Artifact *p, Artifact *c);
+void loggedConnect(Artifact *u, Artifact *v, const Logger &logger);
+bool safeConnect(Artifact *u, Artifact *v, const Logger &logger);
+void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger);
+void disconnect(Artifact *u, Artifact *v, const Logger &logger);
+
+void setupScriptEngineForFile(ScriptEngine *engine, const ResolvedFileContextConstPtr &fileContext,
+ QScriptValue targetObject);
+void setupScriptEngineForProduct(ScriptEngine *engine, const ResolvedProductConstPtr &product,
+ const RuleConstPtr &rule, QScriptValue targetObject,
+ PrepareScriptObserver *observer = 0);
+QString relativeArtifactFileName(const Artifact *artifact); // Debugging helpers
+
+void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger);
+
+template <typename T>
+QStringList toStringList(const T &artifactContainer)
+{
+ QStringList l;
+ foreach (Artifact *n, artifactContainer)
+ l.append(relativeArtifactFileName(n));
+ return l;
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILDGRAPH_H
diff --git a/src/lib/corelib/buildgraph/buildgraph.pri b/src/lib/corelib/buildgraph/buildgraph.pri
new file mode 100644
index 000000000..fa091f1b3
--- /dev/null
+++ b/src/lib/corelib/buildgraph/buildgraph.pri
@@ -0,0 +1,65 @@
+SOURCES += \
+ $$PWD/abstractcommandexecutor.cpp \
+ $$PWD/artifact.cpp \
+ $$PWD/artifactcleaner.cpp \
+ $$PWD/artifactlist.cpp \
+ $$PWD/artifactvisitor.cpp \
+ $$PWD/automoc.cpp \
+ $$PWD/buildgraph.cpp \
+ $$PWD/buildgraphloader.cpp \
+ $$PWD/command.cpp \
+ $$PWD/cycledetector.cpp \
+ $$PWD/executor.cpp \
+ $$PWD/executorjob.cpp \
+ $$PWD/filedependency.cpp \
+ $$PWD/inputartifactscanner.cpp \
+ $$PWD/jscommandexecutor.cpp \
+ $$PWD/processcommandexecutor.cpp \
+ $$PWD/productbuilddata.cpp \
+ $$PWD/productinstaller.cpp \
+ $$PWD/projectbuilddata.cpp \
+ $$PWD/rulegraph.cpp \
+ $$PWD/rulesapplicator.cpp \
+ $$PWD/rulesevaluationcontext.cpp \
+ $$PWD/scanresultcache.cpp \
+ $$PWD/timestampsupdater.cpp \
+ $$PWD/transformer.cpp
+
+HEADERS += \
+ $$PWD/abstractcommandexecutor.h \
+ $$PWD/artifact.h \
+ $$PWD/artifactcleaner.h \
+ $$PWD/artifactlist.h \
+ $$PWD/artifactvisitor.h \
+ $$PWD/automoc.h \
+ $$PWD/buildgraph.h \
+ $$PWD/buildgraphloader.h \
+ $$PWD/command.h \
+ $$PWD/cycledetector.h \
+ $$PWD/executor.h \
+ $$PWD/executorjob.h \
+ $$PWD/filedependency.h \
+ $$PWD/forward_decls.h \
+ $$PWD/inputartifactscanner.h \
+ $$PWD/jscommandexecutor.h \
+ $$PWD/processcommandexecutor.h \
+ $$PWD/productbuilddata.h \
+ $$PWD/productinstaller.h \
+ $$PWD/projectbuilddata.h \
+ $$PWD/rulegraph.h \
+ $$PWD/rulesapplicator.h \
+ $$PWD/rulesevaluationcontext.h \
+ $$PWD/scanresultcache.h \
+ $$PWD/timestampsupdater.h \
+ $$PWD/transformer.h
+
+all_tests {
+ HEADERS += $$PWD/tst_buildgraph.h
+ SOURCES += $$PWD/tst_buildgraph.cpp
+}
+
+!qbs_no_dev_install {
+ buildgraph_headers.files = $$PWD/forward_decls.h
+ buildgraph_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/buildgraph
+ INSTALLS += buildgraph_headers
+}
diff --git a/src/lib/corelib/buildgraph/buildgraphloader.cpp b/src/lib/corelib/buildgraph/buildgraphloader.cpp
new file mode 100644
index 000000000..b76b33eb6
--- /dev/null
+++ b/src/lib/corelib/buildgraph/buildgraphloader.cpp
@@ -0,0 +1,826 @@
+/****************************************************************************
+**
+** 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 "buildgraphloader.h"
+
+#include "artifact.h"
+#include "artifactlist.h"
+#include "buildgraph.h"
+#include "command.h"
+#include "cycledetector.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "rulesevaluationcontext.h"
+#include "transformer.h"
+#include <language/artifactproperties.h>
+#include <language/language.h>
+#include <language/loader.h>
+#include <logging/translator.h>
+#include <tools/propertyfinder.h>
+#include <tools/qbsassert.h>
+#include <tools/setupprojectparameters.h>
+
+#include <QDir>
+#include <QFileInfo>
+
+namespace qbs {
+namespace Internal {
+
+BuildGraphLoader::BuildGraphLoader(const QProcessEnvironment &env, const Logger &logger) :
+ m_logger(logger), m_environment(env)
+{
+}
+
+BuildGraphLoader::~BuildGraphLoader()
+{
+ qDeleteAll(m_objectsToDelete);
+}
+
+static bool isConfigCompatible(const QVariantMap &cfg1, const QVariantMap &cfg2)
+{
+ if (cfg1.count() != cfg2.count())
+ return false;
+ QVariantMap::const_iterator it = cfg1.begin();
+ for (; it != cfg1.end(); ++it) {
+ if (it.value().type() == QVariant::Map) {
+ if (!isConfigCompatible(it.value().toMap(), cfg2.value(it.key()).toMap()))
+ return false;
+ } else {
+ QVariant value = cfg2.value(it.key());
+ if (value != it.value())
+ return false;
+ }
+ }
+ return true;
+}
+
+static void restoreBackPointers(const ResolvedProjectPtr &project)
+{
+ foreach (const ResolvedProductPtr &product, project->products) {
+ product->project = project;
+ if (!product->buildData)
+ continue;
+ foreach (Artifact * const a, product->buildData->artifacts)
+ project->topLevelProject()->buildData->insertIntoLookupTable(a);
+ }
+
+ foreach (const ResolvedProjectPtr &subProject, project->subProjects) {
+ subProject->parentProject = project;
+ restoreBackPointers(subProject);
+ }
+}
+
+BuildGraphLoadResult BuildGraphLoader::load(const SetupProjectParameters &parameters,
+ const RulesEvaluationContextPtr &evalContext)
+{
+ m_result = BuildGraphLoadResult();
+ m_evalContext = evalContext;
+
+ const QString projectId = TopLevelProject::deriveId(parameters.buildConfigurationTree());
+ const QString buildDir
+ = TopLevelProject::deriveBuildDirectory(parameters.buildRoot(), projectId);
+ const QString buildGraphFilePath
+ = ProjectBuildData::deriveBuildGraphFilePath(buildDir, projectId);
+
+ PersistentPool pool(m_logger);
+ m_logger.qbsDebug() << "[BG] trying to load: " << buildGraphFilePath;
+ try {
+ pool.load(buildGraphFilePath);
+ } catch (const ErrorInfo &loadError) {
+ if (parameters.restoreBehavior() == SetupProjectParameters::RestoreOnly)
+ throw;
+ m_logger.qbsInfo() << loadError.toString();
+ return m_result;
+ }
+
+ const TopLevelProjectPtr project = TopLevelProject::create();
+
+ // TODO: Store some meta data that will enable us to show actual progress (e.g. number of products).
+ evalContext->initializeObserver(Tr::tr("Restoring build graph from disk"), 1);
+
+ project->load(pool);
+ project->buildData->evaluationContext = evalContext;
+
+ if (QFileInfo(project->location.fileName()) != QFileInfo(parameters.projectFilePath())) {
+ QString errorMessage = Tr::tr("Stored build graph at '%1' is for project file '%2', but "
+ "input file is '%3'. ")
+ .arg(QDir::toNativeSeparators(buildGraphFilePath),
+ QDir::toNativeSeparators(project->location.fileName()),
+ QDir::toNativeSeparators(parameters.projectFilePath()));
+ if (!parameters.ignoreDifferentProjectFilePath()) {
+ errorMessage += Tr::tr("Aborting.");
+ throw ErrorInfo(errorMessage);
+ }
+
+ // Okay, let's assume it's the same project anyway (the source dir might have moved).
+ errorMessage += Tr::tr("Ignoring.");
+ m_logger.qbsWarning() << errorMessage;
+ }
+
+ restoreBackPointers(project);
+
+ project->location = CodeLocation(parameters.projectFilePath(), project->location.line(),
+ project->location.column());
+ project->setBuildConfiguration(pool.headData().projectConfig);
+ project->buildDirectory = buildDir;
+ m_result.loadedProject = project;
+ evalContext->incrementProgressValue();
+ doSanityChecks(project, m_logger);
+
+ if (parameters.restoreBehavior() == SetupProjectParameters::RestoreOnly)
+ return m_result;
+ QBS_CHECK(parameters.restoreBehavior() == SetupProjectParameters::RestoreAndTrackChanges);
+
+ trackProjectChanges(parameters, buildGraphFilePath, project, pool.headData().projectConfig);
+ return m_result;
+}
+
+void BuildGraphLoader::trackProjectChanges(const SetupProjectParameters &parameters,
+ const QString &buildGraphFilePath, const TopLevelProjectPtr &restoredProject,
+ const QVariantMap &oldProjectConfig)
+{
+ const FileTime buildGraphTimeStamp = FileInfo(buildGraphFilePath).lastModified();
+ QSet<QString> buildSystemFiles = restoredProject->buildSystemFiles;
+ QList<ResolvedProductPtr> allRestoredProducts = restoredProject->allProducts();
+ QList<ResolvedProductPtr> changedProducts;
+ QList<ResolvedProductPtr> productsWithChangedFiles;
+ bool reResolvingNecessary = false;
+ if (!isConfigCompatible(parameters.finalBuildConfigurationTree(), oldProjectConfig))
+ reResolvingNecessary = true;
+ if (hasProductFileChanged(allRestoredProducts, buildGraphTimeStamp,
+ buildSystemFiles, productsWithChangedFiles)) {
+ reResolvingNecessary = true;
+ }
+
+ // "External" changes, e.g. in the environment or in a JavaScript file,
+ // can make the list of source files in a product change without the respective file
+ // having been touched. In such a case, the build data for that product will have to be set up
+ // anew.
+ if (hasBuildSystemFileChanged(buildSystemFiles, buildGraphTimeStamp)
+ || hasEnvironmentChanged(restoredProject)
+ || hasFileExistsResultChanged(restoredProject)
+ || hasFileLastModifiedResultChanged(restoredProject)) {
+ reResolvingNecessary = true;
+ }
+
+ if (!reResolvingNecessary)
+ return;
+
+ restoredProject->buildData->isDirty = true;
+ Loader ldr(m_evalContext->engine(), m_logger);
+ ldr.setSearchPaths(parameters.searchPaths());
+ ldr.setProgressObserver(m_evalContext->observer());
+ m_result.newlyResolvedProject = ldr.loadProject(parameters);
+
+ QMap<QString, ResolvedProductPtr> freshProductsByName;
+ QList<ResolvedProductPtr> allNewlyResolvedProducts
+ = m_result.newlyResolvedProject->allProducts();
+ foreach (const ResolvedProductPtr &cp, allNewlyResolvedProducts)
+ freshProductsByName.insert(cp->name, cp);
+
+ checkAllProductsForChanges(allRestoredProducts, freshProductsByName, changedProducts,
+ productsWithChangedFiles);
+
+ QSharedPointer<ProjectBuildData> oldBuildData;
+ ChildListHash childLists;
+ if (!changedProducts.isEmpty() || !productsWithChangedFiles.isEmpty()) {
+ oldBuildData = QSharedPointer<ProjectBuildData>(
+ new ProjectBuildData(restoredProject->buildData.data()));
+ foreach (const ResolvedProductConstPtr &product, allRestoredProducts) {
+ if (!product->buildData)
+ continue;
+
+ // If the product gets temporarily removed, its artifacts will get disconnected
+ // and this structural information will no longer be directly available from them.
+ foreach (const Artifact * const a, product->buildData->artifacts)
+ childLists.insert(a, a->children);
+ }
+ }
+
+ // For products with "serious" changes such as different prepare scripts, we set up the
+ // build data from scratch to be on the safe side. This can be made more fine-grained
+ // if needed.
+ foreach (const ResolvedProductPtr &product, changedProducts) {
+ ResolvedProductPtr freshProduct = freshProductsByName.value(product->name);
+ if (!freshProduct)
+ continue;
+ onProductRemoved(product, product->topLevelProject()->buildData.data(), false);
+ allRestoredProducts.removeOne(product);
+ productsWithChangedFiles.removeOne(product);
+ }
+
+ // Move over restored build data to newly resolved project.
+ m_result.newlyResolvedProject->buildData.swap(restoredProject->buildData);
+ QBS_CHECK(m_result.newlyResolvedProject->buildData);
+ m_result.newlyResolvedProject->buildData->isDirty = true;
+ for (int i = allNewlyResolvedProducts.count() - 1; i >= 0; --i) {
+ const ResolvedProductPtr &newlyResolvedProduct = allNewlyResolvedProducts.at(i);
+ for (int j = allRestoredProducts.count() - 1; j >= 0; --j) {
+ const ResolvedProductPtr &restoredProduct = allRestoredProducts.at(j);
+ if (newlyResolvedProduct->name == restoredProduct->name) {
+ if (newlyResolvedProduct->enabled) {
+ newlyResolvedProduct->buildData.swap(restoredProduct->buildData);
+ } else {
+ if (restoredProduct->enabled) {
+ QBS_CHECK(restoredProduct->buildData);
+ foreach (Artifact * const a, newlyResolvedProduct->buildData->artifacts) {
+ const bool removeFromDisk = a->artifactType == Artifact::Generated;
+ newlyResolvedProduct->topLevelProject()->buildData->removeArtifact(a,
+ m_logger, removeFromDisk, true);
+ m_objectsToDelete << a;
+ }
+ }
+ productsWithChangedFiles.removeOne(restoredProduct);
+ }
+ if (newlyResolvedProduct->buildData) {
+ foreach (Artifact * const a, newlyResolvedProduct->buildData->artifacts)
+ a->product = newlyResolvedProduct;
+ }
+
+ // Keep in list if build data still needs to be resolved.
+ if (!newlyResolvedProduct->enabled || newlyResolvedProduct->buildData)
+ allNewlyResolvedProducts.removeAt(i);
+
+ allRestoredProducts.removeAt(j);
+ break;
+ }
+ }
+ }
+
+ // Products still left in the list do not exist anymore.
+ foreach (const ResolvedProductPtr &removedProduct, allRestoredProducts) {
+ onProductRemoved(removedProduct, m_result.newlyResolvedProject->buildData.data());
+ productsWithChangedFiles.removeOne(removedProduct);
+ }
+
+ // Products still left in the list need resolving, either because they are new
+ // or because they are newly enabled.
+ if (!allNewlyResolvedProducts.isEmpty()) {
+ BuildDataResolver bpr(m_logger);
+ bpr.resolveProductBuildDataForExistingProject(m_result.newlyResolvedProject,
+ allNewlyResolvedProducts);
+ }
+
+ // For products where only the list of files has changed, we adapt the existing build data
+ // so we won't recompile existing files just because new ones have been added.
+ foreach (const ResolvedProductPtr &product, productsWithChangedFiles) {
+ ResolvedProductPtr freshProduct = freshProductsByName.value(product->name);
+ if (!freshProduct)
+ continue;
+ onProductFileListChanged(product, freshProduct, oldBuildData.data());
+ }
+
+ foreach (const ResolvedProductConstPtr &changedProduct, changedProducts) {
+ rescueOldBuildData(changedProduct, freshProductsByName.value(changedProduct->name),
+ oldBuildData.data(), childLists);
+ }
+
+ doSanityChecks(m_result.newlyResolvedProject, m_logger);
+}
+
+bool BuildGraphLoader::hasEnvironmentChanged(const TopLevelProjectConstPtr &restoredProject) const
+{
+ for (QHash<QString, QString>::ConstIterator it = restoredProject->usedEnvironment.constBegin();
+ it != restoredProject->usedEnvironment.constEnd(); ++it) {
+ if (m_environment.value(it.key()) != it.value()) {
+ m_logger.qbsDebug() << "A relevant environment variable changed, "
+ "must re-resolve project.";
+ return true;
+ }
+ }
+ return false;
+}
+
+bool BuildGraphLoader::hasFileExistsResultChanged(const TopLevelProjectConstPtr &restoredProject) const
+{
+ for (QHash<QString, bool>::ConstIterator it = restoredProject->fileExistsResults.constBegin();
+ it != restoredProject->fileExistsResults.constEnd(); ++it) {
+ if (FileInfo(it.key()).exists() != it.value()) {
+ m_logger.qbsDebug() << "Existence check for file '" << it.key()
+ << " 'changed, must re-resolve project.";
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool BuildGraphLoader::hasFileLastModifiedResultChanged(const TopLevelProjectConstPtr &restoredProject) const
+{
+ for (QHash<QString, FileTime>::ConstIterator it
+ = restoredProject->fileLastModifiedResults.constBegin();
+ it != restoredProject->fileLastModifiedResults.constEnd(); ++it) {
+ if (FileInfo(it.key()).lastModified() != it.value()) {
+ m_logger.qbsDebug() << "Timestamp for file '" << it.key()
+ << " 'changed, must re-resolve project.";
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool BuildGraphLoader::hasProductFileChanged(const QList<ResolvedProductPtr> &restoredProducts,
+ const FileTime &referenceTime, QSet<QString> &remainingBuildSystemFiles,
+ QList<ResolvedProductPtr> &productsWithChangedFiles)
+{
+ bool hasChanged = false;
+ foreach (const ResolvedProductPtr &product, restoredProducts) {
+ const QString fileName = product->location.fileName();
+ const FileInfo pfi(fileName);
+ remainingBuildSystemFiles.remove(fileName);
+ if (!pfi.exists()) {
+ m_logger.qbsDebug() << "A product was removed, must re-resolve project";
+ hasChanged = true;
+ } else if (referenceTime < pfi.lastModified()) {
+ m_logger.qbsDebug() << "A product was changed, must re-resolve project";
+ hasChanged = true;
+ } else if (!productsWithChangedFiles.contains(product)) {
+ foreach (const GroupPtr &group, product->groups) {
+ if (!group->wildcards)
+ continue;
+ const QSet<QString> files
+ = group->wildcards->expandPatterns(group, product->sourceDirectory);
+ QSet<QString> wcFiles;
+ foreach (const SourceArtifactConstPtr &sourceArtifact, group->wildcards->files)
+ wcFiles += sourceArtifact->absoluteFilePath;
+ if (files == wcFiles)
+ continue;
+ hasChanged = true;
+ productsWithChangedFiles += product;
+ break;
+ }
+ }
+ }
+
+ return hasChanged;
+}
+
+bool BuildGraphLoader::hasBuildSystemFileChanged(const QSet<QString> &buildSystemFiles,
+ const FileTime &referenceTime)
+{
+ foreach (const QString &file, buildSystemFiles) {
+ const FileInfo fi(file);
+ if (!fi.exists() || referenceTime < fi.lastModified()) {
+ m_logger.qbsDebug() << "A qbs or js file changed, must re-resolve project.";
+ return true;
+ }
+ }
+ return false;
+}
+
+void BuildGraphLoader::checkAllProductsForChanges(const QList<ResolvedProductPtr> &restoredProducts,
+ const QMap<QString, ResolvedProductPtr> &newlyResolvedProductsByName,
+ QList<ResolvedProductPtr> &changedProducts,
+ QList<ResolvedProductPtr> &productsWithChangedFiles)
+{
+ foreach (const ResolvedProductPtr &restoredProduct, restoredProducts) {
+ if (changedProducts.contains(restoredProduct))
+ continue;
+ const ResolvedProductPtr newlyResolvedProduct
+ = newlyResolvedProductsByName.value(restoredProduct->name);
+ if (!newlyResolvedProduct)
+ continue;
+ if (!productsWithChangedFiles.contains(restoredProduct)
+ && !sourceArtifactListsAreEqual(restoredProduct->allFiles(),
+ newlyResolvedProduct->allFiles())) {
+ m_logger.qbsDebug() << "File list of product '" << restoredProduct->name
+ << "' was changed.";
+ productsWithChangedFiles += restoredProduct;
+ }
+ if (checkProductForChanges(restoredProduct, newlyResolvedProduct)) {
+ m_logger.qbsDebug() << "Product '" << restoredProduct->name
+ << "' was changed, must set up build data from scratch";
+ changedProducts << restoredProduct;
+ }
+ }
+}
+
+static bool dependenciesAreEqual(const ResolvedProductConstPtr &p1,
+ const ResolvedProductConstPtr &p2)
+{
+ if (p1->dependencies.count() != p2->dependencies.count())
+ return false;
+ QSet<QString> names1;
+ QSet<QString> names2;
+ foreach (const ResolvedProductConstPtr &dep, p1->dependencies)
+ names1 << dep->name;
+ foreach (const ResolvedProductConstPtr &dep, p2->dependencies)
+ names2 << dep->name;
+ return names1 == names2;
+}
+
+bool BuildGraphLoader::checkProductForChanges(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct)
+{
+ // This check must come first, as it can prevent build data rescuing as a side effect.
+ // TODO: Similar special checks must be done for qbs.getEnv() and File.exists() in
+ // commands (or possibly it could be reasonable to just forbid such "dynamic" constructs
+ // within commands).
+ if (checkForPropertyChanges(restoredProduct, newlyResolvedProduct))
+ return true;
+
+ return !transformerListsAreEqual(restoredProduct->transformers,
+ newlyResolvedProduct->transformers)
+ || !ruleListsAreEqual(restoredProduct->rules.toList(),
+ newlyResolvedProduct->rules.toList())
+ || !dependenciesAreEqual(restoredProduct, newlyResolvedProduct);
+}
+
+bool BuildGraphLoader::checkProductForInstallInfoChanges(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct)
+{
+ // These are not requested from rules at build time, but we still need to take
+ // them into account.
+ const QStringList specialProperties = QStringList() << QLatin1String("install")
+ << QLatin1String("installDir") << QLatin1String("installPrefix");
+ foreach (const QString &key, specialProperties) {
+ if (restoredProduct->properties->qbsPropertyValue(key)
+ != newlyResolvedProduct->properties->qbsPropertyValue(key)) {
+ m_logger.qbsDebug() << "Product property 'qbs." << key << "' changed.";
+ return true;
+ }
+ }
+ return false;
+}
+
+bool BuildGraphLoader::checkForPropertyChanges(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct)
+{
+ m_logger.qbsDebug() << "Checking for changes in properties requested in prepare scripts for "
+ "product '" << restoredProduct->name << "'.";
+ if (!restoredProduct->buildData)
+ return false;
+
+ // This check must come first, as it can prevent build data rescuing.
+ if (checkTransformersForPropertyChanges(restoredProduct, newlyResolvedProduct))
+ return true;
+
+ if (checkProductForInstallInfoChanges(restoredProduct, newlyResolvedProduct))
+ return true;
+ if (!artifactPropertyListsAreEqual(restoredProduct->artifactProperties,
+ newlyResolvedProduct->artifactProperties)) {
+ return true;
+ }
+ return false;
+}
+
+bool BuildGraphLoader::checkTransformersForPropertyChanges(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct)
+{
+ bool transformerChanges = false;
+ QSet<TransformerConstPtr> seenTransformers;
+ foreach (Artifact * const artifact, restoredProduct->buildData->artifacts) {
+ const TransformerPtr transformer = artifact->transformer;
+ if (!transformer || seenTransformers.contains(transformer))
+ continue;
+ seenTransformers.insert(transformer);
+ if (checkForPropertyChanges(transformer, newlyResolvedProduct))
+ transformerChanges = true;
+ }
+ if (transformerChanges) {
+ m_logger.qbsDebug() << "Property changes in product '"
+ << newlyResolvedProduct->name << "'.";
+ }
+ return transformerChanges;
+}
+
+void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product,
+ ProjectBuildData *projectBuildData, bool removeArtifactsFromDisk)
+{
+ m_logger.qbsDebug() << "[BG] product '" << product->name << "' removed.";
+
+ product->project->products.removeOne(product);
+ if (product->buildData) {
+ foreach (Artifact *artifact, product->buildData->artifacts)
+ projectBuildData->removeArtifact(artifact, m_logger, removeArtifactsFromDisk, false);
+ }
+}
+
+void BuildGraphLoader::onProductFileListChanged(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct, const ProjectBuildData *oldBuildData)
+{
+ m_logger.qbsDebug() << "[BG] product '" << restoredProduct->name << "' changed.";
+
+ QBS_CHECK(newlyResolvedProduct->enabled);
+
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+ QList<Artifact *> addedArtifacts;
+ ArtifactList artifactsToRemove;
+ QHash<QString, SourceArtifactConstPtr> oldArtifacts, newArtifacts;
+
+ const QList<SourceArtifactPtr> restoredProductAllFiles = restoredProduct->allEnabledFiles();
+ foreach (const SourceArtifactConstPtr &a, restoredProductAllFiles)
+ oldArtifacts.insert(a->absoluteFilePath, a);
+ foreach (const SourceArtifactPtr &a, newlyResolvedProduct->allEnabledFiles()) {
+ newArtifacts.insert(a->absoluteFilePath, a);
+ if (!oldArtifacts.contains(a->absoluteFilePath)) {
+ // artifact added
+ m_logger.qbsDebug() << "[BG] artifact '" << a->absoluteFilePath
+ << "' added to product " << restoredProduct->name;
+ Artifact *newArtifact = lookupArtifact(newlyResolvedProduct, oldBuildData,
+ a->absoluteFilePath, true);
+ if (newArtifact) {
+ // User added a source file that was a generated artifact in the previous
+ // build, e.g. a C++ source file that was generated and now is a non-generated
+ // source file.
+ newArtifact->artifactType = Artifact::SourceFile;
+ } else {
+ newArtifact = createArtifact(newlyResolvedProduct, a, m_logger);
+ foreach (FileResourceBase *oldArtifactLookupResult,
+ oldBuildData->lookupFiles(newArtifact->filePath())) {
+ if (oldArtifactLookupResult == newArtifact)
+ continue;
+ FileDependency *oldFileDependency
+ = dynamic_cast<FileDependency *>(oldArtifactLookupResult);
+ if (!oldFileDependency) {
+ // The source file already exists in another product.
+ continue;
+ }
+ // User added a source file that was recognized as file dependency in the
+ // previous build, e.g. a C++ header file.
+ replaceFileDependencyWithArtifact(newlyResolvedProduct,
+ oldFileDependency,
+ newArtifact);
+ }
+ }
+ addedArtifacts += newArtifact;
+ }
+ }
+
+ foreach (const SourceArtifactPtr &a, restoredProductAllFiles) {
+ const SourceArtifactConstPtr changedArtifact = newArtifacts.value(a->absoluteFilePath);
+ if (!changedArtifact) {
+ // artifact removed
+ m_logger.qbsDebug() << "[BG] artifact '" << a->absoluteFilePath
+ << "' removed from product " << restoredProduct->name;
+ Artifact *artifact
+ = lookupArtifact(restoredProduct, oldBuildData, a->absoluteFilePath, true);
+ QBS_CHECK(artifact);
+ newlyResolvedProduct->topLevelProject()->buildData
+ ->removeArtifactAndExclusiveDependents(artifact, m_logger, true,
+ &artifactsToRemove);
+ continue;
+ }
+
+ // TODO: overrideFileTags and properties have to be checked for changes as well.
+ if (changedArtifact->fileTags != a->fileTags) {
+ // artifact's filetags have changed
+ m_logger.qbsDebug() << "[BG] filetags have changed for artifact '"
+ << a->absoluteFilePath << "' from " << a->fileTags << " to "
+ << changedArtifact->fileTags;
+ Artifact *artifact
+ = lookupArtifact(restoredProduct, oldBuildData, a->absoluteFilePath, true);
+ QBS_CHECK(artifact);
+
+ // handle added filetags
+ foreach (const FileTag &addedFileTag, changedArtifact->fileTags - a->fileTags) {
+ artifact->fileTags += addedFileTag;
+ artifactsPerFileTag[addedFileTag] += artifact;
+ }
+
+ // handle removed filetags
+ foreach (const FileTag &removedFileTag, a->fileTags - changedArtifact->fileTags) {
+ artifact->fileTags -= removedFileTag;
+ foreach (Artifact *parent, artifact->parents) {
+ if (parent->transformer && parent->transformer->rule->inputs.contains(removedFileTag)) {
+ // this parent has been created because of the removed filetag
+ newlyResolvedProduct->topLevelProject()->buildData
+ ->removeArtifactAndExclusiveDependents(parent, m_logger, true,
+ &artifactsToRemove);
+ }
+ }
+ }
+ }
+ }
+
+ // apply rules for new artifacts
+ foreach (Artifact *artifact, addedArtifacts)
+ foreach (const FileTag &ft, artifact->fileTags)
+ artifactsPerFileTag[ft] += artifact;
+ RulesApplicator(newlyResolvedProduct, artifactsPerFileTag, m_logger).applyAllRules();
+
+ addTargetArtifacts(newlyResolvedProduct, artifactsPerFileTag, m_logger);
+
+ // parents of removed artifacts must update their transformers
+ foreach (Artifact *removedArtifact, artifactsToRemove)
+ foreach (Artifact *parent, removedArtifact->parents)
+ newlyResolvedProduct->topLevelProject()->buildData->artifactsThatMustGetNewTransformers += parent;
+ newlyResolvedProduct->topLevelProject()->buildData->updateNodesThatMustGetNewTransformer(m_logger);
+
+ // defer destruction of removed artifacts
+ foreach (Artifact *artifact, artifactsToRemove)
+ m_objectsToDelete << artifact;
+}
+
+static SourceArtifactConstPtr findSourceArtifact(const ResolvedProductConstPtr &product,
+ const QString &artifactFilePath, QMap<QString, SourceArtifactConstPtr> &artifactMap)
+{
+ SourceArtifactConstPtr &artifact = artifactMap[artifactFilePath];
+ if (!artifact) {
+ foreach (const SourceArtifactConstPtr &a, product->allFiles()) {
+ if (a->absoluteFilePath == artifactFilePath) {
+ artifact = a;
+ break;
+ }
+ }
+ }
+ return artifact;
+}
+
+bool BuildGraphLoader::checkForPropertyChanges(const TransformerPtr &restoredTrafo,
+ const ResolvedProductPtr &freshProduct)
+{
+ // This check must come first, as it can prevent build data rescuing.
+ foreach (const Property &property, restoredTrafo->propertiesRequestedInCommands) {
+ const QVariantMap properties = property.kind == Property::PropertyInProject
+ ? freshProduct->project->projectProperties() : freshProduct->properties->value();
+ if (checkForPropertyChange(property, properties)) {
+ JavaScriptCommand * const pseudoCommand = new JavaScriptCommand;
+ pseudoCommand->setSourceCode(QLatin1String("random stuff that will cause "
+ "commandsEqual() to fail"));
+ restoredTrafo->commands << pseudoCommand;
+ return true;
+ }
+ }
+
+ foreach (const Property &property, restoredTrafo->propertiesRequestedInPrepareScript) {
+ const QVariantMap properties = property.kind == Property::PropertyInProject
+ ? freshProduct->project->projectProperties() : freshProduct->properties->value();
+ if (checkForPropertyChange(property, properties))
+ return true;
+ }
+
+ QMap<QString, SourceArtifactConstPtr> artifactMap;
+ for (QHash<QString, PropertyList>::ConstIterator it =
+ restoredTrafo->propertiesRequestedFromArtifactInPrepareScript.constBegin();
+ it != restoredTrafo->propertiesRequestedFromArtifactInPrepareScript.constEnd(); ++it) {
+ const SourceArtifactConstPtr artifact
+ = findSourceArtifact(freshProduct, it.key(), artifactMap);
+ if (!artifact)
+ continue;
+ foreach (const Property &property, it.value()) {
+ if (checkForPropertyChange(property, artifact->properties->value()))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool BuildGraphLoader::checkForPropertyChange(const Property &restoredProperty,
+ const QVariantMap &newProperties)
+{
+ PropertyFinder finder;
+ QVariant v;
+ switch (restoredProperty.kind) {
+ case Property::PropertyInProduct:
+ case Property::PropertyInProject:
+ v = newProperties.value(restoredProperty.propertyName);
+ break;
+ case Property::PropertyInModule:
+ if (restoredProperty.value.type() == QVariant::List) {
+ v = finder.propertyValues(newProperties, restoredProperty.moduleName,
+ restoredProperty.propertyName);
+ } else {
+ v = finder.propertyValue(newProperties, restoredProperty.moduleName,
+ restoredProperty.propertyName);
+ }
+ break;
+ }
+ if (restoredProperty.value != v) {
+ m_logger.qbsDebug() << "Value for property '" << restoredProperty.moduleName << "."
+ << restoredProperty.propertyName << "' has changed.";
+ m_logger.qbsDebug() << "Old value was '" << restoredProperty.value << "'.";
+ m_logger.qbsDebug() << "New value is '" << v << "'.";
+ return true;
+ }
+ return false;
+}
+
+void BuildGraphLoader::replaceFileDependencyWithArtifact(const ResolvedProductPtr &fileDepProduct,
+ FileDependency *filedep, Artifact *artifact)
+{
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace()
+ << QString::fromLocal8Bit("[BG] replace file dependency '%1' "
+ "with artifact of type '%2'")
+ .arg(filedep->filePath()).arg(artifact->artifactType);
+ }
+ foreach (const ResolvedProductPtr &product, fileDepProduct->topLevelProject()->allProducts()) {
+ if (!product->buildData)
+ continue;
+ foreach (Artifact *artifactInProduct, product->buildData->artifacts) {
+ if (artifactInProduct->fileDependencies.contains(filedep)) {
+ artifactInProduct->fileDependencies.remove(filedep);
+ loggedConnect(artifactInProduct, artifact, m_logger);
+ }
+ }
+ }
+ fileDepProduct->topLevelProject()->buildData->fileDependencies.remove(filedep);
+ fileDepProduct->topLevelProject()->buildData->removeFromLookupTable(filedep);
+ m_objectsToDelete << filedep;
+}
+
+static bool commandsEqual(const TransformerConstPtr &t1, const TransformerConstPtr &t2)
+{
+ if (t1->commands.count() != t2->commands.count())
+ return false;
+ for (int i = 0; i < t1->commands.count(); ++i)
+ if (!t1->commands.at(i)->equals(t2->commands.at(i)))
+ return false;
+ return true;
+}
+
+/**
+ * Rescues the following data from the restoredProduct to newlyResolvedProduct:
+ * - dependencies between artifacts,
+ * - time stamps of artifacts, if their commands have not changed.
+ */
+void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct,
+ const ProjectBuildData *oldBuildData, const ChildListHash &childLists)
+{
+ if (!restoredProduct->enabled || !newlyResolvedProduct->enabled)
+ return;
+
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] rescue data of "
+ "product '%1'").arg(restoredProduct->name);
+ }
+
+ foreach (Artifact *artifact, newlyResolvedProduct->buildData->artifacts) {
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] artifact '%1'")
+ .arg(artifact->fileName());
+ }
+
+ Artifact * const oldArtifact = lookupArtifact(restoredProduct, oldBuildData,
+ artifact->dirPath(), artifact->fileName(), true);
+ if (!oldArtifact || !oldArtifact->transformer) {
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] no transformer data");
+ continue;
+ }
+
+ if (artifact->transformer
+ && !commandsEqual(artifact->transformer, oldArtifact->transformer)) {
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] artifact invalidated");
+ removeGeneratedArtifactFromDisk(oldArtifact, m_logger);
+ continue;
+ }
+ artifact->setTimestamp(oldArtifact->timestamp());
+
+ foreach (Artifact * const oldChild, childLists.value(oldArtifact)) {
+ foreach (FileResourceBase *childFileRes,
+ newlyResolvedProduct->topLevelProject()->buildData->lookupFiles(oldChild)) {
+ Artifact * const child = dynamic_cast<Artifact *>(childFileRes);
+ if (child && !artifact->children.contains(child))
+ safeConnect(artifact, child, m_logger);
+ }
+ }
+ }
+}
+
+void addTargetArtifacts(const ResolvedProductPtr &product,
+ ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger)
+{
+ foreach (const FileTag &fileTag, product->fileTags) {
+ foreach (Artifact * const artifact, artifactsPerFileTag.value(fileTag)) {
+ if (artifact->artifactType == Artifact::Generated)
+ product->buildData->targetArtifacts += artifact;
+ }
+ }
+ if (product->buildData->targetArtifacts.isEmpty()) {
+ const QString msg = QString::fromLocal8Bit("No artifacts generated for product '%1'.");
+ logger.qbsDebug() << msg.arg(product->name);
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/buildgraphloader.h b/src/lib/corelib/buildgraph/buildgraphloader.h
new file mode 100644
index 000000000..f7f7b79e3
--- /dev/null
+++ b/src/lib/corelib/buildgraph/buildgraphloader.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_BUILDGRAPHLOADER_H
+#define QBS_BUILDGRAPHLOADER_H
+
+#include "forward_decls.h"
+
+#include <buildgraph/artifactlist.h>
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+
+#include <QProcessEnvironment>
+#include <QVariantMap>
+
+namespace qbs {
+class SetupProjectParameters;
+
+namespace Internal {
+class FileDependency;
+class FileResourceBase;
+class FileTime;
+class Property;
+
+class BuildGraphLoadResult
+{
+public:
+ TopLevelProjectPtr newlyResolvedProject;
+ TopLevelProjectPtr loadedProject;
+};
+
+
+class BuildGraphLoader
+{
+public:
+ BuildGraphLoader(const QProcessEnvironment &env, const Logger &logger);
+ ~BuildGraphLoader();
+
+ BuildGraphLoadResult load(const SetupProjectParameters &parameters,
+ const RulesEvaluationContextPtr &evalContext);
+
+private:
+ void trackProjectChanges(const SetupProjectParameters &parameters,
+ const QString &buildGraphFilePath,
+ const TopLevelProjectPtr &restoredProject,
+ const QVariantMap &oldProjectConfig);
+ bool hasEnvironmentChanged(const TopLevelProjectConstPtr &restoredProject) const;
+ bool hasFileExistsResultChanged(const TopLevelProjectConstPtr &restoredProject) const;
+ bool hasFileLastModifiedResultChanged(const TopLevelProjectConstPtr &restoredProject) const;
+ bool hasProductFileChanged(const QList<ResolvedProductPtr> &restoredProducts,
+ const FileTime &referenceTime,
+ QSet<QString> &remainingBuildSystemFiles,
+ QList<ResolvedProductPtr> &productsWithChangedFiles);
+ bool hasBuildSystemFileChanged(const QSet<QString> &buildSystemFiles,
+ const FileTime &referenceTime);
+ void checkAllProductsForChanges(const QList<ResolvedProductPtr> &restoredProducts,
+ const QMap<QString, ResolvedProductPtr> &newlyResolvedProductsByName,
+ QList<ResolvedProductPtr> &changedProducts,
+ QList<ResolvedProductPtr> &productsWithChangedFiles);
+ bool checkProductForChanges(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct);
+ bool checkProductForInstallInfoChanges(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct);
+ bool checkForPropertyChanges(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct);
+ bool checkTransformersForPropertyChanges(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct);
+ void onProductRemoved(const ResolvedProductPtr &product, ProjectBuildData *projectBuildData,
+ bool removeArtifactsFromDisk = true);
+ void onProductFileListChanged(const ResolvedProductPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct, const ProjectBuildData *oldBuildData);
+ void removeArtifactAndExclusiveDependents(Artifact *artifact,
+ ArtifactList *removedArtifacts = 0);
+ bool checkForPropertyChanges(const TransformerPtr &restoredTrafo,
+ const ResolvedProductPtr &freshProduct);
+ bool checkForPropertyChange(const Property &restoredProperty,
+ const QVariantMap &newProperties);
+ void replaceFileDependencyWithArtifact(const ResolvedProductPtr &fileDepProduct,
+ FileDependency *filedep, Artifact *artifact);
+
+ typedef QHash<const Artifact *, ArtifactList> ChildListHash;
+ void rescueOldBuildData(const ResolvedProductConstPtr &restoredProduct,
+ const ResolvedProductPtr &newlyResolvedProduct,
+ const ProjectBuildData *oldBuildData,
+ const ChildListHash &childLists);
+
+ RulesEvaluationContextPtr m_evalContext;
+ BuildGraphLoadResult m_result;
+ Logger m_logger;
+ QProcessEnvironment m_environment;
+
+ // These must only be deleted at the end so we can still peek into the old look-up table.
+ QList<FileResourceBase *> m_objectsToDelete;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/buildgraph/command.cpp b/src/lib/corelib/buildgraph/command.cpp
new file mode 100644
index 000000000..8e30906ee
--- /dev/null
+++ b/src/lib/corelib/buildgraph/command.cpp
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** 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 "command.h"
+#include <logging/translator.h>
+#include <tools/qbsassert.h>
+#include <tools/hostosinfo.h>
+
+#include <QScriptEngine>
+#include <QScriptValueIterator>
+#include <QSet>
+
+namespace qbs {
+namespace Internal {
+
+AbstractCommand::AbstractCommand()
+ : m_description(defaultDescription()),
+ m_highlight(defaultHighLight()),
+ m_silent(defaultIsSilent())
+{
+}
+
+AbstractCommand::~AbstractCommand()
+{
+}
+
+AbstractCommand *AbstractCommand::createByType(AbstractCommand::CommandType commandType)
+{
+ switch (commandType) {
+ case AbstractCommand::ProcessCommandType:
+ return new ProcessCommand;
+ case AbstractCommand::JavaScriptCommandType:
+ return new JavaScriptCommand;
+ }
+ qFatal("%s: Invalid command type %d", Q_FUNC_INFO, commandType);
+ return 0;
+}
+
+bool AbstractCommand::equals(const AbstractCommand *other) const
+{
+ return m_description == other->m_description && m_highlight == other->m_highlight
+ && m_silent == other->m_silent && type() == other->type();
+}
+
+void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation)
+{
+ m_description = scriptValue->property("description").toString();
+ m_highlight = scriptValue->property("highlight").toString();
+ m_silent = scriptValue->property("silent").toBool();
+ m_codeLocation = codeLocation;
+}
+
+void AbstractCommand::load(QDataStream &s)
+{
+ s >> m_description >> m_highlight >> m_silent >> m_codeLocation;
+}
+
+void AbstractCommand::store(QDataStream &s)
+{
+ s << m_description << m_highlight << m_silent << m_codeLocation;
+}
+
+static QScriptValue js_CommandBase(QScriptContext *context, QScriptEngine *engine)
+{
+ QScriptValue cmd = context->thisObject();
+ QBS_ASSERT(context->isCalledAsConstructor(), cmd = engine->newObject());
+ cmd.setProperty("description", engine->toScriptValue(AbstractCommand::defaultDescription()));
+ cmd.setProperty("highlight", engine->toScriptValue(AbstractCommand::defaultHighLight()));
+ cmd.setProperty("silent", engine->toScriptValue(AbstractCommand::defaultIsSilent()));
+ return cmd;
+}
+
+static QScriptValue js_Command(QScriptContext *context, QScriptEngine *engine)
+{
+ if (Q_UNLIKELY(!context->isCalledAsConstructor()))
+ return context->throwError(Tr::tr("Command constructor called without new."));
+
+ static ProcessCommand commandPrototype;
+
+ QScriptValue program = context->argument(0);
+ if (program.isUndefined())
+ program = engine->toScriptValue(commandPrototype.program());
+ QScriptValue arguments = context->argument(1);
+ if (arguments.isUndefined())
+ arguments = engine->toScriptValue(commandPrototype.arguments());
+ QScriptValue cmd = js_CommandBase(context, engine);
+ cmd.setProperty("className", engine->toScriptValue(QString("Command")));
+ cmd.setProperty("program", program);
+ cmd.setProperty("arguments", arguments);
+ cmd.setProperty("workingDir", engine->toScriptValue(commandPrototype.workingDir()));
+ cmd.setProperty("maxExitCode", engine->toScriptValue(commandPrototype.maxExitCode()));
+ cmd.setProperty("stdoutFilterFunction", engine->toScriptValue(commandPrototype.stdoutFilterFunction()));
+ cmd.setProperty("stderrFilterFunction", engine->toScriptValue(commandPrototype.stderrFilterFunction()));
+ cmd.setProperty("responseFileThreshold", engine->toScriptValue(commandPrototype.responseFileThreshold()));
+ cmd.setProperty("responseFileUsagePrefix", engine->toScriptValue(commandPrototype.responseFileUsagePrefix()));
+ cmd.setProperty("environment", engine->toScriptValue(commandPrototype.environment().toStringList()));
+ return cmd;
+}
+
+
+void ProcessCommand::setupForJavaScript(QScriptValue targetObject)
+{
+ QBS_CHECK(targetObject.isObject());
+ QScriptValue ctor = targetObject.engine()->newFunction(js_Command, 2);
+ targetObject.setProperty("Command", ctor);
+}
+
+ProcessCommand::ProcessCommand()
+ : m_maxExitCode(0)
+ , m_responseFileThreshold(HostOsInfo::isWindowsHost() ? 32000 : -1)
+{
+}
+
+void ProcessCommand::getEnvironmentFromList(const QStringList &envList)
+{
+ m_environment.clear();
+ foreach (const QString &env, envList) {
+ const int equalsIndex = env.indexOf(QLatin1Char('='));
+ if (equalsIndex <= 0 || equalsIndex == env.count() - 1)
+ continue;
+ const QString &var = env.left(equalsIndex);
+ const QString &value = env.mid(equalsIndex + 1);
+ m_environment.insert(var, value);
+ }
+}
+
+bool ProcessCommand::equals(const AbstractCommand *otherAbstractCommand) const
+{
+ if (!AbstractCommand::equals(otherAbstractCommand))
+ return false;
+ const ProcessCommand * const other = static_cast<const ProcessCommand *>(otherAbstractCommand);
+ return m_program == other->m_program
+ && m_arguments == other->m_arguments
+ && m_workingDir == other->m_workingDir
+ && m_maxExitCode == other->m_maxExitCode
+ && m_stdoutFilterFunction == other->m_stdoutFilterFunction
+ && m_stderrFilterFunction == other->m_stderrFilterFunction
+ && m_responseFileThreshold == other->m_responseFileThreshold
+ && m_responseFileUsagePrefix == other->m_responseFileUsagePrefix
+ && m_environment == other->m_environment;
+}
+
+void ProcessCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation)
+{
+ AbstractCommand::fillFromScriptValue(scriptValue, codeLocation);
+ m_program = scriptValue->property("program").toString();
+ m_arguments = scriptValue->property("arguments").toVariant().toStringList();
+ m_workingDir = scriptValue->property("workingDirectory").toString();
+ m_maxExitCode = scriptValue->property("maxExitCode").toInt32();
+ m_stdoutFilterFunction = scriptValue->property("stdoutFilterFunction").toString();
+ m_stderrFilterFunction = scriptValue->property("stderrFilterFunction").toString();
+ m_responseFileThreshold = scriptValue->property("responseFileThreshold").toInt32();
+ m_responseFileUsagePrefix = scriptValue->property("responseFileUsagePrefix").toString();
+ QStringList envList = scriptValue->property(QLatin1String("environment")).toVariant()
+ .toStringList();
+ getEnvironmentFromList(envList);
+}
+
+void ProcessCommand::load(QDataStream &s)
+{
+ AbstractCommand::load(s);
+ QStringList envList;
+ s >> m_program
+ >> m_arguments
+ >> envList
+ >> m_workingDir
+ >> m_maxExitCode
+ >> m_stdoutFilterFunction
+ >> m_stderrFilterFunction
+ >> m_responseFileThreshold
+ >> m_responseFileUsagePrefix;
+ getEnvironmentFromList(envList);
+}
+
+void ProcessCommand::store(QDataStream &s)
+{
+ AbstractCommand::store(s);
+ s << m_program
+ << m_arguments
+ << m_environment.toStringList()
+ << m_workingDir
+ << m_maxExitCode
+ << m_stdoutFilterFunction
+ << m_stderrFilterFunction
+ << m_responseFileThreshold
+ << m_responseFileUsagePrefix;
+}
+
+static QScriptValue js_JavaScriptCommand(QScriptContext *context, QScriptEngine *engine)
+{
+ if (Q_UNLIKELY(!context->isCalledAsConstructor()))
+ return context->throwError(Tr::tr("JavaScriptCommand constructor called without new."));
+ if (Q_UNLIKELY(context->argumentCount() != 0)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ "JavaScriptCommand c'tor doesn't take arguments.");
+ }
+
+ static JavaScriptCommand commandPrototype;
+ QScriptValue cmd = js_CommandBase(context, engine);
+ cmd.setProperty("className", engine->toScriptValue(QString("JavaScriptCommand")));
+ cmd.setProperty("sourceCode", engine->toScriptValue(commandPrototype.sourceCode()));
+ return cmd;
+}
+
+void JavaScriptCommand::setupForJavaScript(QScriptValue targetObject)
+{
+ QBS_CHECK(targetObject.isObject());
+ QScriptValue ctor = targetObject.engine()->newFunction(js_JavaScriptCommand, 0);
+ targetObject.setProperty("JavaScriptCommand", ctor);
+}
+
+JavaScriptCommand::JavaScriptCommand()
+{
+}
+
+bool JavaScriptCommand::equals(const AbstractCommand *otherAbstractCommand) const
+{
+ if (!AbstractCommand::equals(otherAbstractCommand))
+ return false;
+ const JavaScriptCommand * const other
+ = static_cast<const JavaScriptCommand *>(otherAbstractCommand);
+ return m_sourceCode == other->m_sourceCode
+ && m_properties == other->m_properties;
+}
+
+void JavaScriptCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation)
+{
+ AbstractCommand::fillFromScriptValue(scriptValue, codeLocation);
+ QScriptValue sourceCode = scriptValue->property("sourceCode");
+ if (sourceCode.isFunction())
+ m_sourceCode = "(" + sourceCode.toString() + ")()";
+ else
+ m_sourceCode = sourceCode.toString();
+ static QSet<QString> predefinedProperties = QSet<QString>()
+ << "description" << "highlight" << "silent" << "className" << "sourceCode";
+
+ QScriptValueIterator it(*scriptValue);
+ while (it.hasNext()) {
+ it.next();
+ if (predefinedProperties.contains(it.name()))
+ continue;
+ m_properties.insert(it.name(), it.value().toVariant());
+ }
+}
+
+void JavaScriptCommand::load(QDataStream &s)
+{
+ AbstractCommand::load(s);
+ s >> m_sourceCode
+ >> m_properties;
+}
+
+void JavaScriptCommand::store(QDataStream &s)
+{
+ AbstractCommand::store(s);
+ s << m_sourceCode
+ << m_properties;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/command.h b/src/lib/corelib/buildgraph/command.h
new file mode 100644
index 000000000..69fa32299
--- /dev/null
+++ b/src/lib/corelib/buildgraph/command.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_COMMAND_H
+#define QBS_COMMAND_H
+
+#include <tools/codelocation.h>
+
+#include <QProcessEnvironment>
+#include <QStringList>
+#include <QVariantMap>
+#include <QScriptValue>
+
+namespace qbs {
+namespace Internal {
+
+class AbstractCommand
+{
+public:
+ virtual ~AbstractCommand();
+
+ enum CommandType {
+ ProcessCommandType,
+ JavaScriptCommandType
+ };
+
+ static AbstractCommand *createByType(CommandType commandType);
+ static QString defaultDescription() { return QString(); }
+ static QString defaultHighLight() { return QString(); }
+ static bool defaultIsSilent() { return false; }
+
+ virtual CommandType type() const = 0;
+ virtual bool equals(const AbstractCommand *other) const;
+ virtual void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation);
+ virtual void load(QDataStream &s);
+ virtual void store(QDataStream &s);
+
+ const QString description() const { return m_description; }
+ const QString highlight() const { return m_highlight; }
+ bool isSilent() const { return m_silent; }
+ CodeLocation codeLocation() const { return m_codeLocation; }
+
+protected:
+ AbstractCommand();
+
+private:
+ QString m_description;
+ QString m_highlight;
+ bool m_silent;
+ CodeLocation m_codeLocation;
+};
+
+class ProcessCommand : public AbstractCommand
+{
+public:
+ static void setupForJavaScript(QScriptValue targetObject);
+
+ ProcessCommand();
+
+ CommandType type() const { return ProcessCommandType; }
+ bool equals(const AbstractCommand *otherAbstractCommand) const;
+ void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation);
+ void load(QDataStream &s);
+ void store(QDataStream &s);
+
+ const QString program() const { return m_program; }
+ const QStringList arguments() const { return m_arguments; }
+ const QString workingDir() const { return m_workingDir; }
+ int maxExitCode() const { return m_maxExitCode; }
+ QString stdoutFilterFunction() const { return m_stdoutFilterFunction; }
+ QString stderrFilterFunction() const { return m_stderrFilterFunction; }
+ int responseFileThreshold() const { return m_responseFileThreshold; }
+ QString responseFileUsagePrefix() const { return m_responseFileUsagePrefix; }
+ QProcessEnvironment environment() const { return m_environment; }
+
+private:
+ void getEnvironmentFromList(const QStringList &envList);
+
+ QString m_program;
+ QStringList m_arguments;
+ QString m_workingDir;
+ int m_maxExitCode;
+ QString m_stdoutFilterFunction;
+ QString m_stderrFilterFunction;
+ int m_responseFileThreshold; // When to use response files? In bytes of (program name + arguments).
+ QString m_responseFileUsagePrefix;
+ QProcessEnvironment m_environment;
+};
+
+class JavaScriptCommand : public AbstractCommand
+{
+public:
+ static void setupForJavaScript(QScriptValue targetObject);
+
+ JavaScriptCommand();
+
+ virtual CommandType type() const { return JavaScriptCommandType; }
+ bool equals(const AbstractCommand *otherAbstractCommand) const;
+ void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation);
+ void load(QDataStream &s);
+ void store(QDataStream &s);
+
+ const QString &sourceCode() const { return m_sourceCode; }
+ void setSourceCode(const QString &str) { m_sourceCode = str; }
+
+ const QVariantMap &properties() const { return m_properties; }
+
+private:
+ QString m_sourceCode;
+ QVariantMap m_properties;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_COMMAND_H
diff --git a/src/lib/corelib/buildgraph/cycledetector.cpp b/src/lib/corelib/buildgraph/cycledetector.cpp
new file mode 100644
index 000000000..1ab6b4bb8
--- /dev/null
+++ b/src/lib/corelib/buildgraph/cycledetector.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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 "cycledetector.h"
+
+#include "artifact.h"
+#include "buildgraph.h"
+#include "productbuilddata.h"
+
+#include <language/language.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+
+namespace qbs {
+namespace Internal {
+
+CycleDetector::CycleDetector(const Logger &logger)
+ : ArtifactVisitor(0), m_parent(0), m_logger(logger)
+{
+}
+
+void CycleDetector::visitProject(const ResolvedProjectConstPtr &project)
+{
+ const QString description = QString::fromLocal8Bit("Cycle detection for project '%1'")
+ .arg(project->name);
+ TimedActivityLogger timeLogger(m_logger, description, QLatin1String("[BG] "), LoggerTrace);
+ ArtifactVisitor::visitProject(project);
+}
+
+void CycleDetector::visitProduct(const ResolvedProductConstPtr &product)
+{
+ if (!product->buildData)
+ return;
+ foreach (Artifact * const artifact, product->buildData->targetArtifacts)
+ visitArtifact(artifact);
+}
+
+void CycleDetector::visitArtifact(Artifact *artifact)
+{
+ if (Q_UNLIKELY(m_artifactsInCurrentPath.contains(artifact))) {
+ ErrorInfo error(Tr::tr("Cycle in build graph detected."));
+ foreach (const Artifact * const a, cycle(artifact))
+ error.append(a->filePath());
+ throw error;
+ }
+
+ if (m_allArtifacts.contains(artifact))
+ return;
+
+ m_artifactsInCurrentPath += artifact;
+ m_parent = artifact;
+ foreach (Artifact * const child, artifact->children)
+ visitArtifact(child);
+ m_artifactsInCurrentPath -= artifact;
+ m_allArtifacts += artifact;
+}
+
+void CycleDetector::doVisit(Artifact *) { }
+
+QList<Artifact *> CycleDetector::cycle(Artifact *doubleEntry)
+{
+ QList<Artifact *> path;
+ findPath(doubleEntry, m_parent, path);
+ return path << doubleEntry;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/cycledetector.h b/src/lib/corelib/buildgraph/cycledetector.h
new file mode 100644
index 000000000..6e55efac3
--- /dev/null
+++ b/src/lib/corelib/buildgraph/cycledetector.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_CYCLEDETECTOR_H
+#define QBS_CYCLEDETECTOR_H
+
+#include "artifactvisitor.h"
+#include <logging/logger.h>
+
+#include <QSet>
+
+namespace qbs {
+namespace Internal {
+
+class CycleDetector : public ArtifactVisitor
+{
+public:
+ CycleDetector(const Logger &logger);
+
+ void visitProject(const ResolvedProjectConstPtr &project);
+ void visitProduct(const ResolvedProductConstPtr &product);
+ void visitArtifact(Artifact *artifact);
+
+private:
+ void doVisit(Artifact *artifact);
+
+ QList<Artifact *> cycle(Artifact *doubleEntry);
+
+ QSet<Artifact *> m_allArtifacts;
+ QSet<Artifact *> m_artifactsInCurrentPath;
+ Artifact *m_parent;
+ Logger m_logger;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_CYCLEDETECTOR_H
diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp
new file mode 100644
index 000000000..c1f7c6af3
--- /dev/null
+++ b/src/lib/corelib/buildgraph/executor.cpp
@@ -0,0 +1,905 @@
+/****************************************************************************
+**
+** 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 "executor.h"
+
+#include "artifactvisitor.h"
+#include "automoc.h"
+#include "buildgraph.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "cycledetector.h"
+#include "executorjob.h"
+#include "inputartifactscanner.h"
+#include "rulesevaluationcontext.h"
+
+#include <buildgraph/transformer.h>
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/progressobserver.h>
+#include <tools/qbsassert.h>
+
+#include <QDir>
+#include <QSet>
+#include <QTimer>
+
+#include <algorithm>
+#include <climits>
+
+namespace qbs {
+namespace Internal {
+
+class MocEffortCalculator : public ArtifactVisitor
+{
+public:
+ MocEffortCalculator() : ArtifactVisitor(Artifact::SourceFile), m_effort(0) {}
+
+ int effort() const { return m_effort; }
+
+private:
+ void doVisit(Artifact *) { m_effort += 10; }
+
+ int m_effort;
+};
+
+class BuildEffortCalculator : public ArtifactVisitor
+{
+public:
+ BuildEffortCalculator() : ArtifactVisitor(Artifact::Generated), m_effort(0) {}
+
+ int effort() const { return m_effort; }
+
+ static int multiplier(const Artifact *artifact) {
+ return artifact->transformer->rule->multiplex ? 200 : 20;
+ }
+
+private:
+ void doVisit(Artifact *artifact)
+ {
+ m_effort += multiplier(artifact);
+ }
+
+ int m_effort;
+};
+
+
+bool Executor::ComparePriority::operator() (const Artifact *x, const Artifact *y) const
+{
+ return x->product->buildData->buildPriority < y->product->buildData->buildPriority;
+}
+
+
+Executor::Executor(const Logger &logger, QObject *parent)
+ : QObject(parent)
+ , m_logger(logger)
+ , m_progressObserver(0)
+ , m_state(ExecutorIdle)
+ , m_doTrace(logger.traceEnabled())
+ , m_doDebug(logger.debugEnabled())
+{
+ m_inputArtifactScanContext = new InputArtifactScannerContext(&m_scanResultCache);
+ m_autoMoc = new AutoMoc(logger);
+ connect(m_autoMoc, SIGNAL(reportCommandDescription(QString,QString)),
+ this, SIGNAL(reportCommandDescription(QString,QString)));
+ m_autoMoc->setScanResultCache(&m_scanResultCache);
+}
+
+Executor::~Executor()
+{
+ // jobs must be destroyed before deleting the shared scan result cache
+ foreach (ExecutorJob *job, m_availableJobs)
+ delete job;
+ foreach (ExecutorJob *job, m_processingJobs.keys())
+ delete job;
+ delete m_autoMoc; // delete before shared scan result cache
+ delete m_inputArtifactScanContext;
+}
+
+FileTime Executor::recursiveFileTime(const QString &filePath) const
+{
+ FileTime newest;
+ if (m_progressObserver) {
+ qApp->processEvents(); // let the cancel event do its work
+ if (m_progressObserver->canceled())
+ return newest;
+ }
+ FileInfo fileInfo(filePath);
+ if (!fileInfo.exists()) {
+ const QString nativeFilePath = QDir::toNativeSeparators(filePath);
+ m_logger.qbsWarning() << Tr::tr("File '%1' not found.").arg(nativeFilePath);
+ return newest;
+ }
+ newest = qMax(fileInfo.lastModified(), fileInfo.lastStatusChange());
+ if (!fileInfo.isDir())
+ return newest;
+ const QStringList dirContents = QDir(filePath)
+ .entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach (const QString &curFileName, dirContents) {
+ const FileTime ft = recursiveFileTime(filePath + QLatin1Char('/') + curFileName);
+ if (ft > newest)
+ newest = ft;
+ }
+ return newest;
+}
+
+void Executor::retrieveSourceFileTimestamp(Artifact *artifact) const
+{
+ QBS_CHECK(artifact->artifactType == Artifact::SourceFile);
+
+ artifact->setTimestamp(recursiveFileTime(artifact->filePath()));
+ artifact->timestampRetrieved = true;
+}
+
+void Executor::build()
+{
+ try {
+ doBuild();
+ } catch (const ErrorInfo &e) {
+ m_error = e;
+ QTimer::singleShot(0, this, SLOT(finish()));
+ }
+}
+
+void Executor::setProject(const TopLevelProjectPtr &project)
+{
+ m_project = project;
+}
+
+void Executor::setProducts(const QList<ResolvedProductPtr> &productsToBuild)
+{
+ m_productsToBuild = productsToBuild;
+}
+
+class ProductPrioritySetter
+{
+ const TopLevelProject *m_topLevelProject;
+ unsigned int m_priority;
+ QSet<ResolvedProductPtr> m_seenProducts;
+public:
+ ProductPrioritySetter(const TopLevelProject *tlp)
+ : m_topLevelProject(tlp)
+ {
+ }
+
+ void apply()
+ {
+ QList<ResolvedProductPtr> allProducts = m_topLevelProject->allProducts();
+ QSet<ResolvedProductPtr> allDependencies;
+ foreach (const ResolvedProductPtr &product, allProducts)
+ allDependencies += product->dependencies;
+ QSet<ResolvedProductPtr> rootProducts = allProducts.toSet() - allDependencies;
+ m_priority = UINT_MAX;
+ m_seenProducts.clear();
+ foreach (const ResolvedProductPtr &rootProduct, rootProducts)
+ traverse(rootProduct);
+ }
+
+private:
+ void traverse(const ResolvedProductPtr &product)
+ {
+ if (m_seenProducts.contains(product))
+ return;
+ m_seenProducts += product;
+ foreach (const ResolvedProductPtr &dependency, product->dependencies)
+ traverse(dependency);
+ if (!product->buildData)
+ return;
+ product->buildData->buildPriority = m_priority--;
+ }
+};
+
+void Executor::doBuild()
+{
+ if (m_buildOptions.maxJobCount() <= 0) {
+ m_buildOptions.setMaxJobCount(BuildOptions::defaultMaxJobCount());
+ m_logger.qbsDebug() << "max job count not explicitly set, using value of "
+ << m_buildOptions.maxJobCount();
+ }
+ QBS_CHECK(m_state == ExecutorIdle);
+ m_leaves = Leaves();
+ m_error.clear();
+ m_explicitlyCanceled = false;
+ m_activeFileTags = FileTags::fromStringList(m_buildOptions.activeFileTags());
+
+ setState(ExecutorRunning);
+
+ if (m_productsToBuild.isEmpty()) {
+ m_logger.qbsTrace() << "No products to build, finishing.";
+ QTimer::singleShot(0, this, SLOT(finish())); // Don't call back on the caller.
+ return;
+ }
+
+ doSanityChecks();
+ m_evalContext = m_project->buildData->evaluationContext;
+ if (!m_evalContext) { // Is null before the first build.
+ m_evalContext = RulesEvaluationContextPtr(new RulesEvaluationContext(m_logger));
+ m_project->buildData->evaluationContext = m_evalContext;
+ }
+
+ m_logger.qbsDebug() << QString::fromLocal8Bit("[EXEC] preparing executor for %1 jobs "
+ "in parallel").arg(m_buildOptions.maxJobCount());
+ addExecutorJobs(m_buildOptions.maxJobCount());
+ foreach (ExecutorJob * const job, m_availableJobs)
+ job->setDryRun(m_buildOptions.dryRun());
+
+ bool sourceFilesChanged = false;
+ prepareAllArtifacts(&sourceFilesChanged);
+ Artifact::BuildState initialBuildState = m_buildOptions.changedFiles().isEmpty()
+ ? Artifact::Buildable : Artifact::Built;
+
+ QList<Artifact *> changedArtifacts;
+ foreach (const QString &filePath, m_buildOptions.changedFiles()) {
+ QList<FileResourceBase *> lookupResults;
+ lookupResults.append(m_project->buildData->lookupFiles(filePath));
+ if (lookupResults.isEmpty()) {
+ m_logger.qbsWarning() << QString::fromLocal8Bit("Out of date file '%1' provided "
+ "but not found.").arg(QDir::toNativeSeparators(filePath));
+ continue;
+ }
+ foreach (FileResourceBase *lookupResult, lookupResults)
+ if (Artifact *artifact = dynamic_cast<Artifact *>(lookupResult))
+ changedArtifacts += artifact;
+ }
+ qSort(changedArtifacts);
+ changedArtifacts.erase(std::unique(changedArtifacts.begin(), changedArtifacts.end()),
+ changedArtifacts.end());
+
+ // prepare products
+ ProductPrioritySetter prioritySetter(m_productsToBuild.first()->topLevelProject());
+ prioritySetter.apply();
+ foreach (ResolvedProductPtr product, m_productsToBuild)
+ product->setupBuildEnvironment(m_evalContext->engine(), m_project->environment);
+
+ // find the root nodes
+ m_roots.clear();
+ foreach (const ResolvedProductPtr &product, m_productsToBuild) {
+ foreach (Artifact *targetArtifact, product->buildData->targetArtifacts) {
+ m_roots += targetArtifact;
+
+ // The user expects that he can delete target artifacts and they get rebuilt.
+ // To achieve this we must retrieve their timestamps.
+ targetArtifact->setTimestamp(FileInfo(targetArtifact->filePath()).lastModified());
+ }
+ }
+
+ prepareReachableArtifacts(initialBuildState);
+ setupProgressObserver(sourceFilesChanged);
+ if (sourceFilesChanged)
+ runAutoMoc();
+ initLeaves(changedArtifacts);
+ if (!scheduleJobs()) {
+ m_logger.qbsTrace() << "Nothing to do at all, finishing.";
+ QTimer::singleShot(0, this, SLOT(finish())); // Don't call back on the caller.
+ }
+}
+
+void Executor::setBuildOptions(const BuildOptions &buildOptions)
+{
+ m_buildOptions = buildOptions;
+}
+
+static void initArtifactsBottomUp(Artifact *artifact)
+{
+ if (artifact->buildState == Artifact::Untouched)
+ return;
+ artifact->buildState = Artifact::Buildable;
+ foreach (Artifact *parent, artifact->parents)
+ initArtifactsBottomUp(parent);
+}
+
+void Executor::initLeaves(const QList<Artifact *> &changedArtifacts)
+{
+ if (changedArtifacts.isEmpty()) {
+ QSet<Artifact *> seenArtifacts;
+ foreach (Artifact *root, m_roots)
+ initLeavesTopDown(root, seenArtifacts);
+ } else {
+ foreach (Artifact *artifact, changedArtifacts) {
+ m_leaves.push(artifact);
+ initArtifactsBottomUp(artifact);
+ }
+ }
+}
+
+void Executor::initLeavesTopDown(Artifact *artifact, QSet<Artifact *> &seenArtifacts)
+{
+ if (seenArtifacts.contains(artifact))
+ return;
+ seenArtifacts += artifact;
+
+ // Artifacts that appear in the build graph after
+ // prepareBuildGraph() has been called, must be initialized.
+ if (artifact->buildState == Artifact::Untouched) {
+ artifact->buildState = Artifact::Buildable;
+ if (artifact->artifactType == Artifact::SourceFile)
+ retrieveSourceFileTimestamp(artifact);
+ }
+
+ if (artifact->children.isEmpty()) {
+ m_leaves.push(artifact);
+ } else {
+ foreach (Artifact *child, artifact->children)
+ initLeavesTopDown(child, seenArtifacts);
+ }
+}
+
+// Returns true if some artifacts are still waiting to be built or currently building.
+bool Executor::scheduleJobs()
+{
+ QBS_CHECK(m_state == ExecutorRunning);
+ while (!m_leaves.empty() && !m_availableJobs.isEmpty()) {
+ Artifact * const artifact = m_leaves.top();
+ m_leaves.pop();
+ buildArtifact(artifact);
+ }
+ return !m_leaves.empty() || !m_processingJobs.isEmpty();
+}
+
+bool Executor::isUpToDate(Artifact *artifact) const
+{
+ QBS_CHECK(artifact->artifactType == Artifact::Generated);
+
+ const bool debug = false;
+ if (debug) {
+ m_logger.qbsDebug() << "[UTD] check " << artifact->filePath() << " "
+ << artifact->timestamp().toString();
+ }
+
+ if (m_buildOptions.forceTimestampCheck()) {
+ artifact->setTimestamp(FileInfo(artifact->filePath()).lastModified());
+ if (debug) {
+ m_logger.qbsDebug() << "[UTD] timestamp retrieved from filesystem: "
+ << artifact->timestamp().toString();
+ }
+ }
+
+ if (!artifact->timestamp().isValid()) {
+ if (debug)
+ m_logger.qbsDebug() << "[UTD] invalid timestamp. Out of date.";
+ return false;
+ }
+
+ foreach (Artifact *child, artifact->children) {
+ QBS_CHECK(child->timestamp().isValid());
+ if (debug)
+ m_logger.qbsDebug() << "[UTD] child timestamp " << child->timestamp().toString();
+ if (artifact->timestamp() < child->timestamp())
+ return false;
+ }
+
+ foreach (FileDependency *fileDependency, artifact->fileDependencies) {
+ if (!fileDependency->timestamp().isValid()) {
+ FileInfo fi(fileDependency->filePath());
+ fileDependency->setTimestamp(fi.lastModified());
+ }
+ if (debug)
+ m_logger.qbsDebug() << "[UTD] file dependency timestamp "
+ << fileDependency->timestamp().toString();
+ if (artifact->timestamp() < fileDependency->timestamp())
+ return false;
+ }
+
+ return true;
+}
+
+bool Executor::mustExecuteTransformer(const TransformerPtr &transformer) const
+{
+ foreach (Artifact *artifact, transformer->outputs)
+ if (artifact->alwaysUpdated)
+ return !isUpToDate(artifact);
+
+ // All outputs of the transformer have alwaysUpdated == false.
+ // We need at least on output that is always updated.
+ QBS_CHECK(false);
+ return true;
+}
+
+void Executor::buildArtifact(Artifact *artifact)
+{
+ QBS_CHECK(!m_availableJobs.isEmpty());
+
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] " << relativeArtifactFileName(artifact);
+
+ // Skip artifacts that are already built.
+ if (artifact->buildState == Artifact::Built) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] artifact already built. Skipping.";
+ return;
+ }
+
+ // skip artifacts without transformer
+ if (artifact->artifactType != Artifact::Generated) {
+ // For source artifacts, that were not reachable when initializing the build, we must
+ // retrieve timestamps. This can happen, if a dependency that's added during the build
+ // makes the source artifact reachable.
+ if (artifact->artifactType == Artifact::SourceFile && !artifact->timestampRetrieved)
+ retrieveSourceFileTimestamp(artifact);
+
+ if (m_doDebug)
+ m_logger.qbsDebug() << QString::fromLocal8Bit("[EXEC] artifact type %1. Skipping.")
+ .arg(toString(artifact->artifactType));
+ finishArtifact(artifact);
+ return;
+ }
+
+ // Every generated artifact must have a transformer.
+ QBS_CHECK(artifact->transformer);
+
+ // Skip if outputs of this transformer are already built.
+ // That means we already ran the transformation.
+ foreach (Artifact *sideBySideArtifact, artifact->transformer->outputs) {
+ if (sideBySideArtifact == artifact)
+ continue;
+ switch (sideBySideArtifact->buildState)
+ {
+ case Artifact::Untouched:
+ case Artifact::Buildable:
+ break;
+ case Artifact::Built:
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] Side by side artifact already finished. Skipping.";
+ finishArtifact(artifact);
+ return;
+ case Artifact::Building:
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] Side by side artifact processing. Skipping.";
+ artifact->buildState = Artifact::Building;
+ return;
+ }
+ }
+
+ // Skip if we're building just one file and the file tags do not match.
+ if (!m_activeFileTags.isEmpty() && !m_activeFileTags.matches(artifact->fileTags)) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] file tags do not match. Skipping.";
+ finishArtifact(artifact);
+ return;
+ }
+
+ // Skip transformers that do not need to be built.
+ if (!mustExecuteTransformer(artifact->transformer)) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] Up to date. Skipping.";
+ finishArtifact(artifact);
+ return;
+ }
+
+ // create the output directories
+ if (!m_buildOptions.dryRun()) {
+ ArtifactList::const_iterator it = artifact->transformer->outputs.begin();
+ for (; it != artifact->transformer->outputs.end(); ++it) {
+ Artifact *output = *it;
+ QDir outDir = QFileInfo(output->filePath()).absoluteDir();
+ if (!outDir.exists())
+ outDir.mkpath(".");
+ }
+ }
+
+ // scan all input artifacts
+ InputArtifactScanner scanner(artifact, m_inputArtifactScanContext, m_logger);
+ scanner.scan();
+
+ // postpone the build of this artifact, if new dependencies found
+ if (scanner.newDependencyAdded()) {
+ bool buildingDependenciesFound = false;
+ QVector<Artifact *> unbuiltDependencies;
+ foreach (Artifact *dependency, artifact->children) {
+ switch (dependency->buildState) {
+ case Artifact::Untouched:
+ case Artifact::Buildable:
+ unbuiltDependencies += dependency;
+ break;
+ case Artifact::Building:
+ buildingDependenciesFound = true;
+ break;
+ case Artifact::Built:
+ // do nothing
+ break;
+ }
+ }
+ if (!unbuiltDependencies.isEmpty()) {
+ artifact->inputsScanned = false;
+ insertLeavesAfterAddingDependencies(unbuiltDependencies);
+ return;
+ }
+ if (buildingDependenciesFound) {
+ artifact->inputsScanned = false;
+ return;
+ }
+ }
+
+ ExecutorJob *job = m_availableJobs.takeFirst();
+ artifact->buildState = Artifact::Building;
+ m_processingJobs.insert(job, artifact);
+
+ Q_ASSERT_X(artifact->product, Q_FUNC_INFO,
+ qPrintable(QString("Generated artifact '%1' belongs to no product.")
+ .arg(QDir::toNativeSeparators(artifact->filePath()))));
+
+ job->run(artifact->transformer.data(), artifact->product);
+}
+
+void Executor::finishJob(ExecutorJob *job, bool success)
+{
+ QBS_CHECK(job);
+ QBS_CHECK(m_state != ExecutorIdle);
+
+ const QHash<ExecutorJob *, Artifact *>::Iterator it = m_processingJobs.find(job);
+ QBS_CHECK(it != m_processingJobs.end());
+ if (success)
+ finishArtifact(it.value());
+ m_processingJobs.erase(it);
+ m_availableJobs.append(job);
+
+ if (!success && !m_buildOptions.keepGoing())
+ cancelJobs();
+
+ if (m_state == ExecutorRunning && m_progressObserver && m_progressObserver->canceled()) {
+ m_logger.qbsTrace() << "Received cancel request; canceling build.";
+ m_explicitlyCanceled = true;
+ cancelJobs();
+ }
+
+ if (m_state == ExecutorCanceling) {
+ if (m_processingJobs.isEmpty()) {
+ m_logger.qbsTrace() << "All pending jobs are done, finishing.";
+ finish();
+ }
+ return;
+ }
+
+ if (!scheduleJobs()) {
+ m_logger.qbsTrace() << "Nothing left to build; finishing.";
+ finish();
+ }
+}
+
+static bool allChildrenBuilt(Artifact *artifact)
+{
+ foreach (Artifact *child, artifact->children)
+ if (child->buildState != Artifact::Built)
+ return false;
+ return true;
+}
+
+void Executor::finishArtifact(Artifact *leaf)
+{
+ QBS_CHECK(leaf);
+
+ if (m_doTrace)
+ m_logger.qbsTrace() << "[EXEC] finishArtifact " << relativeArtifactFileName(leaf);
+
+ leaf->buildState = Artifact::Built;
+ m_scanResultCache.remove(leaf->filePath());
+ foreach (Artifact *parent, leaf->parents) {
+ if (parent->buildState != Artifact::Buildable) {
+ if (m_doTrace) {
+ m_logger.qbsTrace() << "[EXEC] parent " << relativeArtifactFileName(parent)
+ << " build state: " << toString(parent->buildState);
+ }
+ continue;
+ }
+
+ if (allChildrenBuilt(parent)) {
+ m_leaves.push(parent);
+ if (m_doTrace) {
+ m_logger.qbsTrace() << "[EXEC] finishArtifact adds leaf "
+ << relativeArtifactFileName(parent) << " " << toString(parent->buildState);
+ }
+ } else {
+ if (m_doTrace) {
+ m_logger.qbsTrace() << "[EXEC] parent " << relativeArtifactFileName(parent)
+ << " build state: " << toString(parent->buildState);
+ }
+ }
+ }
+
+ if (leaf->transformer)
+ foreach (Artifact *sideBySideArtifact, leaf->transformer->outputs)
+ if (leaf != sideBySideArtifact && sideBySideArtifact->buildState == Artifact::Building)
+ finishArtifact(sideBySideArtifact);
+
+ if (m_progressObserver && leaf->artifactType == Artifact::Generated)
+ m_progressObserver->incrementProgressValue(BuildEffortCalculator::multiplier(leaf));
+}
+
+void Executor::insertLeavesAfterAddingDependencies_recurse(Artifact *const artifact,
+ QSet<Artifact *> *seenArtifacts, Leaves *leaves) const
+{
+ if (seenArtifacts->contains(artifact))
+ return;
+ seenArtifacts->insert(artifact);
+
+ if (artifact->buildState == Artifact::Untouched)
+ artifact->buildState = Artifact::Buildable;
+
+ bool isLeaf = true;
+ foreach (Artifact *child, artifact->children) {
+ if (child->buildState != Artifact::Built) {
+ isLeaf = false;
+ insertLeavesAfterAddingDependencies_recurse(child, seenArtifacts, leaves);
+ }
+ }
+
+ if (isLeaf) {
+ if (m_doDebug)
+ m_logger.qbsDebug() << "[EXEC] adding leaf " << relativeArtifactFileName(artifact);
+ leaves->push(artifact);
+ }
+}
+
+QString Executor::configString() const
+{
+ return tr(" for configuration %1").arg(m_project->id());
+}
+
+void Executor::insertLeavesAfterAddingDependencies(QVector<Artifact *> dependencies)
+{
+ QSet<Artifact *> seenArtifacts;
+ foreach (Artifact *dependency, dependencies)
+ insertLeavesAfterAddingDependencies_recurse(dependency, &seenArtifacts, &m_leaves);
+}
+
+void Executor::cancelJobs()
+{
+ m_logger.qbsTrace() << "Canceling all jobs.";
+ setState(ExecutorCanceling);
+ QList<ExecutorJob *> jobs = m_processingJobs.keys();
+ foreach (ExecutorJob *job, jobs)
+ job->cancel();
+}
+
+void Executor::setupProgressObserver(bool mocWillRun)
+{
+ if (!m_progressObserver)
+ return;
+ MocEffortCalculator mocEffortCalculator;
+ BuildEffortCalculator buildEffortCalculator;
+ foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
+ buildEffortCalculator.visitProduct(product);
+ if (mocWillRun) {
+ foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
+ mocEffortCalculator.visitProduct(product);
+ }
+ m_mocEffort = mocEffortCalculator.effort();
+ const int totalEffort = m_mocEffort + buildEffortCalculator.effort();
+ m_progressObserver->initialize(tr("Building%1").arg(configString()), totalEffort);
+}
+
+void Executor::doSanityChecks()
+{
+ QBS_CHECK(m_project);
+ QBS_CHECK(!m_productsToBuild.isEmpty());
+ foreach (const ResolvedProductConstPtr &product, m_productsToBuild) {
+ QBS_CHECK(product->buildData);
+ QBS_CHECK(product->topLevelProject() == m_project);
+ }
+}
+
+void Executor::handleError(const ErrorInfo &error)
+{
+ m_error = error;
+ if (m_processingJobs.isEmpty())
+ finish();
+ else
+ cancelJobs();
+}
+
+void Executor::addExecutorJobs(int jobNumber)
+{
+ for (int i = 1; i <= jobNumber; i++) {
+ ExecutorJob *job = new ExecutorJob(m_logger, this);
+ job->setMainThreadScriptEngine(m_evalContext->engine());
+ job->setObjectName(QString(QLatin1String("J%1")).arg(i));
+ m_availableJobs.append(job);
+ connect(job, SIGNAL(reportCommandDescription(QString,QString)),
+ this, SIGNAL(reportCommandDescription(QString,QString)), Qt::QueuedConnection);
+ connect(job, SIGNAL(reportProcessResult(qbs::ProcessResult)),
+ this, SIGNAL(reportProcessResult(qbs::ProcessResult)), Qt::QueuedConnection);
+ connect(job, SIGNAL(error(qbs::ErrorInfo)),
+ this, SLOT(onProcessError(qbs::ErrorInfo)), Qt::QueuedConnection);
+ connect(job, SIGNAL(success()), this, SLOT(onProcessSuccess()), Qt::QueuedConnection);
+ }
+}
+
+void Executor::runAutoMoc()
+{
+ bool autoMocApplied = false;
+ foreach (const ResolvedProductPtr &product, m_productsToBuild) {
+ if (m_progressObserver && m_progressObserver->canceled())
+ throw ErrorInfo(Tr::tr("Build canceled%1.").arg(configString()));
+ // HACK call the automoc thingy here only if we have use Qt/core module
+ foreach (const ResolvedModuleConstPtr &m, product->modules) {
+ if (m->name == "Qt/core") {
+ autoMocApplied = true;
+ m_autoMoc->apply(product);
+ break;
+ }
+ }
+ }
+ if (autoMocApplied) {
+ foreach (const ResolvedProductConstPtr &product, m_productsToBuild)
+ CycleDetector(m_logger).visitProduct(product);
+ }
+ if (m_progressObserver)
+ m_progressObserver->incrementProgressValue(m_mocEffort);
+}
+
+void Executor::onProcessError(const qbs::ErrorInfo &err)
+{
+ try {
+ if (m_buildOptions.keepGoing()) {
+ ErrorInfo fullWarning(err);
+ fullWarning.prepend(Tr::tr("Ignoring the following errors on user request:"));
+ m_logger.printWarning(fullWarning);
+ } else {
+ m_error = err;
+ }
+ ExecutorJob * const job = qobject_cast<ExecutorJob *>(sender());
+ finishJob(job, false);
+ } catch (const ErrorInfo &error) {
+ handleError(error);
+ }
+}
+
+void Executor::onProcessSuccess()
+{
+ try {
+ ExecutorJob *job = qobject_cast<ExecutorJob *>(sender());
+ QBS_CHECK(job);
+ Artifact *processedArtifact = m_processingJobs.value(job);
+ QBS_CHECK(processedArtifact);
+
+ // Update the timestamps of the outputs of the transformer we just executed.
+ processedArtifact->product->topLevelProject()->buildData->isDirty = true;
+ foreach (Artifact *artifact, processedArtifact->transformer->outputs) {
+ if (artifact->alwaysUpdated)
+ artifact->setTimestamp(FileTime::currentTime());
+ else
+ artifact->setTimestamp(FileInfo(artifact->filePath()).lastModified());
+ }
+
+ finishJob(job, true);
+ } catch (const ErrorInfo &error) {
+ handleError(error);
+ }
+}
+
+void Executor::finish()
+{
+ QBS_ASSERT(m_state != ExecutorIdle, /* ignore */);
+
+ QStringList unbuiltProductNames;
+ foreach (const ResolvedProductPtr &product, m_productsToBuild) {
+ foreach (Artifact *artifact, product->buildData->targetArtifacts) {
+ if (artifact->buildState != Artifact::Built) {
+ unbuiltProductNames += product->name;
+ break;
+ }
+ }
+ }
+
+ if (unbuiltProductNames.isEmpty()) {
+ m_logger.qbsInfo() << Tr::tr("Build done%1.").arg(configString());
+ } else {
+ m_error.append(Tr::tr("The following products could not be built%1: %2.")
+ .arg(configString(), unbuiltProductNames.join(", ")));
+ }
+
+ if (m_explicitlyCanceled)
+ m_error.append(Tr::tr("Build canceled%1.").arg(configString()));
+ setState(ExecutorIdle);
+ if (m_progressObserver)
+ m_progressObserver->setFinished();
+ emit finished();
+}
+
+/**
+ * Sets the state of all artifacts in the graph to "untouched".
+ * This must be done before doing a build.
+ *
+ * Retrieves the timestamps of source artifacts.
+ *
+ * This function sets *sourceFilesChanged to true, if the timestamp of a reachable source artifact
+ * changed.
+ */
+void Executor::prepareAllArtifacts(bool *sourceFilesChanged)
+{
+ foreach (const ResolvedProductPtr &product, m_productsToBuild) {
+ foreach (Artifact *artifact, product->buildData->artifacts) {
+ artifact->buildState = Artifact::Untouched;
+ artifact->inputsScanned = false;
+ artifact->timestampRetrieved = false;
+
+ if (artifact->artifactType == Artifact::SourceFile) {
+ const FileTime oldTimestamp = artifact->timestamp();
+ retrieveSourceFileTimestamp(artifact);
+ if (oldTimestamp != artifact->timestamp())
+ *sourceFilesChanged = true;
+ }
+
+ // Timestamps of file dependencies must be invalid for every build.
+ foreach (FileDependency *fileDependency, artifact->fileDependencies)
+ fileDependency->clearTimestamp();
+ }
+ }
+}
+
+/**
+ * Walk the build graph top-down from the roots and for each reachable node N
+ * - mark N as buildable.
+ */
+void Executor::prepareReachableArtifacts(const Artifact::BuildState buildState)
+{
+ foreach (Artifact *root, m_roots)
+ prepareReachableArtifacts_impl(root, buildState);
+}
+
+void Executor::prepareReachableArtifacts_impl(Artifact *artifact,
+ const Artifact::BuildState buildState)
+{
+ if (artifact->buildState != Artifact::Untouched)
+ return;
+
+ artifact->buildState = buildState;
+ foreach (Artifact *child, artifact->children)
+ prepareReachableArtifacts_impl(child, buildState);
+}
+
+void Executor::updateBuildGraph(Artifact::BuildState buildState)
+{
+ QSet<Artifact *> seenArtifacts;
+ foreach (Artifact *root, m_roots)
+ updateBuildGraph_impl(root, buildState, seenArtifacts);
+}
+
+void Executor::updateBuildGraph_impl(Artifact *artifact, Artifact::BuildState buildState, QSet<Artifact *> &seenArtifacts)
+{
+ if (seenArtifacts.contains(artifact))
+ return;
+
+ seenArtifacts += artifact;
+ artifact->buildState = buildState;
+
+ foreach (Artifact *child, artifact->children)
+ updateBuildGraph_impl(child, buildState, seenArtifacts);
+}
+
+void Executor::setState(ExecutorState s)
+{
+ if (m_state == s)
+ return;
+ m_state = s;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/executor.h b/src/lib/corelib/buildgraph/executor.h
new file mode 100644
index 000000000..ece3b39a3
--- /dev/null
+++ b/src/lib/corelib/buildgraph/executor.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_BUILDGRAPHEXECUTOR_H
+#define QBS_BUILDGRAPHEXECUTOR_H
+
+#include "forward_decls.h"
+#include <buildgraph/artifact.h>
+#include <buildgraph/scanresultcache.h>
+#include <language/forward_decls.h>
+
+#include <logging/logger.h>
+#include <tools/buildoptions.h>
+#include <tools/error.h>
+
+#include <QObject>
+#include <queue>
+
+namespace qbs {
+class ProcessResult;
+
+namespace Internal {
+class AutoMoc;
+class ExecutorJob;
+class FileTime;
+class InputArtifactScannerContext;
+class ProgressObserver;
+
+class Executor : public QObject
+{
+ Q_OBJECT
+
+public slots:
+ void build();
+
+public:
+ Executor(const Logger &logger, QObject *parent = 0);
+ ~Executor();
+
+ void setProject(const TopLevelProjectPtr &project);
+ void setProducts(const QList<ResolvedProductPtr> &productsToBuild);
+ void setBuildOptions(const BuildOptions &buildOptions);
+ void setProgressObserver(ProgressObserver *observer) { m_progressObserver = observer; }
+
+ ErrorInfo error() const { return m_error; }
+
+signals:
+ void reportCommandDescription(const QString &highlight, const QString &message);
+ void reportProcessResult(const qbs::ProcessResult &result);
+
+ void finished();
+
+private slots:
+ void onProcessError(const qbs::ErrorInfo &err);
+ void onProcessSuccess();
+ void finish();
+
+private:
+ enum ExecutorState { ExecutorIdle, ExecutorRunning, ExecutorCanceling };
+
+ struct ComparePriority
+ {
+ bool operator() (const Artifact *x, const Artifact *y) const;
+ };
+
+ typedef std::priority_queue<Artifact *, std::vector<Artifact *>, ComparePriority> Leaves;
+
+ void doBuild();
+ void prepareAllArtifacts(bool *sourceFilesChanged);
+ void prepareReachableArtifacts(const Artifact::BuildState buildState);
+ void prepareReachableArtifacts_impl(Artifact *artifact, const Artifact::BuildState buildState);
+ void updateBuildGraph(Artifact::BuildState buildState);
+ void updateBuildGraph_impl(Artifact *artifact, Artifact::BuildState buildState, QSet<Artifact *> &seenArtifacts);
+ void initLeaves(const QList<Artifact *> &changedArtifacts);
+ void initLeavesTopDown(Artifact *artifact, QSet<Artifact *> &seenArtifacts);
+ bool scheduleJobs();
+ void buildArtifact(Artifact *artifact);
+ void finishJob(ExecutorJob *job, bool success);
+ void finishArtifact(Artifact *artifact);
+ void setState(ExecutorState);
+ void addExecutorJobs(int jobNumber);
+ void runAutoMoc();
+ void insertLeavesAfterAddingDependencies(QVector<Artifact *> dependencies);
+ void cancelJobs();
+ void setupProgressObserver(bool mocWillRun);
+ void doSanityChecks();
+ void handleError(const ErrorInfo &error);
+
+ bool mustExecuteTransformer(const TransformerPtr &transformer) const;
+ bool isUpToDate(Artifact *artifact) const;
+ void retrieveSourceFileTimestamp(Artifact *artifact) const;
+ FileTime recursiveFileTime(const QString &filePath) const;
+ void insertLeavesAfterAddingDependencies_recurse(Artifact *const artifact,
+ QSet<Artifact *> *seenArtifacts, Leaves *leaves) const;
+ QString configString() const;
+
+ RulesEvaluationContextPtr m_evalContext;
+ BuildOptions m_buildOptions;
+ Logger m_logger;
+ ProgressObserver *m_progressObserver;
+ QList<ExecutorJob*> m_availableJobs;
+ QHash<ExecutorJob*, Artifact *> m_processingJobs;
+ ExecutorState m_state;
+ TopLevelProjectPtr m_project;
+ QList<ResolvedProductPtr> m_productsToBuild;
+ QList<Artifact *> m_roots;
+ Leaves m_leaves;
+ ScanResultCache m_scanResultCache;
+ InputArtifactScannerContext *m_inputArtifactScanContext;
+ AutoMoc *m_autoMoc;
+ int m_mocEffort;
+ ErrorInfo m_error;
+ bool m_explicitlyCanceled;
+ FileTags m_activeFileTags;
+ const bool m_doTrace;
+ const bool m_doDebug;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILDGRAPHEXECUTOR_H
diff --git a/src/lib/corelib/buildgraph/executorjob.cpp b/src/lib/corelib/buildgraph/executorjob.cpp
new file mode 100644
index 000000000..3ad3df6d5
--- /dev/null
+++ b/src/lib/corelib/buildgraph/executorjob.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** 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 "executorjob.h"
+
+#include "command.h"
+#include "jscommandexecutor.h"
+#include "processcommandexecutor.h"
+#include "transformer.h"
+#include <language/language.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+
+#include <QThread>
+
+namespace qbs {
+namespace Internal {
+
+ExecutorJob::ExecutorJob(const Logger &logger, QObject *parent)
+ : QObject(parent)
+ , m_processCommandExecutor(new ProcessCommandExecutor(logger, this))
+ , m_jsCommandExecutor(new JsCommandExecutor(logger, this))
+{
+ connect(m_processCommandExecutor, SIGNAL(reportCommandDescription(QString,QString)),
+ this, SIGNAL(reportCommandDescription(QString,QString)));
+ connect(m_processCommandExecutor, SIGNAL(reportProcessResult(qbs::ProcessResult)),
+ this, SIGNAL(reportProcessResult(qbs::ProcessResult)));
+ connect(m_processCommandExecutor, SIGNAL(error(qbs::ErrorInfo)),
+ this, SLOT(onCommandError(qbs::ErrorInfo)));
+ connect(m_processCommandExecutor, SIGNAL(finished()), SLOT(onCommandFinished()));
+ connect(m_jsCommandExecutor, SIGNAL(reportCommandDescription(QString,QString)),
+ this, SIGNAL(reportCommandDescription(QString,QString)));
+ connect(m_jsCommandExecutor, SIGNAL(error(qbs::ErrorInfo)),
+ this, SLOT(onCommandError(qbs::ErrorInfo)));
+ connect(m_jsCommandExecutor, SIGNAL(finished()), SLOT(onCommandFinished()));
+ setInactive();
+}
+
+ExecutorJob::~ExecutorJob()
+{
+}
+
+void ExecutorJob::setMainThreadScriptEngine(ScriptEngine *engine)
+{
+ m_processCommandExecutor->setMainThreadScriptEngine(engine);
+ m_jsCommandExecutor->setMainThreadScriptEngine(engine);
+}
+
+void ExecutorJob::setDryRun(bool enabled)
+{
+ m_processCommandExecutor->setDryRunEnabled(enabled);
+ m_jsCommandExecutor->setDryRunEnabled(enabled);
+}
+
+void ExecutorJob::run(Transformer *t, const ResolvedProductPtr &product)
+{
+ QBS_ASSERT(m_currentCommandIdx == -1, return);
+
+ if (t->commands.isEmpty()) {
+ emit success();
+ return;
+ }
+
+ t->propertiesRequestedInCommands.clear();
+ m_processCommandExecutor->setProcessEnvironment(product->buildEnvironment);
+
+ m_transformer = t;
+ runNextCommand();
+}
+
+void ExecutorJob::cancel()
+{
+ if (!m_transformer)
+ return;
+ m_currentCommandIdx = m_transformer->commands.count();
+}
+
+void ExecutorJob::waitForFinished()
+{
+ if (m_currentCommandExecutor)
+ m_currentCommandExecutor->waitForFinished();
+}
+
+void ExecutorJob::runNextCommand()
+{
+ QBS_ASSERT(m_currentCommandIdx <= m_transformer->commands.count(), return);
+ ++m_currentCommandIdx;
+ if (m_currentCommandIdx >= m_transformer->commands.count()) {
+ setInactive();
+ emit success();
+ return;
+ }
+
+ const AbstractCommand * const command = m_transformer->commands.at(m_currentCommandIdx);
+ switch (command->type()) {
+ case AbstractCommand::ProcessCommandType:
+ m_currentCommandExecutor = m_processCommandExecutor;
+ break;
+ case AbstractCommand::JavaScriptCommandType:
+ m_currentCommandExecutor = m_jsCommandExecutor;
+ break;
+ default:
+ qFatal("Missing implementation for command type %d", command->type());
+ }
+
+ m_currentCommandExecutor->start(m_transformer, command);
+}
+
+void ExecutorJob::onCommandError(const ErrorInfo &err)
+{
+ setInactive();
+ emit error(err);
+}
+
+void ExecutorJob::onCommandFinished()
+{
+ if (!m_transformer)
+ return;
+ runNextCommand();
+}
+
+void ExecutorJob::setInactive()
+{
+ m_transformer = 0;
+ m_currentCommandExecutor = 0;
+ m_currentCommandIdx = -1;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/executorjob.h b/src/lib/corelib/buildgraph/executorjob.h
new file mode 100644
index 000000000..fcd06cc6d
--- /dev/null
+++ b/src/lib/corelib/buildgraph/executorjob.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_EXECUTORJOB_H
+#define QBS_EXECUTORJOB_H
+
+#include <language/forward_decls.h>
+#include <tools/error.h>
+
+#include <QObject>
+
+namespace qbs {
+class CodeLocation;
+class ProcessResult;
+
+namespace Internal {
+class AbstractCommandExecutor;
+class ProductBuildData;
+class JsCommandExecutor;
+class Logger;
+class ProcessCommandExecutor;
+class ScriptEngine;
+class Transformer;
+
+class ExecutorJob : public QObject
+{
+ Q_OBJECT
+public:
+ ExecutorJob(const Logger &logger, QObject *parent);
+ ~ExecutorJob();
+
+ void setMainThreadScriptEngine(ScriptEngine *engine);
+ void setDryRun(bool enabled);
+ void run(Transformer *t, const ResolvedProductPtr &product);
+ void cancel();
+ void waitForFinished();
+
+signals:
+ void reportCommandDescription(const QString &highlight, const QString &message);
+ void reportProcessResult(const qbs::ProcessResult &result);
+ void error(const qbs::ErrorInfo &error);
+ void success();
+
+private slots:
+ void runNextCommand();
+ void onCommandError(const qbs::ErrorInfo &err);
+ void onCommandFinished();
+
+private:
+ void setInactive();
+
+ AbstractCommandExecutor *m_currentCommandExecutor;
+ ProcessCommandExecutor *m_processCommandExecutor;
+ JsCommandExecutor *m_jsCommandExecutor;
+ Transformer *m_transformer;
+ int m_currentCommandIdx;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_EXECUTORJOB_H
diff --git a/src/lib/corelib/buildgraph/filedependency.cpp b/src/lib/corelib/buildgraph/filedependency.cpp
new file mode 100644
index 000000000..8341de050
--- /dev/null
+++ b/src/lib/corelib/buildgraph/filedependency.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** 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 "filedependency.h"
+
+#include <tools/fileinfo.h>
+#include <tools/persistence.h>
+
+namespace qbs {
+namespace Internal {
+
+FileResourceBase::FileResourceBase()
+{
+}
+
+FileResourceBase::~FileResourceBase()
+{
+}
+
+void FileResourceBase::setTimestamp(const FileTime &t)
+
+{
+ m_timestamp = t;
+}
+
+const FileTime &FileResourceBase::timestamp() const
+{
+ return m_timestamp;
+}
+
+void FileResourceBase::setFilePath(const QString &filePath)
+{
+ m_filePath = filePath;
+ FileInfo::splitIntoDirectoryAndFileName(m_filePath, &m_dirPath, &m_fileName);
+}
+
+const QString &FileResourceBase::filePath() const
+{
+ return m_filePath;
+}
+
+void FileResourceBase::load(PersistentPool &pool)
+{
+ setFilePath(pool.idLoadString());
+ pool.stream()
+ >> m_timestamp;
+}
+
+void FileResourceBase::store(PersistentPool &pool) const
+{
+ pool.storeString(m_filePath);
+ pool.stream()
+ << m_timestamp;
+}
+
+
+FileDependency::FileDependency()
+{
+}
+
+FileDependency::~FileDependency()
+{
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/filedependency.h b/src/lib/corelib/buildgraph/filedependency.h
new file mode 100644
index 000000000..0379c0ec6
--- /dev/null
+++ b/src/lib/corelib/buildgraph/filedependency.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_FILEDEPENDENCY_H
+#define QBS_FILEDEPENDENCY_H
+
+#include <tools/filetime.h>
+#include <tools/persistentobject.h>
+
+namespace qbs {
+namespace Internal {
+
+class FileResourceBase : public virtual PersistentObject
+{
+protected:
+ FileResourceBase();
+
+public:
+ ~FileResourceBase();
+
+ void setTimestamp(const FileTime &t);
+ const FileTime &timestamp() const;
+ void clearTimestamp() { m_timestamp.clear(); }
+
+ void setFilePath(const QString &filePath);
+ const QString &filePath() const;
+ QString dirPath() const { return m_dirPath.toString(); }
+ QString fileName() const { return m_fileName.toString(); }
+
+protected:
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+
+private:
+ FileTime m_timestamp;
+ QString m_filePath;
+ QStringRef m_dirPath;
+ QStringRef m_fileName;
+};
+
+class FileDependency : public FileResourceBase
+{
+public:
+ FileDependency();
+ ~FileDependency();
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_FILEDEPENDENCY_H
diff --git a/src/lib/corelib/buildgraph/forward_decls.h b/src/lib/corelib/buildgraph/forward_decls.h
new file mode 100644
index 000000000..7f3986a9b
--- /dev/null
+++ b/src/lib/corelib/buildgraph/forward_decls.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_BG_FORWARD_DECLS_H
+#define QBS_BG_FORWARD_DECLS_H
+
+#include <QSharedPointer>
+
+namespace qbs {
+namespace Internal {
+
+class Artifact;
+class ProjectBuildData;
+class ProductBuildData;
+
+class Transformer;
+typedef QSharedPointer<Transformer> TransformerPtr;
+typedef QSharedPointer<const Transformer> TransformerConstPtr;
+
+class RulesEvaluationContext;
+typedef QSharedPointer<RulesEvaluationContext> RulesEvaluationContextPtr;
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BG_FORWARD_DECLS_H
diff --git a/src/lib/corelib/buildgraph/inputartifactscanner.cpp b/src/lib/corelib/buildgraph/inputartifactscanner.cpp
new file mode 100644
index 000000000..d5a0fabb6
--- /dev/null
+++ b/src/lib/corelib/buildgraph/inputartifactscanner.cpp
@@ -0,0 +1,369 @@
+/****************************************************************************
+**
+** 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 "inputartifactscanner.h"
+
+#include "artifact.h"
+#include "buildgraph.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "transformer.h"
+
+#include <language/language.h>
+#include <tools/fileinfo.h>
+#include <tools/scannerpluginmanager.h>
+#include <tools/qbsassert.h>
+
+#include <QDir>
+#include <QSet>
+#include <QStringList>
+#include <QVariantMap>
+
+namespace qbs {
+namespace Internal {
+
+InputArtifactScannerContext::InputArtifactScannerContext(ScanResultCache *scanResultCache)
+ : scanResultCache(scanResultCache)
+{
+}
+
+InputArtifactScannerContext::~InputArtifactScannerContext()
+{
+}
+
+static void collectIncludePaths(const QVariantMap &modules, QSet<QString> *collectedPaths)
+{
+ QMapIterator<QString, QVariant> iterator(modules);
+ while (iterator.hasNext()) {
+ iterator.next();
+ if (iterator.key() == "cpp") {
+ QVariant includePathsVariant = iterator .value().toMap().value("includePaths");
+ if (includePathsVariant.isValid())
+ collectedPaths->unite(QSet<QString>::fromList(includePathsVariant.toStringList()));
+ } else {
+ collectIncludePaths(iterator.value().toMap().value("modules").toMap(), collectedPaths);
+ }
+ }
+}
+
+static QStringList collectIncludePaths(const QVariantMap &modules)
+{
+ QSet<QString> collectedPaths;
+
+ collectIncludePaths(modules, &collectedPaths);
+ return QStringList(collectedPaths.toList());
+}
+
+static void resolveWithIncludePath(const QString &includePath,
+ const ScanResultCache::Dependency &dependency, const ResolvedProduct *product,
+ ResolvedDependency *result)
+{
+ QString absDirPath = dependency.dirPath().isEmpty() ? includePath : FileInfo::resolvePath(includePath, dependency.dirPath());
+ if (!dependency.isClean())
+ absDirPath = QDir::cleanPath(absDirPath);
+
+ ResolvedProject *project = product->project.data();
+ FileDependency *fileDependencyArtifact = 0;
+ Artifact *dependencyInProduct = 0;
+ Artifact *dependencyInOtherProduct = 0;
+ foreach (FileResourceBase *lookupResult, project->topLevelProject()
+ ->buildData->lookupFiles(absDirPath, dependency.fileName())) {
+ if ((fileDependencyArtifact = dynamic_cast<FileDependency *>(lookupResult)))
+ continue;
+ Artifact * const foundArtifact = dynamic_cast<Artifact *>(lookupResult);
+ if (foundArtifact->product == product)
+ dependencyInProduct = foundArtifact;
+ else
+ dependencyInOtherProduct = foundArtifact;
+ }
+
+ // prioritize found artifacts
+ if ((result->file = dependencyInProduct)
+ || (result->file = dependencyInOtherProduct)
+ || (result->file = fileDependencyArtifact))
+ {
+ result->filePath = result->file->filePath();
+ return;
+ }
+
+ QString absFilePath = absDirPath + QLatin1Char('/') + dependency.fileName();
+ if (FileInfo::exists(absFilePath))
+ result->filePath = absFilePath;
+}
+
+static bool scanWithScannerPlugin(ScannerPlugin *scannerPlugin,
+ const QString &filePathToBeScanned,
+ ScanResultCache::Result *scanResult)
+{
+ void *scannerHandle = scannerPlugin->open(filePathToBeScanned.utf16(), ScanForDependenciesFlag);
+ if (!scannerHandle)
+ return false;
+ while (true) {
+ int flags = 0;
+ int length = 0;
+ const char *szOutFilePath = scannerPlugin->next(scannerHandle, &length, &flags);
+ if (szOutFilePath == 0)
+ break;
+ QString outFilePath = QString::fromLocal8Bit(szOutFilePath, length);
+ if (outFilePath.isEmpty())
+ continue;
+ bool isLocalInclude = (flags & SC_LOCAL_INCLUDE_FLAG);
+ scanResult->deps += ScanResultCache::Dependency(outFilePath, isLocalInclude);
+ }
+ scannerPlugin->close(scannerHandle);
+ scanResult->valid = true;
+ return true;
+}
+
+
+InputArtifactScanner::InputArtifactScanner(Artifact *artifact, InputArtifactScannerContext *ctx,
+ const Logger &logger)
+ : m_artifact(artifact), m_context(ctx), m_newDependencyAdded(false), m_logger(logger)
+{
+}
+
+void InputArtifactScanner::scan()
+{
+ if (m_artifact->inputsScanned)
+ return;
+
+ m_artifact->inputsScanned = true;
+
+ // clear file dependencies; they will be regenerated
+ m_artifact->fileDependencies.clear();
+
+ // Remove all connections to children that were added by the dependency scanner.
+ // They will be regenerated.
+ foreach (Artifact *dependency, m_artifact->childrenAddedByScanner)
+ disconnect(m_artifact, dependency, m_logger);
+
+ ArtifactList::const_iterator it = m_artifact->transformer->inputs.begin();
+ for (; it != m_artifact->transformer->inputs.end(); ++it) {
+ Artifact *inputArtifact = *it;
+ QStringList includePaths;
+ bool mustCollectIncludePaths = false;
+
+ QSet<ScannerPlugin *> scanners;
+ foreach (const FileTag &fileTag, inputArtifact->fileTags) {
+ foreach (ScannerPlugin *scanner, ScannerPluginManager::scannersForFileTag(fileTag)) {
+ scanners += scanner;
+ if (scanner->flags & ScannerUsesCppIncludePaths)
+ mustCollectIncludePaths = true;
+ }
+ }
+
+ InputArtifactScannerContext::CacheItem &cacheItem = m_context->cache[inputArtifact->properties];
+ if (mustCollectIncludePaths) {
+ const bool cacheHit = cacheItem.valid;
+ if (cacheHit) {
+ includePaths = cacheItem.includePaths;
+ } else {
+ includePaths = collectIncludePaths(inputArtifact->properties->value().value("modules").toMap());
+ cacheItem.includePaths = includePaths;
+ cacheItem.valid = true;
+ }
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace()
+ << "[DEPSCAN] include paths (cache " << (cacheHit ? "hit)" : "miss)");
+ foreach (const QString &s, includePaths)
+ m_logger.qbsTrace() << " " << s;
+ }
+ }
+
+ const QStringList emptyIncludePaths;
+ foreach (ScannerPlugin *scanner, scanners) {
+ scanForFileDependencies(scanner,
+ (scanner->flags & ScannerUsesCppIncludePaths)
+ ? includePaths : emptyIncludePaths,
+ inputArtifact,
+ cacheItem.resolvedDependenciesCache[scanner]);
+ }
+ }
+}
+
+void InputArtifactScanner::scanForFileDependencies(ScannerPlugin *scannerPlugin,
+ const QStringList &includePaths, Artifact *inputArtifact, InputArtifactScannerContext::ResolvedDependenciesCache &resolvedDependenciesCache)
+{
+ if (m_logger.debugEnabled()) {
+ m_logger.qbsDebug() << QString::fromLocal8Bit("scanning %1 [%2]\n from %3")
+ .arg(inputArtifact->filePath()).arg(scannerPlugin->fileTag)
+ .arg(m_artifact->filePath());
+ }
+
+ QSet<QString> visitedFilePaths;
+ QStringList filePathsToScan;
+ filePathsToScan.append(inputArtifact->filePath());
+ QStringList * const filePathsToScanPtr =
+ (scannerPlugin->flags & ScannerRecursiveDependencies) ? &filePathsToScan : 0;
+
+ while (!filePathsToScan.isEmpty()) {
+ const QString filePathToBeScanned = filePathsToScan.takeFirst();
+ if (visitedFilePaths.contains(filePathToBeScanned))
+ continue;
+ visitedFilePaths.insert(filePathToBeScanned);
+
+ ScanResultCache::Result scanResult = m_context->scanResultCache->value(filePathToBeScanned);
+ if (!scanResult.valid) {
+ bool successfulScan = scanWithScannerPlugin(scannerPlugin, filePathToBeScanned, &scanResult);
+ if (!successfulScan)
+ continue;
+ m_context->scanResultCache->insert(filePathToBeScanned, scanResult);
+ }
+
+ resolveScanResultDependencies(includePaths, inputArtifact, scanResult, filePathToBeScanned,
+ filePathsToScanPtr, resolvedDependenciesCache);
+ }
+}
+
+void InputArtifactScanner::resolveScanResultDependencies(const QStringList &includePaths,
+ const Artifact *inputArtifact, const ScanResultCache::Result &scanResult,
+ const QString &filePathToBeScanned, QStringList *filePathsToScan, InputArtifactScannerContext::ResolvedDependenciesCache &resolvedDependenciesCache)
+{
+ QString baseDirOfInFilePath;
+ foreach (const ScanResultCache::Dependency &dependency, scanResult.deps) {
+ const QString &dependencyFilePath = dependency.filePath();
+ ResolvedDependency pristineResolvedDependency;
+ ResolvedDependency *resolvedDependency = &pristineResolvedDependency;
+ InputArtifactScannerContext::ResolvedDependencyCacheItem *cachedResolvedDependencyItem = 0;
+
+ if (FileInfo::isAbsolute(dependencyFilePath)) {
+ resolvedDependency->filePath = dependencyFilePath;
+ goto resolved;
+ }
+
+ if (dependency.isLocal()) {
+ // try base directory of source file
+ if (baseDirOfInFilePath.isNull())
+ baseDirOfInFilePath = FileInfo::path(filePathToBeScanned);
+ resolveWithIncludePath(baseDirOfInFilePath, dependency, inputArtifact->product.data(),
+ resolvedDependency);
+ if (resolvedDependency->isValid())
+ goto resolved;
+ }
+
+ cachedResolvedDependencyItem = &resolvedDependenciesCache[dependency.fileName()][dependency.dirPath()];
+ resolvedDependency = &cachedResolvedDependencyItem->resolvedDependency;
+ if (cachedResolvedDependencyItem->valid) {
+// qDebug() << "RESCACHE HIT" << dependency.filePath();
+ if (resolvedDependency->filePath.isEmpty())
+ goto unresolved;
+ goto resolved;
+ }
+// qDebug() << "RESCACHE MISS";
+ cachedResolvedDependencyItem->valid = true;
+
+ // try include paths
+ foreach (const QString &includePath, includePaths) {
+ resolveWithIncludePath(includePath, dependency, inputArtifact->product.data(),
+ resolvedDependency);
+ if (resolvedDependency->isValid())
+ goto resolved;
+ }
+
+unresolved:
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[DEPSCAN] unresolved '%1'")
+ .arg(dependencyFilePath);
+ }
+ continue;
+
+resolved:
+ // Do not scan artifacts that are being built. Otherwise we might read an incomplete
+ // file or conflict with the writing process.
+ if (filePathsToScan) {
+ Artifact *artifactDependency = dynamic_cast<Artifact *>(resolvedDependency->file);
+ if (!artifactDependency || artifactDependency->buildState != Artifact::Building)
+ filePathsToScan->append(resolvedDependency->filePath);
+ }
+ handleDependency(*resolvedDependency);
+ }
+}
+
+void InputArtifactScanner::handleDependency(ResolvedDependency &dependency)
+{
+ const ResolvedProductPtr product = m_artifact->product;
+ bool insertIntoProduct = true;
+ QBS_CHECK(m_artifact->artifactType == Artifact::Generated);
+ QBS_CHECK(product);
+
+ Artifact *artifactDependency = dynamic_cast<Artifact *>(dependency.file);
+ FileDependency *fileDependency
+ = artifactDependency ? 0 : dynamic_cast<FileDependency *>(dependency.file);
+
+ if (!dependency.file) {
+ // The dependency is an existing file but does not exist in the build graph.
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[DEPSCAN] + '%1'")
+ .arg(dependency.filePath);
+ }
+ fileDependency = new FileDependency();
+ dependency.file = fileDependency;
+ fileDependency->setFilePath(dependency.filePath);
+ product->topLevelProject()->buildData->insertFileDependency(fileDependency);
+ } else if (fileDependency) {
+ // The dependency exists in the project's list of file dependencies.
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[DEPSCAN] ok in deps '%1'")
+ .arg(dependency.filePath);
+ }
+ } else if (artifactDependency->product == product) {
+ // The dependency is in our product.
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[DEPSCAN] ok in product '%1'")
+ .arg(dependency.filePath);
+ }
+ insertIntoProduct = false;
+ } else {
+ // The dependency is in some other product.
+ ResolvedProduct * const otherProduct = artifactDependency->product;
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[DEPSCAN] found in product '%1': '%2'")
+ .arg(otherProduct->name, dependency.filePath);
+ }
+ insertIntoProduct = false;
+ }
+
+ if (m_artifact == dependency.file)
+ return;
+
+ if (fileDependency) {
+ m_artifact->fileDependencies.insert(fileDependency);
+ } else {
+ if (m_artifact->children.contains(artifactDependency))
+ return;
+ if (insertIntoProduct && !product->buildData->artifacts.contains(artifactDependency))
+ insertArtifact(product, artifactDependency, m_logger);
+ safeConnect(m_artifact, artifactDependency, m_logger);
+ m_artifact->childrenAddedByScanner += artifactDependency;
+ m_newDependencyAdded = true;
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/inputartifactscanner.h b/src/lib/corelib/buildgraph/inputartifactscanner.h
new file mode 100644
index 000000000..234d25d83
--- /dev/null
+++ b/src/lib/corelib/buildgraph/inputartifactscanner.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_INPUTARTIFACTSCANNER_H
+#define QBS_INPUTARTIFACTSCANNER_H
+
+#include "scanresultcache.h"
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+
+#include <QHash>
+#include <QStringList>
+
+struct ScannerPlugin;
+
+namespace qbs {
+namespace Internal {
+
+class Artifact;
+class FileResourceBase;
+class PropertyMapInternal;
+
+class ResolvedDependency
+{
+public:
+ ResolvedDependency()
+ : file(0)
+ {}
+
+ bool isValid() const { return !filePath.isNull(); }
+
+ QString filePath;
+ FileResourceBase *file;
+};
+
+class InputArtifactScannerContext
+{
+public:
+ InputArtifactScannerContext(ScanResultCache *scanResultCache);
+ ~InputArtifactScannerContext();
+
+private:
+ ScanResultCache *scanResultCache;
+
+ struct ResolvedDependencyCacheItem
+ {
+ ResolvedDependencyCacheItem()
+ : valid(false)
+ {}
+
+ bool valid;
+ ResolvedDependency resolvedDependency;
+ };
+
+ typedef QHash<QString, QHash<QString, ResolvedDependencyCacheItem> > ResolvedDependenciesCache;
+
+ struct CacheItem
+ {
+ CacheItem()
+ : valid(false)
+ {}
+
+ bool valid;
+ QStringList includePaths;
+ QHash<ScannerPlugin *, ResolvedDependenciesCache> resolvedDependenciesCache;
+ };
+
+ QHash<PropertyMapConstPtr, CacheItem> cache;
+
+ friend class InputArtifactScanner;
+};
+
+class InputArtifactScanner
+{
+public:
+ InputArtifactScanner(Artifact *artifact, InputArtifactScannerContext *ctx,
+ const Logger &logger);
+ void scan();
+ bool newDependencyAdded() const { return m_newDependencyAdded; }
+
+private:
+ void scanForFileDependencies(ScannerPlugin *scannerPlugin, const QStringList &includePaths,
+ Artifact *inputArtifact, InputArtifactScannerContext::ResolvedDependenciesCache &cacheItem);
+ void resolveScanResultDependencies(const QStringList &includePaths,
+ const Artifact *inputArtifact, const ScanResultCache::Result &scanResult,
+ const QString &filePathToBeScanned, QStringList *filePathsToScan, InputArtifactScannerContext::ResolvedDependenciesCache &cacheItem);
+ void handleDependency(ResolvedDependency &dependency);
+
+ Artifact * const m_artifact;
+ InputArtifactScannerContext *const m_context;
+ bool m_newDependencyAdded;
+ Logger m_logger;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_INPUTARTIFACTSCANNER_H
diff --git a/src/lib/corelib/buildgraph/jscommandexecutor.cpp b/src/lib/corelib/buildgraph/jscommandexecutor.cpp
new file mode 100644
index 000000000..ef84da432
--- /dev/null
+++ b/src/lib/corelib/buildgraph/jscommandexecutor.cpp
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** 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 "jscommandexecutor.h"
+
+#include "artifact.h"
+#include "buildgraph.h"
+#include "command.h"
+#include "transformer.h"
+
+#include <language/language.h>
+#include <language/preparescriptobserver.h>
+#include <language/scriptengine.h>
+#include <logging/logger.h>
+#include <tools/codelocation.h>
+#include <tools/error.h>
+
+#include <QEventLoop>
+#include <QThread>
+#include <QTimer>
+
+namespace qbs {
+namespace Internal {
+
+struct JavaScriptCommandResult
+{
+ bool success;
+ QString errorMessage;
+ CodeLocation errorLocation;
+};
+
+class JsCommandExecutorThreadObject : public QObject
+{
+ Q_OBJECT
+public:
+ JsCommandExecutorThreadObject(const Logger &logger)
+ : m_logger(logger)
+ , m_scriptEngine(0)
+ {
+ }
+
+ const JavaScriptCommandResult &result() const
+ {
+ return m_result;
+ }
+
+signals:
+ void finished();
+
+public slots:
+ void start(const JavaScriptCommand *cmd, Transformer *transformer)
+ {
+ m_result.success = true;
+ m_result.errorMessage.clear();
+ ScriptEngine * const scriptEngine = provideScriptEngine();
+ QScriptValue scope = scriptEngine->newObject();
+ PrepareScriptObserver observer(scriptEngine);
+ setupScriptEngineForFile(scriptEngine, transformer->rule->script->fileContext, scope);
+ setupScriptEngineForProduct(scriptEngine, transformer->product(), transformer->rule, scope,
+ &observer);
+ transformer->setupInputs(scriptEngine, scope);
+ transformer->setupOutputs(scriptEngine, scope);
+
+ for (QVariantMap::const_iterator it = cmd->properties().constBegin();
+ it != cmd->properties().constEnd(); ++it) {
+ scope.setProperty(it.key(), scriptEngine->toScriptValue(it.value()));
+ }
+
+ QScriptContext *ctx = scriptEngine->currentContext();
+ ctx->pushScope(scope);
+ scriptEngine->evaluate(cmd->sourceCode());
+ ctx->popScope();
+ transformer->propertiesRequestedInCommands
+ += scriptEngine->propertiesRequestedInScript();
+ scriptEngine->clearRequestedProperties();
+ if (scriptEngine->hasUncaughtException()) {
+ m_result.success = false;
+ m_result.errorMessage = scriptEngine->uncaughtException().toString();
+ // ### We don't know the line number of the command's sourceCode property assignment.
+ m_result.errorLocation = cmd->codeLocation();
+ }
+ emit finished();
+ }
+
+private:
+ ScriptEngine *provideScriptEngine()
+ {
+ if (!m_scriptEngine)
+ m_scriptEngine = new ScriptEngine(m_logger, this);
+ return m_scriptEngine;
+ }
+
+ Logger m_logger;
+ ScriptEngine *m_scriptEngine;
+ JavaScriptCommandResult m_result;
+};
+
+
+JsCommandExecutor::JsCommandExecutor(const Logger &logger, QObject *parent)
+ : AbstractCommandExecutor(logger, parent)
+ , m_thread(new QThread(this))
+ , m_objectInThread(new JsCommandExecutorThreadObject(logger))
+ , m_running(false)
+{
+ m_objectInThread->moveToThread(m_thread);
+ connect(m_objectInThread, SIGNAL(finished()), this, SLOT(onJavaScriptCommandFinished()));
+ connect(this, SIGNAL(startRequested(const JavaScriptCommand *, Transformer *)),
+ m_objectInThread, SLOT(start(const JavaScriptCommand *, Transformer *)));
+}
+
+JsCommandExecutor::~JsCommandExecutor()
+{
+ waitForFinished();
+ delete m_objectInThread;
+ m_thread->quit();
+ m_thread->wait();
+}
+
+void JsCommandExecutor::waitForFinished()
+{
+ if (!m_running)
+ return;
+ QEventLoop loop;
+ loop.connect(m_objectInThread, SIGNAL(finished()), SLOT(quit()));
+ loop.exec();
+}
+
+void JsCommandExecutor::doStart()
+{
+ QBS_ASSERT(!m_running, return);
+ m_thread->start();
+
+ if (dryRun()) {
+ QTimer::singleShot(0, this, SIGNAL(finished())); // Don't call back on the caller.
+ return;
+ }
+
+ m_running = true;
+ emit startRequested(jsCommand(), transformer());
+}
+
+void JsCommandExecutor::onJavaScriptCommandFinished()
+{
+ m_running = false;
+ const JavaScriptCommandResult &result = m_objectInThread->result();
+ if (!result.success) {
+ logger().qbsDebug() << "JS context:\n" << jsCommand()->properties();
+ logger().qbsDebug() << "JS code:\n" << jsCommand()->sourceCode();
+ QString msg = "Error while executing JavaScriptCommand:\n";
+ msg += result.errorMessage;
+ emit error(ErrorInfo(msg, result.errorLocation));
+ }
+ emit finished();
+}
+
+const JavaScriptCommand *JsCommandExecutor::jsCommand() const
+{
+ return static_cast<const JavaScriptCommand *>(command());
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#include "jscommandexecutor.moc"
diff --git a/src/lib/corelib/buildgraph/jscommandexecutor.h b/src/lib/corelib/buildgraph/jscommandexecutor.h
new file mode 100644
index 000000000..94a25e610
--- /dev/null
+++ b/src/lib/corelib/buildgraph/jscommandexecutor.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_JSCOMMANDEXECUTOR_H
+#define QBS_JSCOMMANDEXECUTOR_H
+
+#include "abstractcommandexecutor.h"
+
+#include <QString>
+
+namespace qbs {
+class CodeLocation;
+
+namespace Internal {
+class JavaScriptCommand;
+class JsCommandExecutorThreadObject;
+
+class JsCommandExecutor : public AbstractCommandExecutor
+{
+ Q_OBJECT
+public:
+ explicit JsCommandExecutor(const Logger &logger, QObject *parent = 0);
+ ~JsCommandExecutor();
+
+signals:
+ void startRequested(const JavaScriptCommand *cmd, Transformer *transformer);
+
+private slots:
+ void onJavaScriptCommandFinished();
+
+private:
+ void doStart();
+ void waitForFinished();
+
+ const JavaScriptCommand *jsCommand() const;
+
+ QThread *m_thread;
+ JsCommandExecutorThreadObject *m_objectInThread;
+ bool m_running;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_JSCOMMANDEXECUTOR_H
diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.cpp b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
new file mode 100644
index 000000000..8887cc99f
--- /dev/null
+++ b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
@@ -0,0 +1,346 @@
+/****************************************************************************
+**
+** 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 "processcommandexecutor.h"
+
+#include "artifact.h"
+#include "command.h"
+#include "transformer.h"
+
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/hostosinfo.h>
+#include <tools/processresult.h>
+#include <tools/processresult_p.h>
+#include <tools/qbsassert.h>
+
+#include <QDir>
+#include <QScriptEngine>
+#include <QScriptValue>
+#include <QTemporaryFile>
+#include <QTimer>
+
+namespace qbs {
+namespace Internal {
+
+static QStringList populateExecutableSuffixes()
+{
+ QStringList result;
+ result << QString();
+ if (HostOsInfo::isWindowsHost()) {
+ result << QLatin1String(".com") << QLatin1String(".exe")
+ << QLatin1String(".bat") << QLatin1String(".cmd");
+ }
+ return result;
+}
+
+const QStringList ProcessCommandExecutor::m_executableSuffixes = populateExecutableSuffixes();
+
+ProcessCommandExecutor::ProcessCommandExecutor(const Logger &logger, QObject *parent)
+ : AbstractCommandExecutor(logger, parent)
+{
+ connect(&m_process, SIGNAL(error(QProcess::ProcessError)), SLOT(onProcessError()));
+ connect(&m_process, SIGNAL(finished(int)), SLOT(onProcessFinished(int)));
+}
+
+// returns an empty string or one that starts with a space!
+static QString commandArgsToString(const QStringList &args)
+{
+ QString result;
+ QRegExp ws("\\s");
+ foreach (const QString &arg, args) {
+ result += QLatin1Char(' ');
+
+ if (arg.contains(ws) || arg.isEmpty())
+ result += QLatin1Char('"') + arg + QLatin1Char('"');
+ else
+ result += arg;
+ }
+ return result;
+}
+
+void ProcessCommandExecutor::doStart()
+{
+ QBS_ASSERT(m_process.state() == QProcess::NotRunning, return);
+
+ const ProcessCommand * const cmd = processCommand();
+ QString program = cmd->program();
+ if (FileInfo::isAbsolute(cmd->program())) {
+ if (HostOsInfo::isWindowsHost())
+ program = findProcessCommandBySuffix();
+ } else {
+ program = findProcessCommandInPath();
+ }
+
+ QProcessEnvironment env = m_buildEnvironment;
+ const QProcessEnvironment &additionalVariables = cmd->environment();
+ foreach (const QString &key, additionalVariables.keys())
+ env.insert(key, additionalVariables.value(key));
+ m_process.setProcessEnvironment(env);
+
+ QStringList arguments = cmd->arguments();
+ QString argString = commandArgsToString(arguments);
+
+ if (dryRun()) {
+ QTimer::singleShot(0, this, SIGNAL(finished())); // Don't call back on the caller.
+ return;
+ }
+
+ // Automatically use response files, if the command line gets to long.
+ if (!cmd->responseFileUsagePrefix().isEmpty()) {
+ const int commandLineLength = program.length() + argString.length();
+ if (cmd->responseFileThreshold() >= 0 && commandLineLength > cmd->responseFileThreshold()) {
+ if (logger().debugEnabled()) {
+ logger().qbsDebug() << QString::fromLocal8Bit("[EXEC] Using response file. "
+ "Threshold is %1. Command line length %2.")
+ .arg(cmd->responseFileThreshold()).arg(commandLineLength);
+ }
+
+ // The QTemporaryFile keeps a handle on the file, even if closed.
+ // On Windows, some commands (e.g. msvc link.exe) won't accept that.
+ // We need to delete the file manually, later.
+ QTemporaryFile responseFile;
+ responseFile.setAutoRemove(false);
+ responseFile.setFileTemplate(QDir::tempPath() + "/qbsresp");
+ if (!responseFile.open()) {
+ emit error(ErrorInfo(Tr::tr("Cannot create response file '%1'.")
+ .arg(responseFile.fileName())));
+ return;
+ }
+ for (int i = 0; i < cmd->arguments().count(); ++i) {
+ responseFile.write(cmd->arguments().at(i).toLocal8Bit());
+ responseFile.write("\n");
+ }
+ responseFile.close();
+ m_responseFileName = responseFile.fileName();
+ arguments.clear();
+ arguments += QDir::toNativeSeparators(cmd->responseFileUsagePrefix()
+ + responseFile.fileName());
+ }
+ }
+
+ logger().qbsDebug() << "[EXEC] Running external process; full command line is: " << program
+ << commandArgsToString(arguments);
+ logger().qbsTrace() << "[EXEC] Additional environment:" << additionalVariables.toStringList();
+ m_process.setWorkingDirectory(cmd->workingDir());
+ m_process.start(program, arguments);
+
+ m_program = program;
+ m_arguments = arguments;
+}
+
+void ProcessCommandExecutor::waitForFinished()
+{
+ m_process.waitForFinished(-1);
+}
+
+QString ProcessCommandExecutor::filterProcessOutput(const QByteArray &_output,
+ const QString &filterFunctionSource)
+{
+ const QString output = QString::fromLocal8Bit(_output);
+ if (filterFunctionSource.isEmpty())
+ return output;
+
+ QScriptValue filterFunction = scriptEngine()->evaluate("var f = " + filterFunctionSource + "; f");
+ if (!filterFunction.isFunction()) {
+ emit error(ErrorInfo(Tr::tr("Error in filter function: %1.\n%2")
+ .arg(filterFunctionSource, filterFunction.toString())));
+ return output;
+ }
+
+ QScriptValue outputArg = scriptEngine()->newArray(1);
+ outputArg.setProperty(0, scriptEngine()->toScriptValue(output));
+ QScriptValue filteredOutput = filterFunction.call(scriptEngine()->undefinedValue(), outputArg);
+ if (scriptEngine()->hasErrorOrException(filteredOutput)) {
+ emit error(ErrorInfo(Tr::tr("Error when calling output filter function: %1")
+ .arg(filteredOutput.toString())));
+ return output;
+ }
+
+ return filteredOutput.toString();
+}
+
+void ProcessCommandExecutor::sendProcessOutput(bool success)
+{
+ ProcessResult result;
+ result.d->executableFilePath = m_program;
+ result.d->arguments = m_arguments;
+ result.d->workingDirectory = m_process.workingDirectory();
+ if (result.workingDirectory().isEmpty())
+ result.d->workingDirectory = QDir::currentPath();
+ result.d->exitCode = m_process.exitCode();
+ result.d->exitStatus = m_process.exitStatus();
+ result.d->success = success;
+
+ QString tmp = filterProcessOutput(m_process.readAllStandardOutput(),
+ processCommand()->stdoutFilterFunction());
+ if (!tmp.isEmpty()) {
+ if (tmp.endsWith(QLatin1Char('\n')))
+ tmp.chop(1);
+ result.d->stdOut = tmp.split(QLatin1Char('\n'));
+ }
+ tmp = filterProcessOutput(m_process.readAllStandardError(),
+ processCommand()->stderrFilterFunction());
+ if (!tmp.isEmpty()) {
+ if (tmp.endsWith(QLatin1Char('\n')))
+ tmp.chop(1);
+ result.d->stdErr = tmp.split(QLatin1Char('\n'));
+ }
+
+ emit reportProcessResult(result);
+}
+
+void ProcessCommandExecutor::onProcessError()
+{
+ sendProcessOutput(false);
+ removeResponseFile();
+ QString errorMessage;
+ const QString binary = QDir::toNativeSeparators(processCommand()->program());
+ switch (m_process.error()) {
+ case QProcess::FailedToStart:
+ errorMessage = Tr::tr("The process '%1' could not be started: %2").
+ arg(binary, m_process.errorString());
+ break;
+ case QProcess::Crashed:
+ errorMessage = Tr::tr("The process '%1' crashed.").arg(binary);
+ break;
+ case QProcess::Timedout:
+ errorMessage = Tr::tr("The process '%1' timed out.").arg(binary);
+ break;
+ case QProcess::ReadError:
+ errorMessage = Tr::tr("Error reading process output from '%1'.").arg(binary);
+ break;
+ case QProcess::WriteError:
+ errorMessage = Tr::tr("Error writing to process '%1'.").arg(binary);
+ break;
+ default:
+ errorMessage = Tr::tr("Unknown process error running '%1'.").arg(binary);
+ break;
+ }
+ emit error(ErrorInfo(errorMessage));
+}
+
+void ProcessCommandExecutor::onProcessFinished(int exitCode)
+{
+ removeResponseFile();
+ const bool errorOccurred = quint32(exitCode) > quint32(processCommand()->maxExitCode());
+ sendProcessOutput(!errorOccurred);
+
+ if (Q_UNLIKELY(errorOccurred)) {
+ emit error(ErrorInfo(Tr::tr("Process failed with exit code %1.").arg(exitCode)));
+ return;
+ }
+
+
+ emit finished();
+}
+
+void ProcessCommandExecutor::removeResponseFile()
+{
+ if (m_responseFileName.isEmpty())
+ return;
+ QFile::remove(m_responseFileName);
+ m_responseFileName.clear();
+}
+
+QString ProcessCommandExecutor::findProcessCommandInPath()
+{
+ const ResolvedProductPtr product = transformer()->product();
+ const ProcessCommand * const cmd = processCommand();
+ QString fullProgramPath = product->executablePathCache.value(cmd->program());
+ if (!fullProgramPath.isEmpty())
+ return fullProgramPath;
+
+ fullProgramPath = cmd->program();
+ if (logger().traceEnabled())
+ logger().qbsTrace() << "[EXEC] looking for executable in PATH " << fullProgramPath;
+ const QProcessEnvironment &buildEnvironment = product->buildEnvironment;
+ QStringList pathEnv = buildEnvironment.value("PATH").split(HostOsInfo::pathListSeparator(),
+ QString::SkipEmptyParts);
+ if (HostOsInfo::isWindowsHost())
+ pathEnv.prepend(QLatin1String("."));
+ for (int i = 0; i < pathEnv.count(); ++i) {
+ QString directory = pathEnv.at(i);
+ if (directory == QLatin1String("."))
+ directory = cmd->workingDir();
+ if (!directory.isEmpty()) {
+ const QChar lastChar = directory.at(directory.count() - 1);
+ if (lastChar != QLatin1Char('/') && lastChar != QLatin1Char('\\'))
+ directory.append(QLatin1Char('/'));
+ }
+ if (findProcessCandidateCheck(directory, fullProgramPath, fullProgramPath))
+ break;
+ }
+ product->executablePathCache.insert(cmd->program(), fullProgramPath);
+ return fullProgramPath;
+}
+
+QString ProcessCommandExecutor::findProcessCommandBySuffix()
+{
+ const ResolvedProductPtr product = transformer()->product();
+ const ProcessCommand * const cmd = processCommand();
+ QString fullProgramPath = product->executablePathCache.value(cmd->program());
+ if (!fullProgramPath.isEmpty())
+ return fullProgramPath;
+
+ fullProgramPath = cmd->program();
+ if (logger().traceEnabled())
+ logger().qbsTrace() << "[EXEC] looking for executable by suffix " << fullProgramPath;
+ const QString emptyDirectory;
+ findProcessCandidateCheck(emptyDirectory, fullProgramPath, fullProgramPath);
+ product->executablePathCache.insert(cmd->program(), fullProgramPath);
+ return fullProgramPath;
+}
+
+bool ProcessCommandExecutor::findProcessCandidateCheck(const QString &directory,
+ const QString &program, QString &fullProgramPath)
+{
+ for (int i = 0; i < m_executableSuffixes.count(); ++i) {
+ QString candidate = directory + program + m_executableSuffixes.at(i);
+ if (logger().traceEnabled())
+ logger().qbsTrace() << "[EXEC] candidate: " << candidate;
+ if (FileInfo::exists(candidate)) {
+ fullProgramPath = candidate;
+ return true;
+ }
+ }
+ return false;
+}
+
+const ProcessCommand *ProcessCommandExecutor::processCommand() const
+{
+ return static_cast<const ProcessCommand *>(command());
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.h b/src/lib/corelib/buildgraph/processcommandexecutor.h
new file mode 100644
index 000000000..c90141303
--- /dev/null
+++ b/src/lib/corelib/buildgraph/processcommandexecutor.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_PROCESSCOMMANDEXECUTOR_H
+#define QBS_PROCESSCOMMANDEXECUTOR_H
+
+#include "abstractcommandexecutor.h"
+
+#include <QProcess>
+#include <QProcessEnvironment>
+#include <QString>
+
+namespace qbs {
+class ProcessResult;
+
+namespace Internal {
+class ProcessCommand;
+
+class ProcessCommandExecutor : public AbstractCommandExecutor
+{
+ Q_OBJECT
+public:
+ explicit ProcessCommandExecutor(const Internal::Logger &logger, QObject *parent = 0);
+
+ void setProcessEnvironment(const QProcessEnvironment &processEnvironment) {
+ m_buildEnvironment = processEnvironment;
+ }
+
+signals:
+ void reportProcessResult(const qbs::ProcessResult &result);
+
+private slots:
+ void onProcessError();
+ void onProcessFinished(int exitCode);
+
+private:
+ void doStart();
+ void waitForFinished();
+
+ void startProcessCommand();
+ QString filterProcessOutput(const QByteArray &output, const QString &filterFunctionSource);
+ void sendProcessOutput(bool success);
+ void removeResponseFile();
+ QString findProcessCommandInPath();
+ QString findProcessCommandBySuffix();
+ bool findProcessCandidateCheck(const QString &directory, const QString &program,
+ QString &fullProgramPath);
+ const ProcessCommand *processCommand() const;
+
+private:
+ static const QStringList m_executableSuffixes;
+ QString m_program;
+ QStringList m_arguments;
+
+ QProcess m_process;
+ QProcessEnvironment m_buildEnvironment;
+ QString m_responseFileName;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PROCESSCOMMANDEXECUTOR_H
diff --git a/src/lib/corelib/buildgraph/productbuilddata.cpp b/src/lib/corelib/buildgraph/productbuilddata.cpp
new file mode 100644
index 000000000..0b9f36ab1
--- /dev/null
+++ b/src/lib/corelib/buildgraph/productbuilddata.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** 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 "productbuilddata.h"
+
+#include "artifact.h"
+#include "projectbuilddata.h"
+#include <language/language.h>
+#include <logging/logger.h>
+#include <tools/error.h>
+#include <tools/persistence.h>
+#include <tools/qbsassert.h>
+
+namespace qbs {
+namespace Internal {
+
+ProductBuildData::~ProductBuildData()
+{
+ qDeleteAll(artifacts);
+}
+
+void ProductBuildData::load(PersistentPool &pool)
+{
+ pool.loadContainer(artifacts);
+ pool.loadContainer(targetArtifacts);
+}
+
+void ProductBuildData::store(PersistentPool &pool) const
+{
+ pool.storeContainer(artifacts);
+ pool.storeContainer(targetArtifacts);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/productbuilddata.h b/src/lib/corelib/buildgraph/productbuilddata.h
new file mode 100644
index 000000000..15c8cc78f
--- /dev/null
+++ b/src/lib/corelib/buildgraph/productbuilddata.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PRODUCTBUILDDATA_H
+#define QBS_PRODUCTBUILDDATA_H
+
+#include "artifactlist.h"
+#include <language/forward_decls.h>
+
+#include <tools/persistentobject.h>
+
+#include <QList>
+#include <QSet>
+
+namespace qbs {
+namespace Internal {
+class Logger;
+
+class ProductBuildData : public PersistentObject
+{
+public:
+ ~ProductBuildData();
+
+ QSet<Artifact *> targetArtifacts;
+ ArtifactList artifacts;
+ QList<RuleConstPtr> topSortedRules;
+
+ // Do not store, initialized in executor. Higher prioritized artifacts are built first.
+ unsigned int buildPriority;
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PRODUCTBUILDDATA_H
diff --git a/src/lib/corelib/buildgraph/productinstaller.cpp b/src/lib/corelib/buildgraph/productinstaller.cpp
new file mode 100644
index 000000000..867ad1370
--- /dev/null
+++ b/src/lib/corelib/buildgraph/productinstaller.cpp
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** 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 "productinstaller.h"
+
+#include "artifact.h"
+#include "productbuilddata.h"
+#include <language/language.h>
+#include <logging/translator.h>
+#include <tools/qbsassert.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/propertyfinder.h>
+#include <tools/progressobserver.h>
+#include <tools/qbsassert.h>
+
+#include <QDir>
+#include <QFileInfo>
+
+namespace qbs {
+namespace Internal {
+
+ProductInstaller::ProductInstaller(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const InstallOptions &options,
+ ProgressObserver *observer, const Logger &logger)
+ : m_project(project),
+ m_products(products),
+ m_options(options),
+ m_observer(observer),
+ m_logger(logger)
+{
+ if (!m_options.installRoot().isEmpty()) {
+ QFileInfo installRootFileInfo(m_options.installRoot());
+ QBS_ASSERT(installRootFileInfo.isAbsolute(), /* just complain */);
+ if (m_options.removeExistingInstallation()) {
+ const QString cfp = installRootFileInfo.canonicalFilePath();
+ if (cfp == QFileInfo(QDir::rootPath()).canonicalFilePath())
+ throw ErrorInfo(Tr::tr("Refusing to remove root directory."));
+ if (cfp == QFileInfo(QDir::homePath()).canonicalFilePath())
+ throw ErrorInfo(Tr::tr("Refusing to remove home directory."));
+ }
+ return;
+ }
+
+ if (m_options.installIntoSysroot()) {
+ if (m_options.removeExistingInstallation())
+ throw ErrorInfo(Tr::tr("Refusing to remove sysroot."));
+ }
+ initInstallRoot(project.data(), m_options);
+}
+
+void ProductInstaller::install()
+{
+ if (m_options.removeExistingInstallation())
+ removeInstallRoot();
+
+ QList<const Artifact *> artifactsToInstall;
+ foreach (const ResolvedProductConstPtr &product, m_products) {
+ QBS_CHECK(product->buildData);
+ foreach (const Artifact *artifact, product->buildData->artifacts) {
+ if (artifact->properties->qbsPropertyValue(QLatin1String("install")).toBool())
+ artifactsToInstall += artifact;
+ }
+ }
+ m_observer->initialize(Tr::tr("Installing"), artifactsToInstall.count());
+
+ foreach (const Artifact * const a, artifactsToInstall) {
+ copyFile(a);
+ m_observer->incrementProgressValue();
+ }
+}
+
+QString ProductInstaller::targetFilePath(const TopLevelProject *project,
+ const QString &sourceFilePath, const PropertyMapConstPtr &properties,
+ InstallOptions &options, QString *targetDirectory)
+{
+ if (!properties->qbsPropertyValue(QLatin1String("install")).toBool())
+ return QString();
+ const QString relativeInstallDir
+ = properties->qbsPropertyValue(QLatin1String("installDir")).toString();
+ const QString installPrefix
+ = properties->qbsPropertyValue(QLatin1String("installPrefix")).toString();
+ initInstallRoot(project, options);
+ QString targetDir = options.installRoot();
+ targetDir.append(QLatin1Char('/')).append(installPrefix)
+ .append(QLatin1Char('/')).append(relativeInstallDir);
+ targetDir = QDir::cleanPath(targetDir);
+ const QString targetFilePath = QDir::cleanPath(targetDir + QLatin1Char('/')
+ + FileInfo::fileName(sourceFilePath));
+ if (targetDirectory)
+ *targetDirectory = targetDir;
+ return targetFilePath;
+}
+
+void ProductInstaller::initInstallRoot(const TopLevelProject *project,
+ InstallOptions &options)
+{
+ if (!options.installRoot().isEmpty())
+ return;
+
+ if (options.installIntoSysroot()) {
+ options.setInstallRoot(PropertyFinder().propertyValue(project->buildConfiguration(),
+ QLatin1String("qbs"), QLatin1String("sysroot")).toString());
+ } else {
+ options.setInstallRoot(project->buildDirectory + QLatin1Char('/') +
+ InstallOptions::defaultInstallRoot());
+ }
+}
+
+void ProductInstaller::removeInstallRoot()
+{
+ const QString nativeInstallRoot = QDir::toNativeSeparators(m_options.installRoot());
+ if (m_options.dryRun()) {
+ m_logger.qbsInfo() << Tr::tr("Would remove install root '%1'.").arg(nativeInstallRoot);
+ return;
+ }
+ m_logger.qbsDebug() << QString::fromLocal8Bit("Removing install root '%1'.")
+ .arg(nativeInstallRoot);
+
+ QString errorMessage;
+ if (!removeDirectoryWithContents(m_options.installRoot(), &errorMessage)) {
+ const QString fullErrorMessage = Tr::tr("Cannot remove install root '%1': %2")
+ .arg(QDir::toNativeSeparators(m_options.installRoot()), errorMessage);
+ handleError(fullErrorMessage);
+ }
+}
+
+void ProductInstaller::copyFile(const Artifact *artifact)
+{
+ if (m_observer->canceled()) {
+ throw ErrorInfo(Tr::tr("Installation canceled for configuration '%1'.")
+ .arg(m_products.first()->project->topLevelProject()->id()));
+ }
+ QString targetDir;
+ const QString targetFilePath = this->targetFilePath(m_project.data(), artifact->filePath(),
+ artifact->properties, m_options, &targetDir);
+ const QString nativeFilePath = QDir::toNativeSeparators(artifact->filePath());
+ const QString nativeTargetDir = QDir::toNativeSeparators(targetDir);
+ if (m_options.dryRun()) {
+ m_logger.qbsInfo() << Tr::tr("Would copy file '%1' into target directory '%2'.")
+ .arg(nativeFilePath, nativeTargetDir);
+ return;
+ }
+ m_logger.qbsDebug() << QString::fromLocal8Bit("Copying file '%1' into target directory '%2'.")
+ .arg(nativeFilePath, nativeTargetDir);
+
+ if (!QDir::root().mkpath(targetDir)) {
+ handleError(Tr::tr("Directory '%1' could not be created.").arg(nativeTargetDir));
+ return;
+ }
+ QString errorMessage;
+ if (!copyFileRecursion(artifact->filePath(), targetFilePath, true, &errorMessage))
+ handleError(Tr::tr("Installation error: %1").arg(errorMessage));
+}
+
+void ProductInstaller::handleError(const QString &message)
+{
+ if (!m_options.keepGoing())
+ throw ErrorInfo(message);
+ m_logger.qbsWarning() << message;
+}
+
+} // namespace Intern
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/productinstaller.h b/src/lib/corelib/buildgraph/productinstaller.h
new file mode 100644
index 000000000..3f970b6aa
--- /dev/null
+++ b/src/lib/corelib/buildgraph/productinstaller.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PRODUCT_INSTALLER_H
+#define QBS_PRODUCT_INSTALLER_H
+
+#include "forward_decls.h"
+
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+#include <tools/installoptions.h>
+
+#include <QList>
+
+namespace qbs {
+namespace Internal {
+class ProgressObserver;
+
+class ProductInstaller
+{
+public:
+ ProductInstaller(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ const InstallOptions &options, ProgressObserver *observer, const Logger &logger);
+ void install();
+
+ static QString targetFilePath(const TopLevelProject *project,
+ const QString &sourceFilePath, const PropertyMapConstPtr &properties,
+ InstallOptions &options, QString *targetDirectory = 0);
+ static void initInstallRoot(const TopLevelProject *project, InstallOptions &options);
+
+private:
+ void removeInstallRoot();
+ void copyFile(const Artifact *artifact);
+ void handleError(const QString &message);
+
+ const TopLevelProjectConstPtr m_project;
+ const QList<ResolvedProductPtr> m_products;
+ InstallOptions m_options;
+ ProgressObserver * const m_observer;
+ Logger m_logger;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Header guard
diff --git a/src/lib/corelib/buildgraph/projectbuilddata.cpp b/src/lib/corelib/buildgraph/projectbuilddata.cpp
new file mode 100644
index 000000000..1ba53abf2
--- /dev/null
+++ b/src/lib/corelib/buildgraph/projectbuilddata.cpp
@@ -0,0 +1,408 @@
+/****************************************************************************
+**
+** 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 "projectbuilddata.h"
+
+#include "artifact.h"
+#include "buildgraph.h"
+#include "productbuilddata.h"
+#include "command.h"
+#include "rulesapplicator.h"
+#include "rulesevaluationcontext.h"
+#include "transformer.h"
+#include <language/language.h>
+#include <language/preparescriptobserver.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 {
+
+ProjectBuildData::ProjectBuildData(const ProjectBuildData *other)
+ : isDirty(true), m_doCleanupInDestructor(true)
+{
+ // This is needed for temporary duplication of build data when doing change tracking.
+ if (other) {
+ *this = *other;
+ m_doCleanupInDestructor = false;
+ }
+}
+
+ProjectBuildData::~ProjectBuildData()
+{
+ if (!m_doCleanupInDestructor)
+ return;
+ qDeleteAll(fileDependencies);
+}
+
+QString ProjectBuildData::deriveBuildGraphFilePath(const QString &buildDir, const QString &projectId)
+{
+ return buildDir + QLatin1Char('/') + projectId + QLatin1String(".bg");
+}
+
+void ProjectBuildData::insertIntoLookupTable(FileResourceBase *fileres)
+{
+ QList<FileResourceBase *> &lst
+ = m_artifactLookupTable[fileres->fileName()][fileres->dirPath()];
+ if (!lst.contains(fileres))
+ lst.append(fileres);
+}
+
+void ProjectBuildData::removeFromLookupTable(FileResourceBase *fileres)
+{
+ m_artifactLookupTable[fileres->fileName()][fileres->dirPath()].removeOne(fileres);
+}
+
+QList<FileResourceBase *> ProjectBuildData::lookupFiles(const QString &filePath) const
+{
+ QString dirPath, fileName;
+ FileInfo::splitIntoDirectoryAndFileName(filePath, &dirPath, &fileName);
+ return lookupFiles(dirPath, fileName);
+}
+
+QList<FileResourceBase *> ProjectBuildData::lookupFiles(const QString &dirPath,
+ const QString &fileName) const
+{
+ return m_artifactLookupTable.value(fileName).value(dirPath);
+}
+
+QList<FileResourceBase *> ProjectBuildData::lookupFiles(const Artifact *artifact) const
+{
+ return lookupFiles(artifact->dirPath(), artifact->fileName());
+}
+
+void ProjectBuildData::insertFileDependency(FileDependency *dependency)
+{
+ fileDependencies += dependency;
+ insertIntoLookupTable(dependency);
+}
+
+static void disconnectArtifactChildren(Artifact *artifact, const Logger &logger)
+{
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] disconnectChildren: '%1'")
+ .arg(relativeArtifactFileName(artifact));
+ }
+ foreach (Artifact * const child, artifact->children)
+ child->parents.remove(artifact);
+ artifact->children.clear();
+ artifact->childrenAddedByScanner.clear();
+}
+
+static void disconnectArtifactParents(Artifact *artifact, ProjectBuildData *projectBuildData,
+ const Logger &logger)
+{
+ if (logger.traceEnabled()) {
+ logger.qbsTrace() << QString::fromLocal8Bit("[BG] disconnectParents: '%1'")
+ .arg(relativeArtifactFileName(artifact));
+ }
+ foreach (Artifact * const parent, artifact->parents) {
+ parent->children.remove(artifact);
+ parent->childrenAddedByScanner.remove(artifact);
+ if (parent->transformer) {
+ parent->transformer->inputs.remove(artifact);
+ projectBuildData->artifactsThatMustGetNewTransformers += parent;
+ }
+ }
+
+ artifact->parents.clear();
+}
+
+static void disconnectArtifact(Artifact *artifact, ProjectBuildData *projectBuildData,
+ const Logger &logger)
+{
+ disconnectArtifactChildren(artifact, logger);
+ disconnectArtifactParents(artifact, projectBuildData, logger);
+}
+
+/*!
+ * Removes the artifact and all the artifacts that depend exclusively on it.
+ * Example: if you remove a cpp artifact then the obj artifact is removed but
+ * not the resulting application (if there's more then one cpp artifact).
+ */
+void ProjectBuildData::removeArtifactAndExclusiveDependents(Artifact *artifact,
+ const Logger &logger, bool removeFromProduct,
+ ArtifactList *removedArtifacts)
+{
+ if (removedArtifacts)
+ removedArtifacts->insert(artifact);
+ foreach (Artifact *parent, artifact->parents) {
+ bool removeParent = false;
+ disconnect(parent, artifact, logger);
+ if (parent->children.isEmpty()) {
+ removeParent = true;
+ } else if (parent->transformer) {
+ artifactsThatMustGetNewTransformers += parent;
+ parent->transformer->inputs.remove(artifact);
+ removeParent = parent->transformer->inputs.isEmpty();
+ }
+ if (removeParent) {
+ removeArtifactAndExclusiveDependents(parent, logger, removeFromProduct,
+ removedArtifacts);
+ }
+ }
+ const bool removeFromDisk = artifact->artifactType == Artifact::Generated;
+ removeArtifact(artifact, logger, removeFromDisk, removeFromProduct);
+}
+
+void ProjectBuildData::removeArtifact(Artifact *artifact,
+ const Logger &logger, bool removeFromDisk, bool removeFromProduct)
+{
+ if (logger.traceEnabled())
+ logger.qbsTrace() << "[BG] remove artifact " << relativeArtifactFileName(artifact);
+
+ if (removeFromDisk)
+ removeGeneratedArtifactFromDisk(artifact, logger);
+ removeFromLookupTable(artifact);
+ if (removeFromProduct) {
+ artifact->product->buildData->artifacts.remove(artifact);
+ artifact->product->buildData->targetArtifacts.remove(artifact);
+ }
+ disconnectArtifact(artifact, this, logger);
+ artifactsThatMustGetNewTransformers -= artifact;
+ isDirty = true;
+}
+
+void ProjectBuildData::updateNodesThatMustGetNewTransformer(const Logger &logger)
+{
+ RulesEvaluationContext::Scope s(evaluationContext.data());
+ foreach (Artifact *artifact, artifactsThatMustGetNewTransformers)
+ updateNodeThatMustGetNewTransformer(artifact, logger);
+ artifactsThatMustGetNewTransformers.clear();
+}
+
+void ProjectBuildData::updateNodeThatMustGetNewTransformer(Artifact *artifact, const Logger &logger)
+{
+ QBS_CHECK(artifact->transformer);
+
+ if (logger.debugEnabled()) {
+ logger.qbsDebug() << "[BG] updating transformer for "
+ << relativeArtifactFileName(artifact);
+ }
+
+ removeGeneratedArtifactFromDisk(artifact, logger);
+ artifact->autoMocTimestamp.clear();
+ artifact->clearTimestamp();
+
+ const RuleConstPtr rule = artifact->transformer->rule;
+ isDirty = true;
+
+ QBS_CHECK(artifact->transformer);
+ foreach (Artifact * const sibling, artifact->transformer->outputs)
+ sibling->transformer.clear();
+
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+ foreach (Artifact *input, artifact->children) {
+ foreach (const FileTag &fileTag, input->fileTags)
+ artifactsPerFileTag[fileTag] += input;
+ }
+ RulesApplicator rulesApplier(artifact->product, artifactsPerFileTag, logger);
+ rulesApplier.applyRule(rule);
+}
+
+void ProjectBuildData::load(PersistentPool &pool)
+{
+ int count;
+ pool.stream() >> count;
+ fileDependencies.clear();
+ fileDependencies.reserve(count);
+ for (; --count >= 0;) {
+ FileDependency *fileDependency = pool.idLoad<FileDependency>();
+ insertFileDependency(fileDependency);
+ }
+}
+
+void ProjectBuildData::store(PersistentPool &pool) const
+{
+ pool.storeContainer(fileDependencies);
+}
+
+
+BuildDataResolver::BuildDataResolver(const Logger &logger) : m_logger(logger)
+{
+}
+
+void BuildDataResolver::resolveBuildData(const TopLevelProjectPtr &resolvedProject,
+ const RulesEvaluationContextPtr &evalContext)
+{
+ QBS_CHECK(!resolvedProject->buildData);
+ m_project = resolvedProject;
+ resolvedProject->buildData.reset(new ProjectBuildData);
+ resolvedProject->buildData->evaluationContext = evalContext;
+ const QList<ResolvedProductPtr> allProducts = resolvedProject->allProducts();
+ evalContext->initializeObserver(Tr::tr("Setting up build graph for configuration %1")
+ .arg(resolvedProject->id()), allProducts.count() + 1);
+ foreach (ResolvedProductPtr rProduct, allProducts) {
+ if (rProduct->enabled)
+ resolveProductBuildData(rProduct);
+ evalContext->incrementProgressValue();
+ }
+ evalContext->incrementProgressValue();
+ doSanityChecks(resolvedProject, m_logger);
+}
+
+void BuildDataResolver::resolveProductBuildDataForExistingProject(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &freshProducts)
+{
+ m_project = project;
+ foreach (const ResolvedProductPtr &product, freshProducts) {
+ if (product->enabled)
+ resolveProductBuildData(product);
+ }
+}
+
+void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &product)
+{
+ if (product->buildData)
+ return;
+
+ evalContext()->checkForCancelation();
+
+ product->buildData.reset(new ProductBuildData);
+ ArtifactsPerFileTagMap artifactsPerFileTag;
+
+ foreach (ResolvedProductPtr dependency, product->dependencies) {
+ if (Q_UNLIKELY(!dependency->enabled)) {
+ QString msg = Tr::tr("Product '%1' depends on '%2' but '%2' is disabled.");
+ throw ErrorInfo(msg.arg(product->name, dependency->name));
+ }
+ resolveProductBuildData(dependency);
+ }
+
+ //add qbsFile artifact
+ Artifact *qbsFileArtifact = lookupArtifact(product, product->location.fileName());
+ if (!qbsFileArtifact) {
+ qbsFileArtifact = new Artifact;
+ qbsFileArtifact->artifactType = Artifact::SourceFile;
+ qbsFileArtifact->setFilePath(product->location.fileName());
+ qbsFileArtifact->properties = product->properties;
+ insertArtifact(product, qbsFileArtifact, m_logger);
+ }
+ qbsFileArtifact->fileTags.insert("qbs");
+ artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
+
+ // read sources
+ foreach (const SourceArtifactConstPtr &sourceArtifact, product->allEnabledFiles()) {
+ QString filePath = sourceArtifact->absoluteFilePath;
+ if (lookupArtifact(product, filePath))
+ continue; // ignore duplicate artifacts
+
+ Artifact *artifact = createArtifact(product, sourceArtifact, m_logger);
+ foreach (const FileTag &fileTag, artifact->fileTags)
+ artifactsPerFileTag[fileTag].insert(artifact);
+ }
+
+ // read manually added transformers
+ typedef QPair<ResolvedTransformerConstPtr, TransformerConstPtr> TrafoPair;
+ QList<TrafoPair> trafos;
+ foreach (const ResolvedTransformerConstPtr &rtrafo, product->transformers) {
+ ArtifactList inputArtifacts;
+ foreach (const QString &inputFileName, rtrafo->inputs) {
+ Artifact *artifact = lookupArtifact(product, inputFileName);
+ if (Q_UNLIKELY(!artifact))
+ throw ErrorInfo(QString("Can't find artifact '%0' in the list of source files.").arg(inputFileName));
+ inputArtifacts += artifact;
+ }
+ TransformerPtr transformer = Transformer::create();
+ trafos += TrafoPair(rtrafo, transformer);
+ transformer->inputs = inputArtifacts;
+ const RulePtr rule = Rule::create();
+ ResolvedModulePtr module = ResolvedModule::create();
+ module->name = rtrafo->module->name;
+ rule->module = module;
+ rule->script = rtrafo->transform;
+ foreach (const SourceArtifactConstPtr &sourceArtifact, rtrafo->outputs) {
+ Artifact *outputArtifact = createArtifact(product, sourceArtifact, m_logger);
+ outputArtifact->artifactType = Artifact::Generated;
+ outputArtifact->transformer = transformer;
+ transformer->outputs += outputArtifact;
+ product->buildData->targetArtifacts += outputArtifact;
+ foreach (Artifact *inputArtifact, inputArtifacts)
+ safeConnect(outputArtifact, inputArtifact, m_logger);
+ foreach (const FileTag &fileTag, outputArtifact->fileTags)
+ artifactsPerFileTag[fileTag].insert(outputArtifact);
+
+ RuleArtifactPtr ruleArtifact = RuleArtifact::create();
+ ruleArtifact->fileName = outputArtifact->filePath();
+ ruleArtifact->fileTags = outputArtifact->fileTags;
+ rule->artifacts += ruleArtifact;
+ }
+ transformer->rule = rule;
+
+ RulesEvaluationContext::Scope s(evalContext().data());
+ setupScriptEngineForFile(engine(), transformer->rule->script->fileContext, scope());
+ QScriptValue prepareScriptContext = engine()->newObject();
+ PrepareScriptObserver observer(engine());
+ setupScriptEngineForProduct(engine(), product, transformer->rule, prepareScriptContext,
+ &observer);
+ transformer->setupInputs(engine(), prepareScriptContext);
+ transformer->setupOutputs(engine(), prepareScriptContext);
+ transformer->createCommands(rtrafo->transform, evalContext(),
+ ScriptEngine::argumentList(transformer->rule->script->argumentNames,
+ prepareScriptContext));
+ if (Q_UNLIKELY(transformer->commands.isEmpty()))
+ throw ErrorInfo(QString("There's a transformer without commands."), rtrafo->transform->location);
+ }
+
+ // Handle Transformer.explicitlyDependsOn after all transformer outputs have been created.
+ foreach (const TrafoPair &p, trafos) {
+ const ResolvedTransformerConstPtr &rtrafo = p.first;
+ const TransformerConstPtr &trafo = p.second;
+ foreach (const FileTag &tag, rtrafo->explicitlyDependsOn) {
+ foreach (Artifact *output, trafo->outputs) {
+ foreach (Artifact *dependency, artifactsPerFileTag.value(tag)) {
+ loggedConnect(output, dependency, m_logger);
+ }
+ }
+ }
+ }
+
+ RulesApplicator(product, artifactsPerFileTag, m_logger).applyAllRules();
+ addTargetArtifacts(product, artifactsPerFileTag, m_logger);
+}
+
+RulesEvaluationContextPtr BuildDataResolver::evalContext() const
+{
+ return m_project->buildData->evaluationContext;
+}
+
+ScriptEngine *BuildDataResolver::engine() const
+{
+ return evalContext()->engine();
+}
+
+QScriptValue BuildDataResolver::scope() const
+{
+ return evalContext()->scope();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/projectbuilddata.h b/src/lib/corelib/buildgraph/projectbuilddata.h
new file mode 100644
index 000000000..515d5f00e
--- /dev/null
+++ b/src/lib/corelib/buildgraph/projectbuilddata.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROJECTBUILDDATA_H
+#define QBS_PROJECTBUILDDATA_H
+
+#include "artifactlist.h"
+#include "forward_decls.h"
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+#include <tools/persistentobject.h>
+
+#include <QHash>
+#include <QList>
+#include <QScriptValue>
+#include <QSet>
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+class FileDependency;
+class FileResourceBase;
+class ScriptEngine;
+
+class ProjectBuildData : public PersistentObject
+{
+public:
+ ProjectBuildData(const ProjectBuildData *other = 0);
+ ~ProjectBuildData();
+
+ static QString deriveBuildGraphFilePath(const QString &buildDir, const QString &projectId);
+
+ void insertIntoLookupTable(FileResourceBase *fileres);
+ void removeFromLookupTable(FileResourceBase *fileres);
+
+ QList<FileResourceBase *> lookupFiles(const QString &filePath) const;
+ QList<FileResourceBase *> lookupFiles(const QString &dirPath, const QString &fileName) const;
+ QList<FileResourceBase *> lookupFiles(const Artifact *artifact) const;
+ void insertFileDependency(FileDependency *dependency);
+ void updateNodesThatMustGetNewTransformer(const Logger &logger);
+ void removeArtifactAndExclusiveDependents(Artifact *artifact, const Logger &logger,
+ bool removeFromProduct = true, ArtifactList *removedArtifacts = 0);
+ void removeArtifact(Artifact *artifact, const Logger &logger, bool removeFromDisk = true,
+ bool removeFromProduct = true);
+
+ QSet<FileDependency *> fileDependencies;
+ RulesEvaluationContextPtr evaluationContext;
+ QSet<Artifact *> artifactsThatMustGetNewTransformers;
+ bool isDirty;
+
+private:
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+ void updateNodeThatMustGetNewTransformer(Artifact *artifact, const Logger &logger);
+
+ typedef QHash<QString, QList<FileResourceBase *> > ResultsPerDirectory;
+ typedef QHash<QString, ResultsPerDirectory> ArtifactLookupTable;
+ ArtifactLookupTable m_artifactLookupTable;
+ bool m_doCleanupInDestructor;
+};
+
+
+class BuildDataResolver
+{
+public:
+ BuildDataResolver(const Logger &logger);
+ void resolveBuildData(const TopLevelProjectPtr &resolvedProject,
+ const RulesEvaluationContextPtr &evalContext);
+ void resolveProductBuildDataForExistingProject(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &freshProducts);
+
+private:
+ void resolveProductBuildData(const ResolvedProductPtr &product);
+ RulesEvaluationContextPtr evalContext() const;
+ ScriptEngine *engine() const;
+ QScriptValue scope() const;
+
+ TopLevelProjectPtr m_project;
+ Logger m_logger;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PROJECTBUILDDATA_H
diff --git a/src/lib/corelib/buildgraph/rulegraph.cpp b/src/lib/corelib/buildgraph/rulegraph.cpp
new file mode 100644
index 000000000..b59edaaba
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rulegraph.cpp
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** 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 "rulegraph.h"
+#include <language/language.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+
+namespace qbs {
+namespace Internal {
+
+RuleGraph::RuleGraph()
+{
+}
+
+void RuleGraph::build(const QSet<RulePtr> &rules, const FileTags &productFileTags)
+{
+ QMap<FileTag, QList<const Rule *> > inputFileTagToRule;
+ m_artifacts.reserve(rules.count());
+ foreach (const RulePtr &rule, rules) {
+ foreach (const FileTag &fileTag, rule->outputFileTags())
+ m_outputFileTagToRule[fileTag].append(rule.data());
+ insert(rule);
+ }
+
+ m_parents.resize(rules.count());
+ m_children.resize(rules.count());
+
+ foreach (const RuleConstPtr &rule, m_artifacts) {
+ FileTags inFileTags = rule->inputs;
+ inFileTags += rule->auxiliaryInputs;
+ inFileTags += rule->explicitlyDependsOn;
+ foreach (const FileTag &fileTag, inFileTags) {
+ inputFileTagToRule[fileTag].append(rule.data());
+ foreach (const Rule * const consumingRule, m_outputFileTagToRule.value(fileTag)) {
+ connect(rule.data(), consumingRule);
+ }
+ }
+ }
+
+ QList<const Rule *> productRules;
+ foreach (const FileTag &productFileTag, productFileTags) {
+ QList<const Rule *> rules = m_outputFileTagToRule.value(productFileTag);
+ productRules += rules;
+ //### check: the rule graph must be a in valid shape!
+ }
+ foreach (const Rule *r, productRules)
+ m_rootRules += r->ruleGraphId;
+}
+
+QList<RuleConstPtr> RuleGraph::topSorted()
+{
+ QSet<int> rootRules = m_rootRules;
+ QList<RuleConstPtr> result;
+ foreach (int rootIndex, rootRules) {
+ RuleConstPtr rule = m_artifacts.at(rootIndex);
+ QSet<const Rule *> seenRules;
+ QList<const Rule *> rulePath;
+ result.append(topSort(rule, &seenRules, &rulePath));
+ }
+
+ // remove duplicates from the result of our post-order traversal
+ QSet<const Rule*> seenRules;
+ seenRules.reserve(result.count());
+ for (int i = 0; i < result.count();) {
+ const Rule * const rule = result.at(i).data();
+ if (seenRules.contains(rule))
+ result.removeAt(i);
+ else
+ ++i;
+ seenRules.insert(rule);
+ }
+
+ return result;
+}
+
+void RuleGraph::dump() const
+{
+ QByteArray indent;
+ printf("---rule graph dump:\n");
+ QSet<int> rootRules;
+ foreach (const RuleConstPtr &rule, m_artifacts)
+ if (m_parents[rule->ruleGraphId].isEmpty())
+ rootRules += rule->ruleGraphId;
+ foreach (int idx, rootRules) {
+ dump_impl(indent, idx);
+ }
+}
+
+void RuleGraph::dump_impl(QByteArray &indent, int rootIndex) const
+{
+ const RuleConstPtr r = m_artifacts[rootIndex];
+ printf("%s", indent.constData());
+ printf("%s", qPrintable(r->toString()));
+ printf("\n");
+
+ indent.append(" ");
+ foreach (int childIndex, m_children[rootIndex])
+ dump_impl(indent, childIndex);
+ indent.chop(2);
+}
+
+int RuleGraph::insert(const RulePtr &rule)
+{
+ rule->ruleGraphId = m_artifacts.count();
+ m_artifacts.append(rule);
+ return rule->ruleGraphId;
+}
+
+void RuleGraph::connect(const Rule *creatingRule, const Rule *consumingRule)
+{
+ int maxIndex = qMax(creatingRule->ruleGraphId, consumingRule->ruleGraphId);
+ if (m_parents.count() <= maxIndex) {
+ const int c = maxIndex + 1;
+ m_parents.resize(c);
+ m_children.resize(c);
+ }
+ m_parents[consumingRule->ruleGraphId].append(creatingRule->ruleGraphId);
+ m_children[creatingRule->ruleGraphId].append(consumingRule->ruleGraphId);
+}
+
+QList<RuleConstPtr> RuleGraph::topSort(const RuleConstPtr &rule, QSet<const Rule *> *seenRules,
+ QList<const Rule *> *rulePath)
+{
+ if (seenRules->contains(rule.data())) {
+ QString pathstr;
+ foreach (const Rule *r, *rulePath) {
+ pathstr += QLatin1Char('\n') + r->toString() + QLatin1Char('\t')
+ + r->script->location.toString();
+ }
+ throw ErrorInfo(Tr::tr("Cycle detected in rule dependencies: %1").arg(pathstr));
+ }
+
+ seenRules->insert(rule.data());
+ rulePath->prepend(rule.data());
+
+ QList<RuleConstPtr> result;
+ foreach (int childIndex, m_children.at(rule->ruleGraphId))
+ result.append(topSort(m_artifacts.at(childIndex), seenRules, rulePath));
+
+ result.append(rule);
+ seenRules->remove(rule.data());
+ rulePath->removeFirst();
+ return result;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/rulegraph.h b/src/lib/corelib/buildgraph/rulegraph.h
new file mode 100644
index 000000000..2f9a42a33
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rulegraph.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_RULEGRAPH_H
+#define QBS_RULEGRAPH_H
+
+#include <language/filetags.h>
+#include <language/forward_decls.h>
+
+#include <QList>
+#include <QMap>
+#include <QSet>
+#include <QString>
+#include <QVector>
+
+namespace qbs {
+namespace Internal {
+
+class RuleGraph
+{
+public:
+ RuleGraph();
+
+ void build(const QSet<RulePtr> &rules, const FileTags &productFileTag);
+ QList<RuleConstPtr> topSorted();
+
+ void dump() const;
+
+private:
+ void dump_impl(QByteArray &indent, int rootIndex) const;
+ int insert(const RulePtr &rule);
+ void connect(const Rule *creatingRule, const Rule *consumingRule);
+ QList<RuleConstPtr> topSort(const RuleConstPtr &rule, QSet<const Rule *> *seenRules,
+ QList<const Rule *> *rulePath);
+
+private:
+ QMap<FileTag, QList<const Rule*> > m_outputFileTagToRule;
+ QVector<RulePtr> m_artifacts;
+ QVector< QVector<int> > m_parents;
+ QVector< QVector<int> > m_children;
+ QSet<int> m_rootRules;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_RULEGRAPH_H
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp
new file mode 100644
index 000000000..8aea1f916
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp
@@ -0,0 +1,342 @@
+/****************************************************************************
+**
+** 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 "rulesapplicator.h"
+
+#include "artifact.h"
+#include "buildgraph.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include "rulesevaluationcontext.h"
+#include "transformer.h"
+#include <jsextensions/moduleproperties.h>
+#include <language/artifactproperties.h>
+#include <language/language.h>
+#include <language/preparescriptobserver.h>
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/scripttools.h>
+#include <tools/qbsassert.h>
+
+#include <QDir>
+
+namespace qbs {
+namespace Internal {
+
+RulesApplicator::RulesApplicator(const ResolvedProductPtr &product,
+ ArtifactsPerFileTagMap &artifactsPerFileTag, const Logger &logger)
+ : m_product(product)
+ , m_artifactsPerFileTag(artifactsPerFileTag)
+ , m_logger(logger)
+{
+}
+
+void RulesApplicator::applyAllRules()
+{
+ RulesEvaluationContext::Scope s(m_product->topLevelProject()->buildData->evaluationContext.data());
+ foreach (const RuleConstPtr &rule, m_product->topSortedRules())
+ applyRule(rule);
+}
+
+void RulesApplicator::applyRule(const RuleConstPtr &rule)
+{
+ m_rule = rule;
+ QScriptValue prepareScriptContext = engine()->newObject();
+ PrepareScriptObserver observer(engine());
+ setupScriptEngineForFile(engine(), m_rule->script->fileContext, scope());
+ setupScriptEngineForProduct(engine(), m_product, m_rule, prepareScriptContext, &observer);
+
+ ArtifactList inputArtifacts;
+ foreach (const FileTag &fileTag, m_rule->inputs)
+ inputArtifacts.unite(m_artifactsPerFileTag.value(fileTag));
+ if (m_rule->multiplex) { // apply the rule once for a set of inputs
+ if (!inputArtifacts.isEmpty())
+ doApply(inputArtifacts, prepareScriptContext);
+ } else { // apply the rule once for each input
+ ArtifactList lst;
+ foreach (Artifact * const inputArtifact, inputArtifacts) {
+ setupScriptEngineForArtifact(inputArtifact);
+ lst += inputArtifact;
+ doApply(lst, prepareScriptContext);
+ lst.clear();
+ }
+ }
+}
+
+static void copyProperty(const QString &name, const QScriptValue &src, QScriptValue dst)
+{
+ dst.setProperty(name, src.property(name));
+}
+
+void RulesApplicator::doApply(const ArtifactList &inputArtifacts,
+ QScriptValue &prepareScriptContext)
+{
+ evalContext()->checkForCancelation();
+
+ if (m_logger.debugEnabled()) {
+ m_logger.qbsDebug() << "[BG] apply rule " << m_rule->toString() << " "
+ << toStringList(inputArtifacts).join(",\n ");
+ }
+
+ QList<QPair<const RuleArtifact *, Artifact *> > ruleArtifactArtifactMap;
+ QList<Artifact *> outputArtifacts;
+
+ ArtifactList usingArtifacts;
+ if (!m_rule->usings.isEmpty()) {
+ const FileTags usingsFileTags = m_rule->usings;
+ foreach (const ResolvedProductPtr &dep, m_product->dependencies) {
+ QBS_CHECK(dep->buildData);
+ ArtifactList artifactsToCheck;
+ foreach (Artifact *targetArtifact, dep->buildData->targetArtifacts)
+ artifactsToCheck.unite(targetArtifact->transformer->outputs);
+ foreach (Artifact *artifact, artifactsToCheck) {
+ if (artifact->fileTags.matches(usingsFileTags))
+ usingArtifacts.insert(artifact);
+ }
+ }
+ }
+
+ m_transformer.clear();
+ // create the output artifacts from the set of input artifacts
+ copyProperty(QLatin1String("product"), prepareScriptContext, scope());
+ copyProperty(QLatin1String("project"), prepareScriptContext, scope());
+ foreach (const RuleArtifactConstPtr &ruleArtifact, m_rule->artifacts) {
+ Artifact * const outputArtifact = createOutputArtifact(ruleArtifact, inputArtifacts);
+ outputArtifacts << outputArtifact;
+ ruleArtifactArtifactMap << qMakePair(ruleArtifact.data(), outputArtifact);
+ }
+
+ foreach (Artifact *outputArtifact, outputArtifacts) {
+ // insert the output artifacts into the pool of artifacts
+ foreach (const FileTag &fileTag, outputArtifact->fileTags)
+ m_artifactsPerFileTag[fileTag].insert(outputArtifact);
+
+ // connect artifacts that match the file tags in explicitlyDependsOn
+ foreach (const FileTag &fileTag, m_rule->explicitlyDependsOn)
+ foreach (Artifact *dependency, m_artifactsPerFileTag.value(fileTag))
+ loggedConnect(outputArtifact, dependency, m_logger);
+
+ // Transformer setup
+ for (ArtifactList::const_iterator it = usingArtifacts.constBegin();
+ it != usingArtifacts.constEnd(); ++it)
+ {
+ Artifact *dep = *it;
+ loggedConnect(outputArtifact, dep, m_logger);
+ m_transformer->inputs.insert(dep);
+ }
+ m_transformer->outputs.insert(outputArtifact);
+
+ m_product->topLevelProject()->buildData->artifactsThatMustGetNewTransformers
+ -= outputArtifact;
+ }
+
+ m_transformer->setupInputs(engine(), prepareScriptContext);
+
+ // change the transformer outputs according to the bindings in Artifact
+ QScriptValue scriptValue;
+ if (!ruleArtifactArtifactMap.isEmpty())
+ engine()->currentContext()->pushScope(prepareScriptContext);
+ for (int i = ruleArtifactArtifactMap.count(); --i >= 0;) {
+ const RuleArtifact *ra = ruleArtifactArtifactMap.at(i).first;
+ if (ra->bindings.isEmpty())
+ continue;
+
+ // expose attributes of this artifact
+ Artifact *outputArtifact = ruleArtifactArtifactMap.at(i).second;
+ outputArtifact->properties = outputArtifact->properties->clone();
+
+ scope().setProperty("fileName", engine()->toScriptValue(outputArtifact->filePath()));
+ scope().setProperty("fileTags",
+ toScriptValue(engine(), outputArtifact->fileTags.toStringList()));
+
+ QVariantMap artifactModulesCfg = outputArtifact->properties->value().value("modules").toMap();
+ for (int i=0; i < ra->bindings.count(); ++i) {
+ const RuleArtifact::Binding &binding = ra->bindings.at(i);
+ scriptValue = engine()->evaluate(binding.code);
+ if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue))) {
+ QString msg = QLatin1String("evaluating rule binding '%1': %2");
+ throw ErrorInfo(msg.arg(binding.name.join(QLatin1String(".")), scriptValue.toString()), binding.location);
+ }
+ setConfigProperty(artifactModulesCfg, binding.name, scriptValue.toVariant());
+ }
+ QVariantMap outputArtifactConfig = outputArtifact->properties->value();
+ outputArtifactConfig.insert("modules", artifactModulesCfg);
+ outputArtifact->properties->setValue(outputArtifactConfig);
+ }
+ if (!ruleArtifactArtifactMap.isEmpty())
+ engine()->currentContext()->popScope();
+
+ m_transformer->setupOutputs(engine(), prepareScriptContext);
+ m_transformer->createCommands(m_rule->script, evalContext(),
+ ScriptEngine::argumentList(m_rule->script->argumentNames, prepareScriptContext));
+ if (Q_UNLIKELY(m_transformer->commands.isEmpty()))
+ throw ErrorInfo(QString("There's a rule without commands: %1.").arg(m_rule->toString()), m_rule->script->location);
+}
+
+void RulesApplicator::setupScriptEngineForArtifact(Artifact *artifact)
+{
+ QString inFileName = artifact->fileName();
+ QString inBaseName = FileInfo::baseName(artifact->filePath());
+ QString inCompleteBaseName = FileInfo::completeBaseName(artifact->filePath());
+
+ QString basedir;
+ if (artifact->artifactType == Artifact::SourceFile) {
+ QDir sourceDir(m_product->sourceDirectory);
+ basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath()));
+ } else {
+ QDir buildDir(m_product->topLevelProject()->buildDirectory);
+ basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath()));
+ }
+
+ // expose per file properties we want to use in an Artifact within a Rule
+ QScriptValue scriptValue = engine()->newObject();
+ ModuleProperties::init(scriptValue, artifact);
+ scriptValue.setProperty("fileName", inFileName);
+ scriptValue.setProperty("baseName", inBaseName);
+ scriptValue.setProperty("completeBaseName", inCompleteBaseName);
+ scriptValue.setProperty("baseDir", basedir);
+
+ scope().setProperty("input", scriptValue);
+ Q_ASSERT_X(scriptValue.strictlyEquals(engine()->evaluate("input")),
+ "BG", "The input object is not in current scope.");
+}
+
+Artifact *RulesApplicator::createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
+ const ArtifactList &inputArtifacts)
+{
+ QScriptValue scriptValue = engine()->evaluate(ruleArtifact->fileName);
+ if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue)))
+ throw ErrorInfo("Error in Rule.Artifact fileName: " + scriptValue.toString());
+ QString outputPath = scriptValue.toString();
+ outputPath.replace("..", "dotdot"); // don't let the output artifact "escape" its build dir
+ outputPath = resolveOutPath(outputPath);
+
+ Artifact *outputArtifact = lookupArtifact(m_product, outputPath);
+ if (outputArtifact) {
+ if (outputArtifact->transformer && outputArtifact->transformer != m_transformer) {
+ QBS_CHECK(!m_transformer);
+
+ // This can happen when applying rules after scanning for additional file tags.
+ // We just regenerate the transformer.
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << QString::fromLocal8Bit("[BG] regenerating transformer "
+ "for '%1'").arg(relativeArtifactFileName(outputArtifact));
+ }
+ m_transformer = outputArtifact->transformer;
+ m_transformer->inputs.unite(inputArtifacts);
+
+ if (Q_UNLIKELY(m_transformer->inputs.count() > 1 && !m_rule->multiplex)) {
+ QString th = "[" + outputArtifact->fileTags.toStringList().join(", ") + "]";
+ QString e = Tr::tr("Conflicting rules for producing %1 %2 \n").arg(outputArtifact->filePath(), th);
+ th = "[" + m_rule->inputs.toStringList().join(", ")
+ + "] -> [" + outputArtifact->fileTags.toStringList().join(", ") + "]";
+
+ e += QString(" while trying to apply: %1:%2:%3 %4\n")
+ .arg(m_rule->script->location.fileName())
+ .arg(m_rule->script->location.line())
+ .arg(m_rule->script->location.column())
+ .arg(th);
+
+ e += QString(" was already defined in: %1:%2:%3 %4\n")
+ .arg(outputArtifact->transformer->rule->script->location.fileName())
+ .arg(outputArtifact->transformer->rule->script->location.line())
+ .arg(outputArtifact->transformer->rule->script->location.column())
+ .arg(th);
+
+ QStringList inputFilePaths;
+ foreach (const Artifact * const a, m_transformer->inputs)
+ inputFilePaths << a->filePath();
+ e.append(Tr::tr("The input artifacts are: %1")
+ .arg(inputFilePaths.join(QLatin1String(", "))));
+ throw ErrorInfo(e);
+ }
+ }
+ outputArtifact->fileTags += ruleArtifact->fileTags;
+ } else {
+ outputArtifact = new Artifact;
+ outputArtifact->artifactType = Artifact::Generated;
+ outputArtifact->setFilePath(outputPath);
+ outputArtifact->fileTags = ruleArtifact->fileTags;
+ outputArtifact->alwaysUpdated = ruleArtifact->alwaysUpdated;
+ outputArtifact->properties = m_product->properties;
+ insertArtifact(m_product, outputArtifact, m_logger);
+ }
+
+ if (outputArtifact->fileTags.isEmpty())
+ outputArtifact->fileTags = m_product->fileTagsForFileName(outputArtifact->fileName());
+
+ for (int i = 0; i < m_product->artifactProperties.count(); ++i) {
+ const ArtifactPropertiesConstPtr &props = m_product->artifactProperties.at(i);
+ if (outputArtifact->fileTags.matches(props->fileTagsFilter())) {
+ outputArtifact->properties = props->propertyMap();
+ break;
+ }
+ }
+
+ foreach (Artifact *inputArtifact, inputArtifacts) {
+ QBS_CHECK(outputArtifact != inputArtifact);
+ loggedConnect(outputArtifact, inputArtifact, m_logger);
+ }
+
+ // create transformer if not already done so
+ if (!m_transformer) {
+ m_transformer = Transformer::create();
+ m_transformer->rule = m_rule;
+ m_transformer->inputs = inputArtifacts;
+ }
+ outputArtifact->transformer = m_transformer;
+
+ return outputArtifact;
+}
+
+QString RulesApplicator::resolveOutPath(const QString &path) const
+{
+ QString buildDir = m_product->topLevelProject()->buildDirectory;
+ QString result = FileInfo::resolvePath(buildDir, path);
+ result = QDir::cleanPath(result);
+ return result;
+}
+
+RulesEvaluationContextPtr RulesApplicator::evalContext() const
+{
+ return m_product->topLevelProject()->buildData->evaluationContext;
+}
+
+ScriptEngine *RulesApplicator::engine() const
+{
+ return evalContext()->engine();
+}
+
+QScriptValue RulesApplicator::scope() const
+{
+ return evalContext()->scope();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.h b/src/lib/corelib/buildgraph/rulesapplicator.h
new file mode 100644
index 000000000..76664baac
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rulesapplicator.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_RULESAPPLICATOR_H
+#define QBS_RULESAPPLICATOR_H
+
+#include "artifactlist.h"
+#include "forward_decls.h"
+#include <language/filetags.h>
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+
+#include <QMap>
+#include <QScriptValue>
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+class ScriptEngine;
+
+typedef QMap<FileTag, ArtifactList> ArtifactsPerFileTagMap;
+
+class RulesApplicator
+{
+public:
+ RulesApplicator(const ResolvedProductPtr &product, ArtifactsPerFileTagMap &artifactsPerFileTag,
+ const Logger &logger);
+ void applyAllRules();
+ void applyRule(const RuleConstPtr &rule);
+
+private:
+ void doApply(const ArtifactList &inputArtifacts, QScriptValue &prepareScriptContext);
+ void setupScriptEngineForArtifact(Artifact *artifact);
+ Artifact *createOutputArtifact(const RuleArtifactConstPtr &ruleArtifact,
+ const ArtifactList &inputArtifacts);
+ QString resolveOutPath(const QString &path) const;
+ RulesEvaluationContextPtr evalContext() const;
+ ScriptEngine *engine() const;
+ QScriptValue scope() const;
+
+ const ResolvedProductPtr m_product;
+ ArtifactsPerFileTagMap &m_artifactsPerFileTag;
+
+ RuleConstPtr m_rule;
+ TransformerPtr m_transformer;
+ Logger m_logger;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_RULESAPPLICATOR_H
diff --git a/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp
new file mode 100644
index 000000000..5cabea9d7
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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 "rulesevaluationcontext.h"
+
+#include "artifact.h"
+#include "command.h"
+#include "transformer.h"
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/progressobserver.h>
+#include <tools/qbsassert.h>
+
+#include <QVariant>
+
+namespace qbs {
+namespace Internal {
+
+RulesEvaluationContext::RulesEvaluationContext(const Logger &logger)
+ : m_engine(new ScriptEngine(logger)), m_observer(0), m_initScopeCalls(0)
+{
+ m_prepareScriptScope = m_engine->newObject();
+ ProcessCommand::setupForJavaScript(m_prepareScriptScope);
+ JavaScriptCommand::setupForJavaScript(m_prepareScriptScope);
+}
+
+RulesEvaluationContext::~RulesEvaluationContext()
+{
+ delete m_engine;
+}
+
+void RulesEvaluationContext::initializeObserver(const QString &description, int maximumProgress)
+{
+ if (m_observer)
+ m_observer->initialize(description, maximumProgress);
+}
+
+void RulesEvaluationContext::incrementProgressValue()
+{
+ if (m_observer)
+ m_observer->incrementProgressValue();
+}
+
+void RulesEvaluationContext::checkForCancelation()
+{
+ if (Q_UNLIKELY(m_observer && m_observer->canceled()))
+ throw ErrorInfo(Tr::tr("Build canceled."));
+}
+
+void RulesEvaluationContext::initScope()
+{
+ if (m_initScopeCalls++ > 0)
+ return;
+
+ m_engine->setProperty("lastSetupProject", QVariant());
+ m_engine->setProperty("lastSetupProduct", QVariant());
+
+ m_engine->clearImportsCache();
+ m_engine->pushContext();
+ m_scope = m_engine->newObject();
+ m_scope.setPrototype(m_prepareScriptScope);
+ m_engine->currentContext()->pushScope(m_scope);
+}
+
+void RulesEvaluationContext::cleanupScope()
+{
+ QBS_CHECK(m_initScopeCalls > 0);
+ if (--m_initScopeCalls > 0)
+ return;
+
+ m_scope = QScriptValue();
+ m_engine->currentContext()->popScope();
+ m_engine->popContext();
+}
+
+RulesEvaluationContext::Scope::Scope(RulesEvaluationContext *evalContext)
+ : m_evalContext(evalContext)
+{
+ evalContext->initScope();
+}
+
+RulesEvaluationContext::Scope::~Scope()
+{
+ m_evalContext->cleanupScope();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/rulesevaluationcontext.h b/src/lib/corelib/buildgraph/rulesevaluationcontext.h
new file mode 100644
index 000000000..1682e858b
--- /dev/null
+++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_RULESEVALUATIONCONTEXT_H
+#define QBS_RULESEVALUATIONCONTEXT_H
+
+#include <language/forward_decls.h>
+
+#include <QHash>
+#include <QScriptProgram>
+#include <QScriptValue>
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+class Logger;
+class ProgressObserver;
+class ScriptEngine;
+
+class RulesEvaluationContext
+{
+public:
+ RulesEvaluationContext(const Logger &logger);
+ ~RulesEvaluationContext();
+
+ class Scope
+ {
+ public:
+ Scope(RulesEvaluationContext *evalContext);
+ ~Scope();
+
+ private:
+ RulesEvaluationContext * const m_evalContext;
+ };
+
+ ScriptEngine *engine() const { return m_engine; }
+ QScriptValue scope() const { return m_scope; }
+
+ void setObserver(ProgressObserver *observer) { m_observer = observer; }
+ ProgressObserver *observer() const { return m_observer; }
+ void initializeObserver(const QString &description, int maximumProgress);
+ void incrementProgressValue();
+ void checkForCancelation();
+
+private:
+ friend class Scope;
+
+ void initScope();
+ void cleanupScope();
+
+ ScriptEngine * const m_engine;
+ ProgressObserver *m_observer;
+ unsigned int m_initScopeCalls;
+ QScriptValue m_scope;
+ QScriptValue m_prepareScriptScope;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_RULESEVALUATIONCONTEXT_H
diff --git a/src/lib/corelib/buildgraph/scanresultcache.cpp b/src/lib/corelib/buildgraph/scanresultcache.cpp
new file mode 100644
index 000000000..98cfb7fba
--- /dev/null
+++ b/src/lib/corelib/buildgraph/scanresultcache.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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 "scanresultcache.h"
+#include <tools/fileinfo.h>
+
+namespace qbs {
+namespace Internal {
+
+ScanResultCache::Dependency::Dependency(const QString &filePath, bool isLocal)
+ : m_isLocal(isLocal)
+{
+ FileInfo::splitIntoDirectoryAndFileName(filePath, &m_dirPath, &m_fileName);
+
+ m_isClean = !m_dirPath.contains(QLatin1Char('.'))
+ && !m_dirPath.contains(QLatin1String("//"));
+}
+
+ScanResultCache::Result ScanResultCache::value(const QString &fileName) const
+{
+ return m_data.value(fileName);
+}
+
+void ScanResultCache::insert(const QString &fileName, const ScanResultCache::Result &value)
+{
+ m_data.insert(fileName, value);
+}
+
+void ScanResultCache::remove(const QString &filePath)
+{
+ m_data.remove(filePath);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/scanresultcache.h b/src/lib/corelib/buildgraph/scanresultcache.h
new file mode 100644
index 000000000..289aa31e6
--- /dev/null
+++ b/src/lib/corelib/buildgraph/scanresultcache.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_SCANRESULTCACHE_H
+#define QBS_SCANRESULTCACHE_H
+
+#include <language/filetags.h>
+
+#include <QHash>
+#include <QString>
+#include <QVector>
+
+namespace qbs {
+namespace Internal {
+
+class ScanResultCache
+{
+public:
+ class Dependency
+ {
+ public:
+ Dependency() : m_isLocal(false), m_isClean(true) {}
+ Dependency(const QString &filePath, bool m_isLocal);
+
+ QString filePath() const { return m_dirPath.isEmpty() ? m_fileName : m_dirPath + QLatin1Char('/') + m_fileName; }
+ const QString &dirPath() const { return m_dirPath; }
+ const QString &fileName() const { return m_fileName; }
+ bool isLocal() const { return m_isLocal; }
+ bool isClean() const { return m_isClean; }
+
+ private:
+ QString m_dirPath;
+ QString m_fileName;
+ bool m_isLocal;
+ bool m_isClean;
+ };
+
+ class Result
+ {
+ public:
+ Result()
+ : valid(false)
+ {}
+
+ QVector<Dependency> deps;
+ FileTags additionalFileTags;
+ bool valid;
+ };
+
+ Result value(const QString &fileName) const;
+ void insert(const QString &fileName, const Result &value);
+ void remove(const QString &filePath);
+
+private:
+ QHash<QString, Result> m_data;
+};
+
+} // namespace qbs
+} // namespace qbs
+
+#endif // QBS_SCANRESULTCACHE_H
diff --git a/src/lib/corelib/buildgraph/timestampsupdater.cpp b/src/lib/corelib/buildgraph/timestampsupdater.cpp
new file mode 100644
index 000000000..c22e9f96c
--- /dev/null
+++ b/src/lib/corelib/buildgraph/timestampsupdater.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** 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 "timestampsupdater.h"
+
+#include "artifact.h"
+#include "artifactvisitor.h"
+#include "productbuilddata.h"
+#include "projectbuilddata.h"
+#include <language/language.h>
+#include <tools/filetime.h>
+#include <tools/qbsassert.h>
+
+#include <QFile>
+
+namespace qbs {
+namespace Internal {
+
+class TimestampsUpdateVisitor : public ArtifactVisitor
+{
+public:
+ TimestampsUpdateVisitor()
+ : ArtifactVisitor(Artifact::Generated), m_now(FileTime::currentTime()) {}
+
+ 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.
+ foreach (Artifact * const targetArtifact, product->buildData->targetArtifacts) {
+ if (FileInfo(targetArtifact->filePath()).exists())
+ QFile(targetArtifact->filePath()).open(QIODevice::WriteOnly | QIODevice::Append);
+ }
+ }
+
+private:
+ void doVisit(Artifact *artifact)
+ {
+ if (FileInfo(artifact->filePath()).exists())
+ artifact->setTimestamp(m_now);
+ }
+
+ FileTime m_now;
+};
+
+void TimestampsUpdater::updateTimestamps(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const Logger &logger)
+{
+ TimestampsUpdateVisitor v;
+ foreach (const ResolvedProductPtr &product, products)
+ v.visitProduct(product);
+ project->buildData->isDirty = !products.isEmpty();
+ project->store(logger);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/timestampsupdater.h b/src/lib/corelib/buildgraph/timestampsupdater.h
new file mode 100644
index 000000000..c1f918837
--- /dev/null
+++ b/src/lib/corelib/buildgraph/timestampsupdater.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef TIMESTAMPSUPDATER_H
+#define TIMESTAMPSUPDATER_H
+
+#include <language/forward_decls.h>
+
+#include <QList>
+
+namespace qbs {
+namespace Internal {
+class Logger;
+
+class TimestampsUpdater
+{
+public:
+ void updateTimestamps(const TopLevelProjectPtr &project,
+ const QList<ResolvedProductPtr> &products, const Logger &logger);
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // TIMESTAMPSUPDATER_H
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
diff --git a/src/lib/corelib/buildgraph/transformer.h b/src/lib/corelib/buildgraph/transformer.h
new file mode 100644
index 000000000..b6ef09561
--- /dev/null
+++ b/src/lib/corelib/buildgraph/transformer.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_TRANSFORMER_H
+#define QBS_TRANSFORMER_H
+
+#include "artifactlist.h"
+#include "forward_decls.h"
+#include <language/forward_decls.h>
+#include <language/property.h>
+#include <tools/persistentobject.h>
+
+#include <QHash>
+#include <QScriptEngine>
+
+namespace qbs {
+namespace Internal {
+class Artifact;
+class AbstractCommand;
+class Rule;
+class ScriptEngine;
+
+class Transformer : public PersistentObject
+{
+public:
+ static TransformerPtr create() { return TransformerPtr(new Transformer); }
+
+ ~Transformer();
+
+ ArtifactList inputs; // Subset of "children of all outputs".
+ ArtifactList outputs;
+ RuleConstPtr rule;
+ QList<AbstractCommand *> commands;
+ PropertyList propertiesRequestedInPrepareScript;
+ PropertyList propertiesRequestedInCommands;
+ QHash<QString, PropertyList> propertiesRequestedFromArtifactInPrepareScript;
+
+ static QScriptValue translateFileConfig(QScriptEngine *scriptEngine,
+ Artifact *artifact,
+ const QString &defaultModuleName);
+ static QScriptValue translateInOutputs(QScriptEngine *scriptEngine,
+ const ArtifactList &artifacts,
+ const QString &defaultModuleName);
+
+ ResolvedProductPtr product() const;
+ void setupInputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue);
+ void setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue);
+ void createCommands(const ScriptFunctionConstPtr &script,
+ const RulesEvaluationContextPtr &evalContext, const QScriptValueList &args);
+
+private:
+ Transformer();
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_TRANSFORMER_H
diff --git a/src/lib/corelib/buildgraph/tst_buildgraph.cpp b/src/lib/corelib/buildgraph/tst_buildgraph.cpp
new file mode 100644
index 000000000..a159c2938
--- /dev/null
+++ b/src/lib/corelib/buildgraph/tst_buildgraph.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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 "tst_buildgraph.h"
+
+#include <buildgraph/artifact.h>
+#include <buildgraph/productbuilddata.h>
+#include <buildgraph/cycledetector.h>
+#include <language/language.h>
+#include <logging/logger.h>
+#include <tools/error.h>
+
+#include <QtTest>
+
+namespace qbs {
+namespace Internal {
+
+TestBuildGraph::TestBuildGraph(ILogSink *logSink) : m_logSink(logSink)
+{
+}
+
+void TestBuildGraph::initTestCase()
+{
+}
+
+void TestBuildGraph::cleanupTestCase()
+{
+ qDeleteAll(m_artifacts);
+}
+
+
+bool TestBuildGraph::cycleDetected(const ResolvedProductConstPtr &product)
+{
+ try {
+ CycleDetector(Logger(m_logSink)).visitProduct(product);
+ return false;
+ } catch (const ErrorInfo &) {
+ return true;
+ }
+}
+
+ResolvedProductConstPtr TestBuildGraph::productWithDirectCycle()
+{
+ Artifact * const root = new Artifact;
+ Artifact * const child = new Artifact;
+ m_artifacts << root << child;
+ root->children.insert(child);
+ child->children.insert(root);
+ const ResolvedProductPtr product = ResolvedProduct::create();
+ product->buildData.reset(new ProductBuildData);
+ product->buildData->targetArtifacts.insert(root);
+ return product;
+}
+
+ResolvedProductConstPtr TestBuildGraph::productWithLessDirectCycle()
+{
+ Artifact * const root = new Artifact;
+ Artifact * const child = new Artifact;
+ Artifact * const grandchild = new Artifact;
+ m_artifacts << root << child << grandchild;
+ root->children.insert(child);
+ child->children.insert(grandchild);
+ grandchild->children.insert(root);
+ const ResolvedProductPtr product = ResolvedProduct::create();
+ product->buildData.reset(new ProductBuildData);
+ product->buildData->targetArtifacts << root;
+ return product;
+}
+
+// root appears as a child, but in a different tree
+ResolvedProductConstPtr TestBuildGraph::productWithNoCycle()
+{
+ Artifact * const root = new Artifact;
+ Artifact * const root2 = new Artifact;
+ m_artifacts << root << root2;
+ root2->children.insert(root);
+ const ResolvedProductPtr product = ResolvedProduct::create();
+ product->buildData.reset(new ProductBuildData);
+ product->buildData->targetArtifacts << root << root2;
+ return product;
+}
+
+void TestBuildGraph::testCycle()
+{
+ QVERIFY(cycleDetected(productWithDirectCycle()));
+ QVERIFY(cycleDetected(productWithLessDirectCycle()));
+ QVERIFY(!cycleDetected(productWithNoCycle()));
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/tst_buildgraph.h b/src/lib/corelib/buildgraph/tst_buildgraph.h
new file mode 100644
index 000000000..470300c4b
--- /dev/null
+++ b/src/lib/corelib/buildgraph/tst_buildgraph.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef TST_BUILDGRAPH_H
+#define TST_BUILDGRAPH_H
+
+#include <buildgraph/forward_decls.h>
+#include <language/forward_decls.h>
+#include <logging/ilogsink.h>
+#include <tools/qbs_export.h>
+
+#include <QList>
+#include <QObject>
+
+namespace qbs {
+namespace Internal {
+
+class QBS_EXPORT TestBuildGraph : public QObject
+{
+ Q_OBJECT
+public:
+ TestBuildGraph(ILogSink *logSink);
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void testCycle();
+
+private:
+ ResolvedProductConstPtr productWithDirectCycle();
+ ResolvedProductConstPtr productWithLessDirectCycle();
+ ResolvedProductConstPtr productWithNoCycle();
+ bool cycleDetected(const ResolvedProductConstPtr &product);
+
+ QList<Artifact *> m_artifacts;
+ ILogSink * const m_logSink;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // TST_BUILDGRAPH_H
diff --git a/src/lib/corelib/corelib.pro b/src/lib/corelib/corelib.pro
new file mode 100644
index 000000000..2eb02e7a3
--- /dev/null
+++ b/src/lib/corelib/corelib.pro
@@ -0,0 +1,31 @@
+TARGET = qbscore
+include(../library.pri)
+
+QT += script gui
+all_tests:QT += testlib
+
+INCLUDEPATH += $$PWD
+
+CONFIG += depend_includepath
+DEFINES += QT_CREATOR QML_BUILD_STATIC_LIB # needed for QmlJS
+
+DEFINES += SRCDIR=\\\"$$PWD\\\"
+
+include(api/api.pri)
+include(buildgraph/buildgraph.pri)
+include(jsextensions/jsextensions.pri)
+include(language/language.pri)
+include(logging/logging.pri)
+include(parser/parser.pri)
+include(tools/tools.pri)
+
+HEADERS += \
+ qbs.h
+
+!qbs_no_dev_install {
+ qbs_h.files = qbs.h
+ qbs_h.path = $${QBS_INSTALL_PREFIX}/include/qbs
+ use_pri.files = use_installed_corelib.pri ../../../qbs_version.pri
+ use_pri.path = $${qbs_h.path}
+ INSTALLS += qbs_h use_pri
+}
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
new file mode 100644
index 000000000..e14ba956c
--- /dev/null
+++ b/src/lib/corelib/corelib.qbs
@@ -0,0 +1,341 @@
+import qbs 1.0
+import "../Library.qbs" as QbsLibrary
+
+QbsLibrary {
+ Depends { name: "cpp" }
+ Depends { name: "Qt"; submodules: ["gui", "script", "xml"] }
+ Depends { condition: project.enableUnitTests; name: "Qt.test" }
+ name: "qbscore"
+ cpp.includePaths: base.concat([
+ ".",
+ "../.." // for the plugin headers
+ ])
+ cpp.defines: base.concat([
+ "QBS_VERSION=\"" + version + "\"",
+ "QT_CREATOR", "QML_BUILD_STATIC_LIB", // needed for QmlJS
+ "SRCDIR=\"" + path + "\""
+ ])
+
+ Group {
+ name: product.name
+ files: ["qbs.h"]
+ qbs.install: project.installApiHeaders
+ qbs.installDir: headerInstallPrefix
+ }
+ Group {
+ name: "api"
+ prefix: name + '/'
+ files: [
+ "changeset.cpp",
+ "changeset.h",
+ "internaljobs.cpp",
+ "internaljobs.h",
+ "jobs.cpp",
+ "project.cpp",
+ "projectdata.cpp",
+ "projectdata_p.h",
+ "projectfileupdater.cpp",
+ "projectfileupdater.h",
+ "qmljsrewriter.cpp",
+ "qmljsrewriter.h",
+ "propertymap_p.h",
+ "runenvironment.cpp",
+ ]
+ }
+ Group {
+ name: "public api headers"
+ qbs.install: project.installApiHeaders
+ qbs.installDir: headerInstallPrefix + "/api"
+ prefix: "api/"
+ files: [
+ "jobs.h",
+ "project.h",
+ "projectdata.h",
+ "runenvironment.h"
+ ]
+ }
+ Group {
+ name: "buildgraph"
+ prefix: name + '/'
+ files: [
+ "abstractcommandexecutor.cpp",
+ "abstractcommandexecutor.h",
+ "artifact.cpp",
+ "artifact.h",
+ "artifactcleaner.cpp",
+ "artifactcleaner.h",
+ "artifactlist.cpp",
+ "artifactlist.h",
+ "artifactvisitor.cpp",
+ "artifactvisitor.h",
+ "automoc.cpp",
+ "automoc.h",
+ "buildgraph.cpp",
+ "buildgraph.h",
+ "buildgraphloader.cpp",
+ "buildgraphloader.h",
+ "command.cpp",
+ "command.h",
+ "cycledetector.cpp",
+ "cycledetector.h",
+ "executor.cpp",
+ "executor.h",
+ "executorjob.cpp",
+ "executorjob.h",
+ "filedependency.cpp",
+ "filedependency.h",
+ "inputartifactscanner.cpp",
+ "inputartifactscanner.h",
+ "jscommandexecutor.cpp",
+ "jscommandexecutor.h",
+ "processcommandexecutor.cpp",
+ "processcommandexecutor.h",
+ "productbuilddata.cpp",
+ "productbuilddata.h",
+ "productinstaller.cpp",
+ "productinstaller.h",
+ "projectbuilddata.cpp",
+ "projectbuilddata.h",
+ "rulegraph.cpp",
+ "rulegraph.h",
+ "rulesapplicator.cpp",
+ "rulesapplicator.h",
+ "rulesevaluationcontext.cpp",
+ "rulesevaluationcontext.h",
+ "scanresultcache.cpp",
+ "scanresultcache.h",
+ "timestampsupdater.cpp",
+ "timestampsupdater.h",
+ "transformer.cpp",
+ "transformer.h"
+ ]
+ }
+ Group {
+ name: "public buildgraph headers"
+ qbs.install: project.installApiHeaders
+ qbs.installDir: headerInstallPrefix + "/buildgraph"
+ files: "buildgraph/forward_decls.h"
+ }
+ Group {
+ name: "jsextensions"
+ prefix: name + '/'
+ files: [
+ "file.cpp",
+ "file.h",
+ "jsextensions.cpp",
+ "jsextensions.h",
+ "moduleproperties.cpp",
+ "moduleproperties.h",
+ "process.cpp",
+ "process.h",
+ "textfile.cpp",
+ "textfile.h",
+ "domxml.cpp",
+ "domxml.h"
+ ]
+ }
+ Group {
+ name: "language"
+ prefix: name + '/'
+ files: [
+ "artifactproperties.cpp",
+ "artifactproperties.h",
+ "asttools.cpp",
+ "asttools.h",
+ "builtindeclarations.cpp",
+ "builtindeclarations.h",
+ "builtinvalue.cpp",
+ "builtinvalue.h",
+ "evaluationdata.h",
+ "evaluator.cpp",
+ "evaluator.h",
+ "evaluatorscriptclass.cpp",
+ "evaluatorscriptclass.h",
+ "filecontext.cpp",
+ "filecontext.h",
+ "filetags.cpp",
+ "filetags.h",
+ "functiondeclaration.h",
+ "identifiersearch.cpp",
+ "identifiersearch.h",
+ "importversion.cpp",
+ "importversion.h",
+ "item.cpp",
+ "item.h",
+ "itemdeclaration.cpp",
+ "itemdeclaration.h",
+ "itemobserver.h",
+ "itempool.cpp",
+ "itempool.h",
+ "itemreader.cpp",
+ "itemreader.h",
+ "itemreaderastvisitor.cpp",
+ "itemreaderastvisitor.h",
+ "jsimports.h",
+ "language.cpp",
+ "language.h",
+ "loader.cpp",
+ "loader.h",
+ "moduleloader.cpp",
+ "moduleloader.h",
+ "preparescriptobserver.cpp",
+ "preparescriptobserver.h",
+ "projectresolver.cpp",
+ "projectresolver.h",
+ "property.h",
+ "propertydeclaration.cpp",
+ "propertydeclaration.h",
+ "propertymapinternal.cpp",
+ "propertymapinternal.h",
+ "scriptengine.cpp",
+ "scriptengine.h",
+ "scriptpropertyobserver.h",
+ "value.cpp",
+ "value.h"
+ ]
+ }
+ Group {
+ name: "public language headers"
+ qbs.install: project.installApiHeaders
+ qbs.installDir: headerInstallPrefix + "/language"
+ files: "language/forward_decls.h"
+ }
+ Group {
+ name: "logging"
+ prefix: name + '/'
+ files: [
+ "ilogsink.cpp",
+ "logger.cpp",
+ "logger.h",
+ "translator.h"
+ ]
+ }
+ Group {
+ name: "public logging headers"
+ qbs.install: project.installApiHeaders
+ qbs.installDir: headerInstallPrefix + "/logging"
+ files: "logging/ilogsink.h"
+ }
+ Group {
+ name: "parser"
+ prefix: name + '/'
+ files: [
+ "qmlerror.cpp",
+ "qmlerror.h",
+ "qmljsast.cpp",
+ "qmljsast_p.h",
+ "qmljsastfwd_p.h",
+ "qmljsastvisitor.cpp",
+ "qmljsastvisitor_p.h",
+ "qmljsengine_p.cpp",
+ "qmljsengine_p.h",
+ "qmljsglobal_p.h",
+ "qmljsgrammar.cpp",
+ "qmljsgrammar_p.h",
+ "qmljskeywords_p.h",
+ "qmljslexer.cpp",
+ "qmljslexer_p.h",
+ "qmljsmemorypool_p.h",
+ "qmljsparser.cpp",
+ "qmljsparser_p.h"
+ ]
+ }
+ Group {
+ name: "tools"
+ prefix: name + '/'
+ files: [
+ "buildoptions.cpp",
+ "cleanoptions.cpp",
+ "codelocation.cpp",
+ "error.cpp",
+ "fileinfo.cpp",
+ "fileinfo.h",
+ "filetime.h",
+ "hostosinfo.h",
+ "id.cpp",
+ "id.h",
+ "installoptions.cpp",
+ "persistence.cpp",
+ "persistence.h",
+ "persistentobject.h",
+ "preferences.cpp",
+ "processresult.cpp",
+ "processresult_p.h",
+ "profile.cpp",
+ "progressobserver.cpp",
+ "progressobserver.h",
+ "propertyfinder.cpp",
+ "propertyfinder.h",
+ "qbsassert.cpp",
+ "qbsassert.h",
+ "qttools.cpp",
+ "qttools.h",
+ "scannerpluginmanager.cpp",
+ "scannerpluginmanager.h",
+ "scripttools.cpp",
+ "scripttools.h",
+ "settings.cpp",
+ "setupprojectparameters.cpp",
+ "weakpointer.h"
+ ]
+ }
+ Group {
+ name: "public tools headers"
+ prefix: "tools/"
+ files: [
+ "buildoptions.h",
+ "cleanoptions.h",
+ "codelocation.h",
+ "error.h",
+ "installoptions.h",
+ "preferences.h",
+ "processresult.h",
+ "profile.h",
+ "qbs_export.h",
+ "settings.h",
+ "setupprojectparameters.h",
+ ]
+ qbs.install: project.installApiHeaders
+ qbs.installDir: headerInstallPrefix + "/tools"
+ }
+ Group {
+ condition: qbs.targetOS.contains("windows")
+ name: "tools (Windows)"
+ prefix: "tools/"
+ files: [
+ "filetime_win.cpp"
+ ]
+ }
+ Group {
+ condition: qbs.targetOS.contains("unix")
+ name: "tools (Unix)"
+ prefix: "tools/"
+ files: [
+ "filetime_unix.cpp"
+ ]
+ }
+ Group {
+ name: "use_installed.pri"
+ files: [
+ "use_installed_corelib.pri",
+ "../../../qbs_version.pri"
+ ]
+ qbs.install: project.installApiHeaders
+ qbs.installDir: headerInstallPrefix
+ }
+ Group {
+ condition: project.enableUnitTests
+ name: "tests"
+ files: [
+ "buildgraph/tst_buildgraph.cpp",
+ "buildgraph/tst_buildgraph.h",
+ "language/tst_language.cpp",
+ "language/tst_language.h",
+ "tools/tst_tools.h",
+ "tools/tst_tools.cpp"
+ ]
+ }
+ Export {
+ Depends { name: "Qt"; submodules: ["script", "xml"] }
+ }
+}
diff --git a/src/lib/corelib/jsextensions/domxml.cpp b/src/lib/corelib/jsextensions/domxml.cpp
new file mode 100644
index 000000000..a66f2b21b
--- /dev/null
+++ b/src/lib/corelib/jsextensions/domxml.cpp
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+** 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 "domxml.h"
+
+#include <QFile>
+#include <QScriptEngine>
+
+
+namespace qbs {
+namespace Internal {
+
+void initializeJsExtensionXml(QScriptValue extensionObject)
+{
+ QScriptEngine *engine = extensionObject.engine();
+ QScriptValue obj = engine->newQMetaObject(&XmlDomDocument::staticMetaObject, engine->newFunction(&XmlDomDocument::ctor));
+ extensionObject.setProperty("XmlDomDocument", obj);
+ obj = engine->newQMetaObject(&XmlDomNode::staticMetaObject, engine->newFunction(&XmlDomNode::ctor));
+ extensionObject.setProperty("XmlDomElement", obj);
+}
+
+QScriptValue XmlDomDocument::ctor(QScriptContext *context, QScriptEngine *engine)
+{
+ XmlDomDocument *xml = 0;
+ switch (context->argumentCount()) {
+ case 0:
+ xml = new XmlDomDocument(context);
+ break;
+ case 1:
+ xml = new XmlDomDocument(context, context->argument(0).toString());
+ break;
+ default:
+ return context->throwError("DomXml(QString file = QLatin1String(\"\"))");
+ }
+ QScriptValue obj = engine->newQObject(xml, QScriptEngine::ScriptOwnership);
+ return obj;
+}
+
+QScriptValue XmlDomDocument::documentElement()
+{
+ return engine()->newQObject(new XmlDomNode(m_domDocument.documentElement()), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomDocument::createElement(const QString &tagName)
+{
+ return engine()->newQObject(new XmlDomNode(m_domDocument.createElement(tagName)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomDocument::createCDATASection(const QString &value)
+{
+ return engine()->newQObject(new XmlDomNode(m_domDocument.createCDATASection(value)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomDocument::createTextNode(const QString &value)
+{
+ return engine()->newQObject(new XmlDomNode(m_domDocument.createTextNode(value)), QScriptEngine::ScriptOwnership);
+}
+
+bool XmlDomDocument::setContent(const QString &content)
+{
+ return m_domDocument.setContent(content);
+}
+
+QString XmlDomDocument::toString(int indent)
+{
+ return m_domDocument.toString(indent);
+}
+
+void XmlDomDocument::save(const QString &filePath, int indent)
+{
+ QFile f(filePath);
+ if (!f.open(QIODevice::WriteOnly)) {
+ context()->throwError(QString::fromLatin1("unable to open '%1'")
+ .arg(filePath));
+ return;
+ }
+
+ QByteArray buff(m_domDocument.toByteArray(indent));
+ if (buff.size() != f.write(buff))
+ {
+ context()->throwError(f.errorString());
+ f.close();
+ return;
+ }
+
+ f.close();
+ if (f.error() != QFile::NoError)
+ context()->throwError(f.errorString());
+}
+
+void XmlDomDocument::load(const QString &filePath)
+{
+ QFile f(filePath);
+ if (!f.open(QIODevice::ReadOnly)) {
+ context()->throwError(QString::fromLatin1("unable to open '%1'")
+ .arg(filePath));
+ return;
+ }
+
+ QString errorMsg;
+ if (!m_domDocument.setContent(&f, &errorMsg)) {
+ context()->throwError(errorMsg);
+ return;
+ }
+}
+
+XmlDomDocument::XmlDomDocument(QScriptContext *context, const QString &name):m_domDocument(name)
+{
+ Q_UNUSED(context)
+ m_domNode = m_domDocument;
+}
+
+QScriptValue XmlDomNode::ctor(QScriptContext *context, QScriptEngine *engine)
+{
+ Q_UNUSED(context)
+ return engine->newQObject(new XmlDomNode(), QScriptEngine::ScriptOwnership);
+}
+
+bool XmlDomNode::isElement() const
+{
+ return m_domNode.isElement();
+}
+
+bool XmlDomNode::isCDATASection() const
+{
+ return m_domNode.isCDATASection();
+}
+
+bool XmlDomNode::isText() const
+{
+ return m_domNode.isText();
+}
+
+QString XmlDomNode::attribute(const QString &name, const QString &defValue)
+{
+ QDomElement el = m_domNode.toElement();
+ if (el.isNull()) {
+ context()->throwError(QString::fromLatin1("Node '%1' is not an element node").arg(m_domNode.nodeName()));
+ return defValue;
+ }
+ return el.attribute(name, defValue);
+}
+
+void XmlDomNode::setAttribute(const QString &name, const QString &value)
+{
+ QDomElement el = m_domNode.toElement();
+ if (el.isNull()) {
+ context()->throwError(QString::fromLatin1("Node '%1' is not an element node").arg(m_domNode.nodeName()));
+ return;
+ }
+ el.setAttribute(name, value);
+}
+
+bool XmlDomNode::hasAttribute(const QString &name) const
+{
+ QDomElement el = m_domNode.toElement();
+ if (el.isNull()) {
+ context()->throwError(QString::fromLatin1("Node '%1' is not an element node").arg(m_domNode.nodeName()));
+ return false;
+ }
+ return el.hasAttribute(name);
+}
+
+QString XmlDomNode::tagName() const
+{
+ QDomElement el = m_domNode.toElement();
+ if (el.isNull()) {
+ context()->throwError(QString::fromLatin1("Node '%1' is not an element node").arg(m_domNode.nodeName()));
+ return QString();
+ }
+ return el.tagName();
+}
+
+void XmlDomNode::setTagName(const QString &name)
+{
+ QDomElement el = m_domNode.toElement();
+ if (el.isNull()) {
+ context()->throwError(QString::fromLatin1("Node '%1' is not an element node").arg(m_domNode.nodeName()));
+ return;
+ }
+ el.setTagName(name);
+}
+
+QString XmlDomNode::text() const
+{
+ QDomElement el = m_domNode.toElement();
+ if (el.isNull()) {
+ context()->throwError(QString::fromLatin1("Node '%1' is not an element node").arg(m_domNode.nodeName()));
+ return QString();
+ }
+ return el.text();
+}
+
+QString XmlDomNode::data() const
+{
+ if (m_domNode.isText())
+ return m_domNode.toText().data();
+ if (m_domNode.isCDATASection())
+ return m_domNode.toCDATASection().data();
+ if (m_domNode.isCharacterData())
+ return m_domNode.toCharacterData().data();
+ context()->throwError(QString::fromLatin1("Node '%1' is not a character data node").arg(m_domNode.nodeName()));
+ return QString();
+}
+
+void XmlDomNode::setData(const QString &v) const
+{
+ if (m_domNode.isText())
+ return m_domNode.toText().setData(v);
+ if (m_domNode.isCDATASection())
+ return m_domNode.toCDATASection().setData(v);
+ if (m_domNode.isCharacterData())
+ return m_domNode.toCharacterData().setData(v);
+ context()->throwError(QString::fromLatin1("Node '%1' is not a character data node").arg(m_domNode.nodeName()));
+ return;
+}
+
+void XmlDomNode::clear()
+{
+ m_domNode.clear();
+}
+
+bool XmlDomNode::hasAttributes() const
+{
+ return m_domNode.hasAttributes();
+}
+
+bool XmlDomNode::hasChildNodes() const
+{
+ return m_domNode.hasChildNodes();
+}
+
+QScriptValue XmlDomNode::parentNode() const
+{
+ return engine()->newQObject(new XmlDomNode(m_domNode.parentNode()), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomNode::firstChild(const QString &tagName)
+{
+ if (tagName.isEmpty())
+ return engine()->newQObject(new XmlDomNode(m_domNode.firstChild()), QScriptEngine::ScriptOwnership);
+ return engine()->newQObject(new XmlDomNode(m_domNode.firstChildElement(tagName)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomNode::lastChild(const QString &tagName) const
+{
+ if (tagName.isEmpty())
+ return engine()->newQObject(new XmlDomNode(m_domNode.lastChild()), QScriptEngine::ScriptOwnership);
+ return engine()->newQObject(new XmlDomNode(m_domNode.lastChildElement(tagName)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomNode::previousSibling(const QString &tagName) const
+{
+ if (tagName.isEmpty())
+ return engine()->newQObject(new XmlDomNode(m_domNode.previousSibling()), QScriptEngine::ScriptOwnership);
+ return engine()->newQObject(new XmlDomNode(m_domNode.previousSiblingElement(tagName)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomNode::nextSibling(const QString &tagName) const
+{
+ if (tagName.isEmpty())
+ return engine()->newQObject(new XmlDomNode(m_domNode.nextSibling()), QScriptEngine::ScriptOwnership);
+ return engine()->newQObject(new XmlDomNode(m_domNode.nextSiblingElement(tagName)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomNode::appendChild(QScriptValue newChild)
+{
+ XmlDomNode *newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
+ if (!newNode) {
+ context()->throwError(QString::fromLatin1("First argument is not a XmlDomNode object"));
+ return QScriptValue();
+ }
+ return engine()->newQObject(new XmlDomNode(m_domNode.appendChild(newNode->m_domNode)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomNode::insertBefore(const QScriptValue &newChild, const QScriptValue &refChild)
+{
+ XmlDomNode *newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
+ if (!newNode) {
+ context()->throwError(QString::fromLatin1("First argument is not a XmlDomNode object"));
+ return QScriptValue();
+ }
+
+ XmlDomNode *refNode = qobject_cast<XmlDomNode*>(refChild.toQObject());
+ if (!refNode) {
+ context()->throwError(QString::fromLatin1("Second argument is not a XmlDomNode object"));
+ return QScriptValue();
+ }
+
+ return engine()->newQObject(new XmlDomNode(m_domNode.insertBefore(newNode->m_domNode, refNode->m_domNode)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomNode::insertAfter(const QScriptValue &newChild, const QScriptValue &refChild)
+{
+ XmlDomNode *newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
+ if (!newNode) {
+ context()->throwError(QString::fromLatin1("First argument is not a XmlDomNode object"));
+ return QScriptValue();
+ }
+
+ XmlDomNode *refNode = qobject_cast<XmlDomNode*>(refChild.toQObject());
+ if (!refNode) {
+ context()->throwError(QString::fromLatin1("Second argument is not a XmlDomNode object"));
+ return QScriptValue();
+ }
+
+ return engine()->newQObject(new XmlDomNode(m_domNode.insertAfter(newNode->m_domNode, refNode->m_domNode)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomNode::replaceChild(const QScriptValue &newChild, const QScriptValue &oldChild)
+{
+ XmlDomNode *newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
+ if (!newNode) {
+ context()->throwError(QString::fromLatin1("First argument is not a XmlDomNode object"));
+ return QScriptValue();
+ }
+
+ XmlDomNode *oldNode = qobject_cast<XmlDomNode*>(oldChild.toQObject());
+ if (!oldNode) {
+ context()->throwError(QString::fromLatin1("Second argument is not a XmlDomNode object"));
+ return QScriptValue();
+ }
+
+ return engine()->newQObject(new XmlDomNode(m_domNode.replaceChild(newNode->m_domNode, oldNode->m_domNode)), QScriptEngine::ScriptOwnership);
+}
+
+QScriptValue XmlDomNode::removeChild(const QScriptValue &oldChild)
+{
+ XmlDomNode *oldNode = qobject_cast<XmlDomNode*>(oldChild.toQObject());
+ if (!oldNode) {
+ context()->throwError(QString::fromLatin1("First argument is not a XmlDomNode object"));
+ return QScriptValue();
+ }
+
+ return engine()->newQObject(new XmlDomNode(m_domNode.removeChild(oldNode->m_domNode)), QScriptEngine::ScriptOwnership);
+}
+
+XmlDomNode::XmlDomNode(const QDomNode &other)
+{
+ m_domNode = other;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/jsextensions/domxml.h b/src/lib/corelib/jsextensions/domxml.h
new file mode 100644
index 000000000..7df603328
--- /dev/null
+++ b/src/lib/corelib/jsextensions/domxml.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+** 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.
+**
+****************************************************************************/
+
+#ifndef DOMXML_H
+#define DOMXML_H
+
+#include <QDomDocument>
+#include <QDomNode>
+#include <QObject>
+#include <QScriptValue>
+#include <QScriptable>
+#include <QVariant>
+
+namespace qbs {
+namespace Internal {
+
+class XmlDomDocument;
+
+void initializeJsExtensionXml(QScriptValue extensionObject);
+
+class XmlDomNode: public QObject, public QScriptable
+{
+ Q_OBJECT
+public:
+ static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
+
+ Q_INVOKABLE bool isElement() const;
+ Q_INVOKABLE bool isCDATASection() const;
+ Q_INVOKABLE bool isText() const;
+
+ Q_INVOKABLE QString attribute(const QString & name, const QString & defValue = QString());
+ Q_INVOKABLE void setAttribute(const QString & name, const QString & value);
+ Q_INVOKABLE bool hasAttribute(const QString & name) const;
+ Q_INVOKABLE QString tagName() const;
+ Q_INVOKABLE void setTagName(const QString & name);
+
+ Q_INVOKABLE QString text() const;
+
+ Q_INVOKABLE QString data() const;
+ Q_INVOKABLE void setData(const QString &v) const;
+
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE bool hasAttributes() const;
+ Q_INVOKABLE bool hasChildNodes() const;
+ Q_INVOKABLE QScriptValue parentNode() const;
+ Q_INVOKABLE QScriptValue firstChild(const QString & tagName = QString());
+ Q_INVOKABLE QScriptValue lastChild(const QString & tagName = QString()) const;
+ Q_INVOKABLE QScriptValue previousSibling(const QString & tagName = QString()) const;
+ Q_INVOKABLE QScriptValue nextSibling(const QString & tagName = QString()) const;
+
+ Q_INVOKABLE QScriptValue appendChild(QScriptValue newChild);
+ Q_INVOKABLE QScriptValue insertBefore(const QScriptValue& newChild, const QScriptValue& refChild);
+ Q_INVOKABLE QScriptValue insertAfter(const QScriptValue& newChild, const QScriptValue& refChild);
+ Q_INVOKABLE QScriptValue replaceChild(const QScriptValue& newChild, const QScriptValue& oldChild);
+ Q_INVOKABLE QScriptValue removeChild(const QScriptValue& oldChild);
+
+protected:
+ friend class XmlDomDocument;
+ XmlDomNode(const QDomNode &other = QDomNode());
+ QDomNode m_domNode;
+};
+
+class XmlDomDocument: public XmlDomNode
+{
+ Q_OBJECT
+public:
+ static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
+ Q_INVOKABLE QScriptValue documentElement();
+ Q_INVOKABLE QScriptValue createElement(const QString & tagName);
+ Q_INVOKABLE QScriptValue createCDATASection(const QString & value);
+ Q_INVOKABLE QScriptValue createTextNode(const QString & value);
+
+ Q_INVOKABLE bool setContent(const QString & content);
+ Q_INVOKABLE QString toString(int indent = 1);
+
+ Q_INVOKABLE void save(const QString & filePath, int indent = 1);
+ Q_INVOKABLE void load(const QString & filePath);
+
+protected:
+ XmlDomDocument(QScriptContext *context, const QString &name = QString());
+
+private:
+ QDomDocument m_domDocument;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::Internal::XmlDomDocument *)
+Q_DECLARE_METATYPE(qbs::Internal::XmlDomNode *)
+
+#endif // DOMXML_H
diff --git a/src/lib/corelib/jsextensions/file.cpp b/src/lib/corelib/jsextensions/file.cpp
new file mode 100644
index 000000000..1e6f2947c
--- /dev/null
+++ b/src/lib/corelib/jsextensions/file.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** 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 "file.h"
+
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+#include <tools/fileinfo.h>
+
+#include <QFileInfo>
+#include <QScriptEngine>
+
+namespace qbs {
+namespace Internal {
+
+class File
+{
+ friend void initializeJsExtensionFile(QScriptValue extensionObject);
+
+private:
+ static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_copy(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_exists(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_lastModified(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_remove(QScriptContext *context, QScriptEngine *engine);
+};
+
+
+void initializeJsExtensionFile(QScriptValue extensionObject)
+{
+ QScriptEngine *engine = extensionObject.engine();
+ QScriptValue fileObj = engine->newFunction(File::js_ctor);
+ fileObj.setProperty("copy", engine->newFunction(File::js_copy));
+ fileObj.setProperty("exists", engine->newFunction(File::js_exists));
+ fileObj.setProperty("lastModified", engine->newFunction(File::js_lastModified));
+ fileObj.setProperty("remove", engine->newFunction(File::js_remove));
+ extensionObject.setProperty("File", fileObj);
+}
+
+QScriptValue File::js_ctor(QScriptContext *context, QScriptEngine *engine)
+{
+ // Add instance variables here etc., if needed.
+ Q_UNUSED(context);
+ Q_UNUSED(engine);
+ return true;
+}
+
+QScriptValue File::js_copy(QScriptContext *context, QScriptEngine *engine)
+{
+ Q_UNUSED(engine);
+ if (Q_UNLIKELY(context->argumentCount() < 2)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ Tr::tr("copy expects 2 arguments"));
+ }
+
+ const QString sourceFile = context->argument(0).toString();
+ const QString targetFile = context->argument(1).toString();
+ QString errorMessage;
+ if (Q_UNLIKELY(!copyFileRecursion(sourceFile, targetFile, false, &errorMessage)))
+ return context->throwError(errorMessage);
+ return true;
+}
+
+QScriptValue File::js_exists(QScriptContext *context, QScriptEngine *engine)
+{
+ Q_UNUSED(engine);
+ if (Q_UNLIKELY(context->argumentCount() < 1)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ Tr::tr("exist expects 1 argument"));
+ }
+ const QString filePath = context->argument(0).toString();
+ const bool exists = FileInfo::exists(filePath);
+ ScriptEngine * const se = static_cast<ScriptEngine *>(engine);
+ se->addFileExistsResult(filePath, exists);
+ return exists;
+}
+
+QScriptValue File::js_remove(QScriptContext *context, QScriptEngine *engine)
+{
+ Q_UNUSED(engine);
+ if (Q_UNLIKELY(context->argumentCount() < 1)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ Tr::tr("remove expects 1 argument"));
+ }
+ QString fileName = context->argument(0).toString();
+
+ QString errorMessage;
+ if (Q_UNLIKELY(!removeFileRecursion(QFileInfo(fileName), &errorMessage)))
+ return context->throwError(errorMessage);
+ return true;
+}
+
+QScriptValue File::js_lastModified(QScriptContext *context, QScriptEngine *engine)
+{
+ Q_UNUSED(engine);
+ if (Q_UNLIKELY(context->argumentCount() < 1)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ Tr::tr("File.lastModified() expects an argument"));
+ }
+ const QString filePath = context->argument(0).toString();
+ const FileTime timestamp = FileInfo(filePath).lastModified();
+ ScriptEngine * const se = static_cast<ScriptEngine *>(engine);
+ se->addFileLastModifiedResult(filePath, timestamp);
+ return static_cast<qsreal>(timestamp.m_fileTime);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/jsextensions/file.h b/src/lib/corelib/jsextensions/file.h
new file mode 100644
index 000000000..874c40c71
--- /dev/null
+++ b/src/lib/corelib/jsextensions/file.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_FILE_H
+#define QBS_FILE_H
+
+#include <QScriptContext>
+#include <QScriptValue>
+
+namespace qbs {
+namespace Internal {
+
+void initializeJsExtensionFile(QScriptValue extensionObject);
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_FILE_H
diff --git a/src/lib/corelib/jsextensions/jsextensions.cpp b/src/lib/corelib/jsextensions/jsextensions.cpp
new file mode 100644
index 000000000..b6b5e099b
--- /dev/null
+++ b/src/lib/corelib/jsextensions/jsextensions.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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 "jsextensions.h"
+
+#include "domxml.h"
+#include "file.h"
+#include "process.h"
+#include "textfile.h"
+
+namespace qbs {
+namespace Internal {
+
+void JsExtensions::setupExtensions(const QStringList &names, QScriptValue scope)
+{
+ foreach (const QString &name, names)
+ initializers().value(name)(scope);
+}
+
+bool JsExtensions::hasExtension(const QString &name)
+{
+ return initializers().contains(name);
+}
+
+JsExtensions::InitializerMap JsExtensions::initializers()
+{
+ if (m_initializers.isEmpty()) {
+ m_initializers.insert(QLatin1String("File"), &initializeJsExtensionFile);
+ m_initializers.insert(QLatin1String("Process"), &initializeJsExtensionProcess);
+ m_initializers.insert(QLatin1String("Xml"), &initializeJsExtensionXml);
+ m_initializers.insert(QLatin1String("TextFile"), &initializeJsExtensionTextFile);
+ }
+ return m_initializers;
+}
+
+JsExtensions::InitializerMap JsExtensions::m_initializers;
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/jsextensions/jsextensions.h b/src/lib/corelib/jsextensions/jsextensions.h
new file mode 100644
index 000000000..e268ec215
--- /dev/null
+++ b/src/lib/corelib/jsextensions/jsextensions.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_JSEXTENSIONS_H
+#define QBS_JSEXTENSIONS_H
+
+#include <QHash>
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+class QScriptValue;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+class JsExtensions
+{
+public:
+ static void setupExtensions(const QStringList &names, QScriptValue scope);
+ static bool hasExtension(const QString &name);
+
+private:
+ typedef QHash<QString, void (*)(QScriptValue)> InitializerMap;
+ static InitializerMap initializers();
+
+ static InitializerMap m_initializers;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/jsextensions/jsextensions.pri b/src/lib/corelib/jsextensions/jsextensions.pri
new file mode 100644
index 000000000..21c97ef64
--- /dev/null
+++ b/src/lib/corelib/jsextensions/jsextensions.pri
@@ -0,0 +1,17 @@
+QT += xml
+
+HEADERS += \
+ $$PWD/file.h \
+ $$PWD/textfile.h \
+ $$PWD/process.h \
+ $$PWD/moduleproperties.h \
+ $$PWD/domxml.h \
+ $$PWD/jsextensions.h
+
+SOURCES += \
+ $$PWD/file.cpp \
+ $$PWD/textfile.cpp \
+ $$PWD/process.cpp \
+ $$PWD/moduleproperties.cpp \
+ $$PWD/domxml.cpp \
+ $$PWD/jsextensions.cpp
diff --git a/src/lib/corelib/jsextensions/moduleproperties.cpp b/src/lib/corelib/jsextensions/moduleproperties.cpp
new file mode 100644
index 000000000..83d92e776
--- /dev/null
+++ b/src/lib/corelib/jsextensions/moduleproperties.cpp
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** 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 "moduleproperties.h"
+
+#include <buildgraph/artifact.h>
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/propertyfinder.h>
+
+#include <QScriptEngine>
+
+namespace qbs {
+namespace Internal {
+
+static QString ptrKey() { return QLatin1String("__internalPtr"); }
+static QString typeKey() { return QLatin1String("__type"); }
+static QString productType() { return QLatin1String("product"); }
+static QString artifactType() { return QLatin1String("artifact"); }
+
+void ModuleProperties::init(QScriptValue productObject,
+ const ResolvedProductConstPtr &product)
+{
+ init(productObject, product.data(), productType());
+}
+
+void ModuleProperties::init(QScriptValue artifactObject, const Artifact *artifact)
+{
+ init(artifactObject, artifact, artifactType());
+}
+
+void ModuleProperties::init(QScriptValue objectWithProperties, const void *ptr,
+ const QString &type)
+{
+ QScriptEngine * const engine = objectWithProperties.engine();
+ objectWithProperties.setProperty("moduleProperties",
+ engine->newFunction(ModuleProperties::js_moduleProperties, 2));
+ objectWithProperties.setProperty("moduleProperty",
+ engine->newFunction(ModuleProperties::js_moduleProperty, 2));
+ objectWithProperties.setProperty(ptrKey(), engine->toScriptValue(quintptr(ptr)));
+ objectWithProperties.setProperty(typeKey(), type);
+}
+
+QScriptValue ModuleProperties::js_moduleProperties(QScriptContext *context, QScriptEngine *engine)
+{
+ try {
+ return moduleProperties(context, engine, false);
+ } catch (const ErrorInfo &e) {
+ return context->throwError(e.toString());
+ }
+}
+
+QScriptValue ModuleProperties::js_moduleProperty(QScriptContext *context, QScriptEngine *engine)
+{
+ try {
+ return moduleProperties(context, engine, true);
+ } catch (const ErrorInfo &e) {
+ return context->throwError(e.toString());
+ }
+}
+
+QScriptValue ModuleProperties::moduleProperties(QScriptContext *context, QScriptEngine *engine,
+ bool oneValue)
+{
+ if (Q_UNLIKELY(context->argumentCount() < 2)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ Tr::tr("Function moduleProperties() expects 2 arguments"));
+ }
+
+ const QScriptValue objectWithProperties = context->thisObject();
+ const QScriptValue typeScriptValue = objectWithProperties.property(typeKey());
+ if (Q_UNLIKELY(!typeScriptValue.isString())) {
+ return context->throwError(QScriptContext::TypeError,
+ QLatin1String("Internal error: __type not set up"));
+ }
+ const QScriptValue ptrScriptValue = objectWithProperties.property(ptrKey());
+ if (Q_UNLIKELY(!ptrScriptValue.isNumber())) {
+ return context->throwError(QScriptContext::TypeError,
+ QLatin1String("Internal error: __internalPtr not set up"));
+ }
+
+ const void *ptr = reinterpret_cast<const void *>(qscriptvalue_cast<quintptr>(ptrScriptValue));
+ PropertyMapConstPtr properties;
+ const Artifact *artifact = 0;
+ if (typeScriptValue.toString() == productType()) {
+ properties = static_cast<const ResolvedProduct *>(ptr)->properties;
+ } else if (typeScriptValue.toString() == artifactType()) {
+ artifact = static_cast<const Artifact *>(ptr);
+ properties = artifact->properties;
+ } else {
+ return context->throwError(QScriptContext::TypeError,
+ QLatin1String("Internal error: invalid type"));
+ }
+
+ ScriptEngine * const qbsEngine = static_cast<ScriptEngine *>(engine);
+ const QString moduleName = internalModuleName(context->argument(0).toString());
+ const QString propertyName = context->argument(1).toString();
+
+ QVariant value = qbsEngine->retrieveFromPropertyCache(moduleName, propertyName, properties);
+ if (!value.isValid()) {
+ if (oneValue)
+ value = PropertyFinder().propertyValue(properties->value(), moduleName, propertyName);
+ else
+ value = PropertyFinder().propertyValues(properties->value(), moduleName, propertyName);
+ const Property p(moduleName, propertyName, value);
+ if (artifact)
+ qbsEngine->addPropertyRequestedFromArtifact(artifact, p);
+ else
+ qbsEngine->addPropertyRequestedInScript(p);
+
+ // Cache the variant value. We must not cache the QScriptValue here, because it's a
+ // reference and the user might change the actual object.
+ qbsEngine->addToPropertyCache(moduleName, propertyName, properties, value);
+ }
+ return engine->toScriptValue(value);
+}
+
+QString ModuleProperties::internalModuleName(const QString &name)
+{
+ QString result = name;
+ result.replace(QLatin1Char('.'), QLatin1Char('/'));
+ return result;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/jsextensions/moduleproperties.h b/src/lib/corelib/jsextensions/moduleproperties.h
new file mode 100644
index 000000000..81f27f6ec
--- /dev/null
+++ b/src/lib/corelib/jsextensions/moduleproperties.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_MODULEPROPERTIES_H
+#define QBS_MODULEPROPERTIES_H
+
+#include <buildgraph/forward_decls.h>
+#include <language/forward_decls.h>
+
+#include <QScriptContext>
+#include <QScriptValue>
+
+namespace qbs {
+namespace Internal {
+
+class ModuleProperties
+{
+public:
+ static void init(QScriptValue productObject, const ResolvedProductConstPtr &product);
+ static void init(QScriptValue artifactObject, const Artifact *artifact);
+
+private:
+ static void init(QScriptValue objectWithProperties, const void *ptr, const QString &type);
+
+ static QScriptValue js_moduleProperties(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_moduleProperty(QScriptContext *context, QScriptEngine *engine);
+
+ static QScriptValue moduleProperties(QScriptContext *context, QScriptEngine *engine,
+ bool oneValue);
+ static QString internalModuleName(const QString &name);
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_MODULEPROPERTIES_H
diff --git a/src/lib/corelib/jsextensions/process.cpp b/src/lib/corelib/jsextensions/process.cpp
new file mode 100644
index 000000000..3cf74ab31
--- /dev/null
+++ b/src/lib/corelib/jsextensions/process.cpp
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** 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 "process.h"
+
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+
+#include <QProcess>
+#include <QScriptEngine>
+#include <QScriptValue>
+#include <QTextCodec>
+#include <QTextStream>
+
+namespace qbs {
+namespace Internal {
+
+void initializeJsExtensionProcess(QScriptValue extensionObject)
+{
+ QScriptEngine *engine = extensionObject.engine();
+ QScriptValue obj = engine->newQMetaObject(&Process::staticMetaObject, engine->newFunction(&Process::ctor));
+ extensionObject.setProperty("Process", obj);
+}
+
+QScriptValue Process::ctor(QScriptContext *context, QScriptEngine *engine)
+{
+ Process *t;
+ switch (context->argumentCount()) {
+ case 0:
+ t = new Process(context);
+ break;
+ default:
+ return context->throwError("Process()");
+ }
+
+ QScriptValue obj = engine->newQObject(t, QScriptEngine::ScriptOwnership);
+
+ // Get environment
+ QVariant v = engine->property("_qbs_procenv");
+ if (!v.isNull())
+ t->m_environment
+ = QProcessEnvironment(*reinterpret_cast<QProcessEnvironment*>(v.value<void*>()));
+
+ return obj;
+}
+
+Process::~Process()
+{
+ delete m_textStream;
+ delete m_qProcess;
+}
+
+Process::Process(QScriptContext *context)
+{
+ Q_UNUSED(context);
+ Q_ASSERT(thisObject().engine() == engine());
+
+ m_qProcess = new QProcess;
+ m_textStream = new QTextStream(m_qProcess);
+}
+
+QString Process::getEnv(const QString &name)
+{
+ Q_ASSERT(thisObject().engine() == engine());
+ return m_environment.value(name);
+}
+
+void Process::setEnv(const QString &name, const QString &value)
+{
+ Q_ASSERT(thisObject().engine() == engine());
+ m_environment.insert(name, value);
+}
+
+QString Process::workingDirectory()
+{
+ Q_ASSERT(thisObject().engine() == engine());
+ return m_workingDirectory;
+}
+
+void Process::setWorkingDirectory(const QString &dir)
+{
+ Q_ASSERT(thisObject().engine() == engine());
+ m_workingDirectory = dir;
+}
+
+bool Process::start(const QString &program, const QStringList &arguments)
+{
+ Q_ASSERT(thisObject().engine() == engine());
+
+ if (!m_workingDirectory.isEmpty())
+ m_qProcess->setWorkingDirectory(m_workingDirectory);
+
+ m_qProcess->setProcessEnvironment(m_environment);
+ m_qProcess->start(program, arguments);
+ return m_qProcess->waitForStarted();
+}
+
+int Process::exec(const QString &program, const QStringList &arguments, bool throwOnError)
+{
+ Q_ASSERT(thisObject().engine() == engine());
+
+ if (!start(program, arguments)) {
+ if (throwOnError) {
+ context()->throwError(Tr::tr("Error running '%1': %2")
+ .arg(program, m_qProcess->errorString()));
+ }
+ return -1;
+ }
+ m_qProcess->closeWriteChannel();
+ m_qProcess->waitForFinished(-1);
+ if (throwOnError) {
+ if (m_qProcess->error() != QProcess::UnknownError) {
+ context()->throwError(Tr::tr("Error running '%1': %2")
+ .arg(program, m_qProcess->errorString()));
+ } else if (m_qProcess->exitCode() != 0) {
+ QString errorMessage = Tr::tr("Process '%1' finished with exit code %2.")
+ .arg(program).arg(m_qProcess->exitCode());
+ const QString stdErr = readStdErr();
+ if (!stdErr.isEmpty())
+ errorMessage.append(Tr::tr(" The standard error output was:\n")).append(stdErr);
+ context()->throwError(errorMessage);
+ }
+ }
+ if (m_qProcess->error() != QProcess::UnknownError)
+ return -1;
+ return m_qProcess->exitCode();
+}
+
+void Process::close()
+{
+ Q_ASSERT(thisObject().engine() == engine());
+ delete m_qProcess;
+ m_qProcess = 0;
+ delete m_textStream;
+ m_textStream = 0;
+}
+
+bool Process::waitForFinished(int msecs)
+{
+ Q_ASSERT(thisObject().engine() == engine());
+
+ if (m_qProcess->state() == QProcess::NotRunning)
+ return true;
+ return m_qProcess->waitForFinished(msecs);
+}
+
+void Process::terminate()
+{
+ m_qProcess->terminate();
+}
+
+void Process::kill()
+{
+ m_qProcess->kill();
+}
+
+void Process::setCodec(const QString &codec)
+{
+ Q_ASSERT(thisObject().engine() == engine());
+ m_textStream->setCodec(qPrintable(codec));
+}
+
+QString Process::readLine()
+{
+ return m_textStream->readLine();
+}
+
+QString Process::readStdOut()
+{
+ return m_textStream->readAll();
+}
+
+QString Process::readStdErr()
+{
+ return m_textStream->codec()->toUnicode(m_qProcess->readAllStandardError());
+}
+
+int Process::exitCode() const
+{
+ return m_qProcess->exitCode();
+}
+
+void Process::write(const QString &str)
+{
+ (*m_textStream) << str;
+}
+
+void Process::writeLine(const QString &str)
+{
+ (*m_textStream) << str;
+ if (HostOsInfo::isWindowsHost())
+ (*m_textStream) << '\r';
+ (*m_textStream) << '\n';
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/jsextensions/process.h b/src/lib/corelib/jsextensions/process.h
new file mode 100644
index 000000000..1dea021a3
--- /dev/null
+++ b/src/lib/corelib/jsextensions/process.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_PROCESS_H
+#define QBS_PROCESS_H
+
+#include <QObject>
+#include <QProcessEnvironment>
+#include <QScriptable>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+class QProcess;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+void initializeJsExtensionProcess(QScriptValue extensionObject);
+
+class Process : public QObject, public QScriptable
+{
+ Q_OBJECT
+public:
+ static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
+ Process(QScriptContext *context);
+ ~Process();
+
+ Q_INVOKABLE QString getEnv(const QString &name);
+ Q_INVOKABLE void setEnv(const QString &name, const QString &value);
+ Q_INVOKABLE void setCodec(const QString &codec);
+
+ Q_INVOKABLE QString workingDirectory();
+ Q_INVOKABLE void setWorkingDirectory(const QString &dir);
+
+ Q_INVOKABLE bool start(const QString &program, const QStringList &arguments);
+ Q_INVOKABLE int exec(const QString &program, const QStringList &arguments,
+ bool throwOnError = false);
+ Q_INVOKABLE void close();
+ Q_INVOKABLE bool waitForFinished(int msecs = 30000);
+ Q_INVOKABLE void terminate();
+ Q_INVOKABLE void kill();
+
+ Q_INVOKABLE QString readLine();
+ Q_INVOKABLE QString readStdOut();
+ Q_INVOKABLE QString readStdErr();
+
+ Q_INVOKABLE void write(const QString &str);
+ Q_INVOKABLE void writeLine(const QString &str);
+
+ Q_INVOKABLE int exitCode() const;
+
+private:
+ QProcess *m_qProcess;
+ QProcessEnvironment m_environment;
+ QString m_workingDirectory;
+ QTextStream *m_textStream;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::Internal::Process *)
+
+#endif // QBS_PROCESS_H
diff --git a/src/lib/corelib/jsextensions/textfile.cpp b/src/lib/corelib/jsextensions/textfile.cpp
new file mode 100644
index 000000000..7129ee955
--- /dev/null
+++ b/src/lib/corelib/jsextensions/textfile.cpp
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** 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 "textfile.h"
+
+#include <tools/hostosinfo.h>
+
+#include <QFile>
+#include <QScriptEngine>
+#include <QScriptValue>
+#include <QTextStream>
+
+namespace qbs {
+namespace Internal {
+
+void initializeJsExtensionTextFile(QScriptValue extensionObject)
+{
+ QScriptEngine *engine = extensionObject.engine();
+ QScriptValue obj = engine->newQMetaObject(&TextFile::staticMetaObject, engine->newFunction(&TextFile::ctor));
+ extensionObject.setProperty("TextFile", obj);
+}
+
+QScriptValue TextFile::ctor(QScriptContext *context, QScriptEngine *engine)
+{
+ TextFile *t;
+ switch (context->argumentCount()) {
+ case 1:
+ t = new TextFile(context,
+ context->argument(0).toString());
+ break;
+ case 2:
+ t = new TextFile(context,
+ context->argument(0).toString(),
+ static_cast<OpenMode>(context->argument(1).toInt32())
+ );
+ break;
+ case 3:
+ t = new TextFile(context,
+ context->argument(0).toString(),
+ static_cast<OpenMode>(context->argument(1).toInt32()),
+ context->argument(2).toString()
+ );
+ break;
+ default:
+ return context->throwError("TextFile(QString file, OpenMode mode = ReadOnly, QString codec = QLatin1String(\"UTF8\"))");
+ }
+
+ QScriptValue obj = engine->newQObject(t, QScriptEngine::ScriptOwnership);
+// obj.setProperty("d", engine->newQObject(new FileImplementation(t),
+// QScriptEngine::QScriptEngine::QtOwnership));
+ return obj;
+}
+
+TextFile::~TextFile()
+{
+ delete qstream;
+ delete qfile;
+}
+
+TextFile::TextFile(QScriptContext *context, const QString &file, OpenMode mode, const QString &codec)
+{
+ Q_UNUSED(codec)
+ Q_ASSERT(thisObject().engine() == engine());
+ TextFile *t = this;
+
+ t->qfile = new QFile(file);
+ QIODevice::OpenMode m = QIODevice::ReadOnly;
+ if (mode == ReadWrite)
+ m = QIODevice::ReadWrite;
+ else if (mode == ReadOnly)
+ m = QIODevice::ReadOnly;
+ else if (mode == WriteOnly)
+ m = QIODevice::WriteOnly;
+ if (Q_UNLIKELY(!t->qfile->open(m))) {
+ delete t->qfile;
+ t->qfile = 0;
+ context->throwError(QString::fromLatin1("unable to open '%1'")
+ .arg(file)
+ );
+ }
+
+ t->qstream = new QTextStream(t->qfile);
+}
+
+void TextFile::close()
+{
+ Q_ASSERT(thisObject().engine() == engine());
+ TextFile *t = qscriptvalue_cast<TextFile*>(thisObject());
+ if (t->qfile)
+ t->qfile->close();
+ delete t->qfile;
+ t->qfile = 0;
+ delete t->qstream;
+ t->qstream = 0;
+}
+
+void TextFile::setCodec(const QString &codec)
+{
+ Q_ASSERT(thisObject().engine() == engine());
+ TextFile *t = qscriptvalue_cast<TextFile*>(thisObject());
+ if (!t->qstream)
+ return;
+ t->qstream->setCodec(qPrintable(codec));
+}
+
+QString TextFile::readLine()
+{
+ TextFile *t = qscriptvalue_cast<TextFile*>(thisObject());
+ if (!t->qfile)
+ return QString();
+ return t->qstream->readLine();
+}
+
+QString TextFile::readAll()
+{
+ TextFile *t = qscriptvalue_cast<TextFile*>(thisObject());
+ if (!t->qfile)
+ return QString();
+ return t->qstream->readAll();
+}
+
+bool TextFile::atEof() const
+{
+ TextFile *t = qscriptvalue_cast<TextFile*>(thisObject());
+ if (!t->qstream)
+ return true;
+ return t->qstream->atEnd();
+}
+
+void TextFile::truncate()
+{
+ TextFile *t = qscriptvalue_cast<TextFile*>(thisObject());
+ if (!t->qstream)
+ return;
+ t->qfile->resize(0);
+ t->qstream->reset();
+}
+
+void TextFile::write(const QString &str)
+{
+ TextFile *t = qscriptvalue_cast<TextFile*>(thisObject());
+ if (!t->qstream)
+ return;
+ (*t->qstream) << str;
+}
+
+void TextFile::writeLine(const QString &str)
+{
+ TextFile *t = qscriptvalue_cast<TextFile*>(thisObject());
+ if (!t->qstream)
+ return;
+ (*t->qstream) << str;
+ if (HostOsInfo::isWindowsHost())
+ (*t->qstream) << '\r';
+ (*t->qstream) << '\n';
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/jsextensions/textfile.h b/src/lib/corelib/jsextensions/textfile.h
new file mode 100644
index 000000000..ff8aaf43b
--- /dev/null
+++ b/src/lib/corelib/jsextensions/textfile.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_TEXTFILE_H
+#define QBS_TEXTFILE_H
+
+#include <QObject>
+#include <QScriptable>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+class QFile;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+void initializeJsExtensionTextFile(QScriptValue extensionObject);
+
+class TextFile : public QObject, public QScriptable
+{
+ Q_OBJECT
+ Q_ENUMS(OpenMode)
+public:
+ enum OpenMode { ReadOnly, WriteOnly, ReadWrite };
+ static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
+ TextFile(QScriptContext *context, const QString &file, OpenMode mode = ReadOnly, const QString &codec = QLatin1String("UTF8"));
+ ~TextFile();
+ Q_INVOKABLE void close();
+ Q_INVOKABLE void setCodec(const QString &codec);
+ Q_INVOKABLE QString readLine();
+ Q_INVOKABLE QString readAll();
+ Q_INVOKABLE bool atEof() const;
+ Q_INVOKABLE void truncate();
+ Q_INVOKABLE void write(const QString &str);
+ Q_INVOKABLE void writeLine(const QString &str);
+private:
+ QFile *qfile;
+ QTextStream *qstream;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::Internal::TextFile *)
+
+#endif // QBS_TEXTFILE_H
diff --git a/src/lib/corelib/language/artifactproperties.cpp b/src/lib/corelib/language/artifactproperties.cpp
new file mode 100644
index 000000000..470163004
--- /dev/null
+++ b/src/lib/corelib/language/artifactproperties.cpp
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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 "artifactproperties.h"
+#include <language/propertymapinternal.h>
+#include <tools/persistence.h>
+
+namespace qbs {
+namespace Internal {
+
+ArtifactPropertiesPtr ArtifactProperties::create()
+{
+ return ArtifactPropertiesPtr(new ArtifactProperties);
+}
+
+ArtifactProperties::ArtifactProperties()
+{
+}
+
+void ArtifactProperties::load(PersistentPool &pool)
+{
+ pool.stream() >> m_fileTagsFilter;
+ m_propertyMap = pool.idLoadS<PropertyMapInternal>();
+}
+
+void ArtifactProperties::store(PersistentPool &pool) const
+{
+ pool.stream() << m_fileTagsFilter;
+ pool.store(m_propertyMap);
+}
+
+bool operator==(const ArtifactProperties &ap1, const ArtifactProperties &ap2)
+{
+ return ap1.fileTagsFilter() == ap2.fileTagsFilter()
+ && ap1.propertyMap()->value() == ap2.propertyMap()->value();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/artifactproperties.h b/src/lib/corelib/language/artifactproperties.h
new file mode 100644
index 000000000..8c688da20
--- /dev/null
+++ b/src/lib/corelib/language/artifactproperties.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ARTIFACTPROPERTIES_H
+#define QBS_ARTIFACTPROPERTIES_H
+
+#include "language.h"
+#include "forward_decls.h"
+#include <tools/persistentobject.h>
+
+namespace qbs {
+namespace Internal {
+
+class ArtifactProperties : public PersistentObject
+{
+public:
+ static ArtifactPropertiesPtr create();
+
+ void setFileTagsFilter(const FileTags &filter) { m_fileTagsFilter = filter; }
+ FileTags fileTagsFilter() const { return m_fileTagsFilter; }
+
+ PropertyMapPtr propertyMap() const { return m_propertyMap; }
+ void setPropertyMapInternal(const PropertyMapPtr &pmap) { m_propertyMap = pmap; }
+
+private:
+ ArtifactProperties();
+
+ void load(PersistentPool &);
+ void store(PersistentPool &) const;
+
+ FileTags m_fileTagsFilter;
+ PropertyMapPtr m_propertyMap;
+};
+
+bool operator==(const ArtifactProperties &ap1, const ArtifactProperties &ap2);
+inline bool operator!=(const ArtifactProperties &ap1, const ArtifactProperties &ap2) {
+ return !(ap1 == ap2);
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ARTIFACTPROPERTIES_H
diff --git a/src/lib/corelib/language/asttools.cpp b/src/lib/corelib/language/asttools.cpp
new file mode 100644
index 000000000..c394fc187
--- /dev/null
+++ b/src/lib/corelib/language/asttools.cpp
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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 "asttools.h"
+#include <parser/qmljsast_p.h>
+
+namespace qbs {
+namespace Internal {
+
+QStringList toStringList(QbsQmlJS::AST::UiQualifiedId *qid)
+{
+ QStringList result;
+ for (; qid; qid = qid->next)
+ result.append(qid->name.toString());
+ return result;
+}
+
+CodeLocation toCodeLocation(const QString &filePath, QbsQmlJS::AST::SourceLocation location)
+{
+ return CodeLocation(filePath, location.startLine, location.startColumn);
+}
+
+QString textOf(const QString &source, QbsQmlJS::AST::Node *node)
+{
+ if (!node)
+ return QString();
+ return source.mid(node->firstSourceLocation().begin(),
+ node->lastSourceLocation().end() - node->firstSourceLocation().begin());
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/asttools.h b/src/lib/corelib/language/asttools.h
new file mode 100644
index 000000000..cb0f0b852
--- /dev/null
+++ b/src/lib/corelib/language/asttools.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ASTTOOLS_H
+#define QBS_ASTTOOLS_H
+
+#include <parser/qmljsastfwd_p.h>
+#include <tools/codelocation.h>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+
+QStringList toStringList(QbsQmlJS::AST::UiQualifiedId *qid);
+CodeLocation toCodeLocation(const QString &filePath, QbsQmlJS::AST::SourceLocation location);
+QString textOf(const QString &source, QbsQmlJS::AST::Node *node);
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ASTTOOLS_H
diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp
new file mode 100644
index 000000000..4bbc02579
--- /dev/null
+++ b/src/lib/corelib/language/builtindeclarations.cpp
@@ -0,0 +1,401 @@
+/****************************************************************************
+**
+** 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 "builtindeclarations.h"
+#include "item.h"
+
+namespace qbs {
+namespace Internal {
+
+const char QBS_LANGUAGE_VERSION[] = "1.0";
+
+BuiltinDeclarations::BuiltinDeclarations()
+ : m_languageVersion(QLatin1String(QBS_LANGUAGE_VERSION))
+{
+ addArtifactItem();
+ addDependsItem();
+ addExportItem();
+ addFileTaggerItem();
+ addGroupItem();
+ addModuleItem();
+ addProbeItem();
+ addProductItem();
+ addProjectItem();
+ addPropertiesItem();
+ addPropertyOptionsItem();
+ addRuleItem();
+ addSubprojectItem();
+ addTransformerItem();
+}
+
+QString BuiltinDeclarations::languageVersion() const
+{
+ return m_languageVersion;
+}
+
+QByteArray BuiltinDeclarations::qmlTypeInfo() const
+{
+ // Header:
+ QByteArray result;
+ result.append("import QtQuick.tooling 1.0\n\n");
+ result.append("// This file describes the plugin-supplied types contained in the library.\n");
+ result.append("// It is used for QML tooling purposes only.\n\n");
+ result.append("Module {\n");
+
+ // Individual Components:
+ foreach (const QString &component, m_builtins.keys()) {
+ QByteArray componentName = component.toUtf8();
+ result.append(" Component {\n");
+ result.append(QByteArray(" name: \"") + componentName + QByteArray("\"\n"));
+ result.append(" exports: [ \"qbs/");
+ result.append(componentName);
+ result.append(" ");
+ result.append(QBS_LANGUAGE_VERSION);
+ result.append("\" ]\n");
+ result.append(" prototype: \"QQuickItem\"\n");
+
+ ItemDeclaration itemDecl = m_builtins.value(component);
+ foreach (const PropertyDeclaration &property, itemDecl.properties()) {
+ result.append(" Property { name=\"");
+ result.append(property.name.toUtf8());
+ result.append("\"; ");
+ switch (property.type) {
+ case qbs::Internal::PropertyDeclaration::UnknownType:
+ result.append("type=\"unknown\"");
+ break;
+ case qbs::Internal::PropertyDeclaration::Boolean:
+ result.append("type=\"bool\"");
+ break;
+ case qbs::Internal::PropertyDeclaration::Integer:
+ result.append("type=\"int\"");
+ break;
+ case qbs::Internal::PropertyDeclaration::Path:
+ result.append("type=\"string\"");
+ break;
+ case qbs::Internal::PropertyDeclaration::PathList:
+ result.append("type=\"string\"; isList=true");
+ break;
+ case qbs::Internal::PropertyDeclaration::String:
+ result.append("type=\"string\"");
+ break;
+ case qbs::Internal::PropertyDeclaration::StringList:
+ result.append("type=\"string\"; isList=true");
+ break;
+ case qbs::Internal::PropertyDeclaration::Variant:
+ result.append("type=\"QVariant\"");
+ break;
+ case qbs::Internal::PropertyDeclaration::Verbatim:
+ result.append("type=\"string\"");
+ break;
+ }
+ result.append(" }\n"); // Property
+ }
+
+ result.append(" }\n"); // Component
+ }
+
+ // Footer:
+ result.append("}\n"); // Module
+ return result;
+}
+
+bool BuiltinDeclarations::containsType(const QString &typeName) const
+{
+ return m_builtins.contains(typeName);
+}
+
+ItemDeclaration BuiltinDeclarations::declarationsForType(const QString &typeName) const
+{
+ return m_builtins.value(typeName);
+}
+
+void BuiltinDeclarations::setupItemForBuiltinType(Item *item) const
+{
+ foreach (const PropertyDeclaration &pd, declarationsForType(item->typeName()).properties()) {
+ item->m_propertyDeclarations.insert(pd.name, pd);
+ ValuePtr &value = item->m_properties[pd.name];
+ if (!value) {
+ JSSourceValuePtr sourceValue = JSSourceValue::create();
+ sourceValue->setFile(item->file());
+ sourceValue->setSourceCode(pd.initialValueSource.isEmpty() ?
+ "undefined" : pd.initialValueSource);
+ value = sourceValue;
+ }
+ }
+}
+
+void BuiltinDeclarations::insert(const ItemDeclaration &decl)
+{
+ m_builtins.insert(decl.typeName(), decl);
+}
+
+static PropertyDeclaration conditionProperty()
+{
+ PropertyDeclaration decl(QLatin1String("condition"), PropertyDeclaration::Boolean);
+ decl.initialValueSource = QLatin1String("true");
+ return decl;
+}
+
+static PropertyDeclaration nameProperty()
+{
+ return PropertyDeclaration(QLatin1String("name"), PropertyDeclaration::String);
+}
+
+static PropertyDeclaration prepareScriptProperty()
+{
+ PropertyDeclaration decl(QLatin1String("prepare"), PropertyDeclaration::Verbatim);
+ decl.functionArgumentNames
+ << QLatin1String("project") << QLatin1String("product")
+ << QLatin1String("inputs") << QLatin1String("outputs")
+ << QLatin1String("input") << QLatin1String("output");
+ return decl;
+}
+
+void BuiltinDeclarations::addArtifactItem()
+{
+ ItemDeclaration item(QLatin1String("Artifact"));
+ item << conditionProperty();
+ item << PropertyDeclaration(QLatin1String("fileName"), PropertyDeclaration::Verbatim);
+ item << PropertyDeclaration(QLatin1String("fileTags"), PropertyDeclaration::Variant);
+ PropertyDeclaration decl(QLatin1String("alwaysUpdated"), PropertyDeclaration::Boolean);
+ decl.initialValueSource = QLatin1String("true");
+ item << decl;
+ insert(item);
+}
+
+void BuiltinDeclarations::addDependsItem()
+{
+ ItemDeclaration item(QLatin1String("Depends"));
+ item << conditionProperty();
+ item << nameProperty();
+ item << PropertyDeclaration(QLatin1String("submodules"), PropertyDeclaration::Variant);
+ PropertyDeclaration requiredDecl(QLatin1String("required"), PropertyDeclaration::Boolean);
+ requiredDecl.initialValueSource = QLatin1String("true");
+ item << requiredDecl;
+ insert(item);
+}
+
+void BuiltinDeclarations::addExportItem()
+{
+ ItemDeclaration item(QLatin1String("Export"));
+ item.setAllowedChildTypes(ItemDeclaration::TypeNames()
+ << QLatin1String("Depends")
+ << QLatin1String("Module") // needed, because we're adding module instances internally
+ );
+ insert(item);
+}
+
+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);
+}
+
+void BuiltinDeclarations::addGroupItem()
+{
+ ItemDeclaration item(QLatin1String("Group"));
+ item << conditionProperty();
+ item << PropertyDeclaration(QLatin1String("name"), PropertyDeclaration::String,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(QLatin1String("files"), PropertyDeclaration::Variant,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(QLatin1String("fileTagsFilter"), PropertyDeclaration::Variant,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(QLatin1String("excludeFiles"), PropertyDeclaration::Variant,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(QLatin1String("fileTags"), PropertyDeclaration::Variant,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(QLatin1String("prefix"), PropertyDeclaration::Variant,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ PropertyDeclaration declaration;
+ declaration.name = QLatin1String("overrideTags");
+ declaration.type = PropertyDeclaration::Boolean;
+ declaration.flags = PropertyDeclaration::PropertyNotAvailableInConfig;
+ declaration.initialValueSource = QLatin1String("true");
+ item << declaration;
+ insert(item);
+}
+
+void BuiltinDeclarations::addModuleItem()
+{
+ ItemDeclaration item(QLatin1String("Module"));
+ item.setAllowedChildTypes(ItemDeclaration::TypeNames()
+ << QLatin1String("Module") // needed, because we're adding module instances internally
+ );
+ item << nameProperty();
+ item << conditionProperty();
+ item << PropertyDeclaration(QLatin1String("setupBuildEnvironment"),
+ PropertyDeclaration::Verbatim);
+ item << PropertyDeclaration(QLatin1String("setupRunEnvironment"),
+ PropertyDeclaration::Verbatim);
+ item << PropertyDeclaration(QLatin1String("validate"),
+ PropertyDeclaration::Variant,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(QLatin1String("additionalProductFileTags"),
+ PropertyDeclaration::Variant);
+ PropertyDeclaration presentDecl(QLatin1String("present"), PropertyDeclaration::Boolean);
+ presentDecl.initialValueSource = QLatin1String("true");
+ item << presentDecl;
+ insert(item);
+}
+
+void BuiltinDeclarations::addProbeItem()
+{
+ ItemDeclaration item(QLatin1String("Probe"));
+ item << conditionProperty();
+ PropertyDeclaration foundProperty(QLatin1String("found"), PropertyDeclaration::Boolean);
+ foundProperty.initialValueSource = QLatin1String("false");
+ item << foundProperty;
+ item << PropertyDeclaration(QLatin1String("configure"), PropertyDeclaration::Verbatim);
+ insert(item);
+}
+
+void BuiltinDeclarations::addProductItem()
+{
+ ItemDeclaration item(QLatin1String("Product"));
+ item.setAllowedChildTypes(ItemDeclaration::TypeNames()
+ << QLatin1String("Module")
+ << QLatin1String("Depends")
+ << QLatin1String("Transformer")
+ << QLatin1String("Group")
+ << QLatin1String("FileTagger")
+ << QLatin1String("Export")
+ << QLatin1String("Probe")
+ << QLatin1String("Rule"));
+ item << conditionProperty();
+ PropertyDeclaration decl(QLatin1String("type"), PropertyDeclaration::StringList);
+ decl.initialValueSource = QLatin1String("[]");
+ item << decl;
+ item << nameProperty();
+ decl = PropertyDeclaration("targetName", PropertyDeclaration::String);
+ decl.initialValueSource = QLatin1String("name");
+ item << decl;
+ decl = PropertyDeclaration(QLatin1String("destinationDirectory"), PropertyDeclaration::String);
+ decl.initialValueSource = QLatin1String("'.'");
+ item << decl;
+ item << PropertyDeclaration(QLatin1String("consoleApplication"),
+ PropertyDeclaration::Boolean);
+ item << PropertyDeclaration(QLatin1String("files"), PropertyDeclaration::Variant,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(QLatin1String("excludeFiles"), PropertyDeclaration::Variant,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(QLatin1String("qbsSearchPaths"),
+ PropertyDeclaration::StringList);
+ item << PropertyDeclaration(QLatin1String("version"), PropertyDeclaration::String);
+ insert(item);
+}
+
+void BuiltinDeclarations::addProjectItem()
+{
+ ItemDeclaration item(QLatin1String("Project"));
+ item.setAllowedChildTypes(ItemDeclaration::TypeNames()
+ << QLatin1String("Module")
+ << QLatin1String("Project")
+ << QLatin1String("SubProject")
+ << QLatin1String("Product")
+ << QLatin1String("FileTagger")
+ << QLatin1String("Rule"));
+ item << nameProperty();
+ item << conditionProperty();
+ item << PropertyDeclaration(QLatin1String("references"), PropertyDeclaration::Variant,
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(QLatin1String("qbsSearchPaths"),
+ PropertyDeclaration::StringList, PropertyDeclaration::PropertyNotAvailableInConfig);
+ insert(item);
+}
+
+void BuiltinDeclarations::addPropertiesItem()
+{
+ insert(ItemDeclaration(QLatin1String("Properties")));
+}
+
+void BuiltinDeclarations::addPropertyOptionsItem()
+{
+ ItemDeclaration item(QLatin1String("PropertyOptions"));
+ item << nameProperty();
+ item << PropertyDeclaration(QLatin1String("allowedValues"), PropertyDeclaration::Variant);
+ item << PropertyDeclaration(QLatin1String("description"), PropertyDeclaration::String);
+ insert(item);
+}
+
+void BuiltinDeclarations::addRuleItem()
+{
+ ItemDeclaration item(QLatin1String("Rule"));
+ item.setAllowedChildTypes(ItemDeclaration::TypeNames()
+ << QLatin1String("Artifact"));
+ item << conditionProperty();
+ PropertyDeclaration decl(QLatin1String("multiplex"), PropertyDeclaration::Boolean);
+ decl.initialValueSource = QLatin1String("false");
+ item << decl;
+ item << PropertyDeclaration(QLatin1String("inputs"), PropertyDeclaration::StringList);
+ item << PropertyDeclaration(QLatin1String("usings"), PropertyDeclaration::StringList);
+ item << PropertyDeclaration(QLatin1String("auxiliaryInputs"),
+ PropertyDeclaration::StringList);
+ item << PropertyDeclaration(QLatin1String("explicitlyDependsOn"),
+ PropertyDeclaration::StringList);
+ item << prepareScriptProperty();
+ insert(item);
+}
+
+void BuiltinDeclarations::addSubprojectItem()
+{
+ ItemDeclaration item(QLatin1String("SubProject"));
+ item.setAllowedChildTypes(ItemDeclaration::TypeNames()
+ << QLatin1String("Project") // needed, because we're adding this internally
+ << QLatin1String("Properties"));
+ item << PropertyDeclaration(QLatin1String("filePath"), PropertyDeclaration::Path);
+ PropertyDeclaration inheritProperty;
+ inheritProperty.name = QLatin1String("inheritProperties");
+ inheritProperty.type = PropertyDeclaration::Boolean;
+ inheritProperty.initialValueSource = QLatin1String("true");
+ item << inheritProperty;
+ insert(item);
+}
+
+void BuiltinDeclarations::addTransformerItem()
+{
+ ItemDeclaration item(QLatin1String("Transformer"));
+ item.setAllowedChildTypes(ItemDeclaration::TypeNames()
+ << QLatin1String("Artifact"));
+ item << conditionProperty();
+ item << PropertyDeclaration(QLatin1String("inputs"), PropertyDeclaration::Variant);
+ item << prepareScriptProperty();
+ item << PropertyDeclaration(QLatin1String("explicitlyDependsOn"),
+ PropertyDeclaration::StringList);
+ insert(item);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/builtindeclarations.h b/src/lib/corelib/language/builtindeclarations.h
new file mode 100644
index 000000000..b793c5aae
--- /dev/null
+++ b/src/lib/corelib/language/builtindeclarations.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_BUILTINDECLARATIONS_H
+#define QBS_BUILTINDECLARATIONS_H
+
+#include "itemdeclaration.h"
+
+#include <QByteArray>
+#include <QMap>
+
+namespace qbs {
+namespace Internal {
+
+class Item;
+
+class BuiltinDeclarations
+{
+public:
+ BuiltinDeclarations();
+
+ QString languageVersion() const;
+ QByteArray qmlTypeInfo() const;
+ bool containsType(const QString &typeName) const;
+ ItemDeclaration declarationsForType(const QString &typeName) const;
+ void setupItemForBuiltinType(qbs::Internal::Item *item) const;
+
+private:
+ void insert(const ItemDeclaration &decl);
+ void addArtifactItem();
+ void addDependsItem();
+ void addExportItem();
+ void addFileTaggerItem();
+ void addGroupItem();
+ void addModuleItem();
+ void addProbeItem();
+ void addProductItem();
+ void addProjectItem();
+ void addPropertiesItem();
+ void addPropertyOptionsItem();
+ void addRuleItem();
+ void addSubprojectItem();
+ void addTransformerItem();
+
+ QString m_languageVersion;
+ QMap<QString, ItemDeclaration> m_builtins;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILTINDECLARATIONS_H
diff --git a/src/lib/corelib/language/builtinvalue.cpp b/src/lib/corelib/language/builtinvalue.cpp
new file mode 100644
index 000000000..d7f98083d
--- /dev/null
+++ b/src/lib/corelib/language/builtinvalue.cpp
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** 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 "builtinvalue.h"
+
+namespace qbs {
+namespace Internal {
+
+BuiltinValue::BuiltinValue(Builtin builtin)
+ : Value(Value::BuiltinValueType)
+ , m_builtin(builtin)
+{
+}
+
+BuiltinValuePtr BuiltinValue::create(Builtin builtin)
+{
+ return BuiltinValuePtr(new BuiltinValue(builtin));
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/builtinvalue.h b/src/lib/corelib/language/builtinvalue.h
new file mode 100644
index 000000000..77c6fd14a
--- /dev/null
+++ b/src/lib/corelib/language/builtinvalue.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_BUILTINVALUE_H
+#define QBS_BUILTINVALUE_H
+
+#include "value.h"
+
+namespace qbs {
+namespace Internal {
+
+class BuiltinValue : public Value
+{
+public:
+ enum Builtin
+ {
+ GetNativeSettingFunction,
+ GetEnvFunction,
+ GetHostOSFunction,
+ CanonicalArchitectureFunction
+ };
+
+ static BuiltinValuePtr create(Builtin builtin);
+
+ void apply(ValueHandler *handler) { handler->handle(this); }
+
+ Builtin builtin() const { return m_builtin; }
+ void setBuiltin(const Builtin &builtin) { m_builtin = builtin; }
+
+private:
+ BuiltinValue(Builtin builtin);
+
+ Builtin m_builtin;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_BUILTINVALUE_H
diff --git a/src/lib/corelib/language/evaluationdata.h b/src/lib/corelib/language/evaluationdata.h
new file mode 100644
index 000000000..930ac1f0c
--- /dev/null
+++ b/src/lib/corelib/language/evaluationdata.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_EVALUATIONDATA_H
+#define QBS_EVALUATIONDATA_H
+
+#include <QHash>
+#include <QScriptEngine>
+#include <QScriptValue>
+#include <QVariant>
+
+namespace qbs {
+namespace Internal {
+
+class Evaluator;
+class Item;
+
+class EvaluationData
+{
+public:
+ Evaluator *evaluator;
+ const Item *item;
+ mutable QHash<QScriptString, QScriptValue> valueCache;
+
+ void attachTo(QScriptValue &);
+ static EvaluationData *get(const QScriptValue &);
+};
+
+inline void EvaluationData::attachTo(QScriptValue &scriptValue)
+{
+ QVariant v;
+ v.setValue<quintptr>(reinterpret_cast<quintptr>(this));
+ scriptValue.setData(scriptValue.engine()->newVariant(v));
+}
+
+inline EvaluationData *EvaluationData::get(const QScriptValue &scriptValue)
+{
+ const quintptr ptr = scriptValue.data().toVariant().value<quintptr>();
+ return reinterpret_cast<EvaluationData *>(ptr);
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_EVALUATIONDATA_H
diff --git a/src/lib/corelib/language/evaluator.cpp b/src/lib/corelib/language/evaluator.cpp
new file mode 100644
index 000000000..29b3837fd
--- /dev/null
+++ b/src/lib/corelib/language/evaluator.cpp
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** 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 "evaluator.h"
+#include "evaluationdata.h"
+#include "evaluatorscriptclass.h"
+#include "filecontext.h"
+#include "filetags.h"
+#include "item.h"
+#include <jsextensions/jsextensions.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+#include <QDebug>
+#include <QScriptEngine>
+
+namespace qbs {
+namespace Internal {
+
+
+Evaluator::Evaluator(ScriptEngine *scriptEngine, const Logger &logger)
+ : m_scriptEngine(scriptEngine)
+ , m_scriptClass(new EvaluatorScriptClass(scriptEngine, logger))
+{
+}
+
+Evaluator::~Evaluator()
+{
+ QHash<const Item *, QScriptValue>::iterator it = m_scriptValueMap.begin();
+ for (; it != m_scriptValueMap.end(); ++it) {
+ EvaluationData *data = EvaluationData::get(*it);
+ if (data) {
+ if (data->item)
+ data->item->setPropertyObserver(0);
+ delete data;
+ }
+ }
+ delete m_scriptClass;
+}
+
+QScriptValue Evaluator::property(const Item *item, const QString &name)
+{
+ return scriptValue(item).property(name);
+}
+
+QScriptValue Evaluator::property(const Item *item, const QStringList &nameParts)
+{
+ const Item *targetItem = item;
+ const int c = nameParts.count() - 1;
+ for (int i = 0; i < c; ++i) {
+ ValuePtr v = targetItem->properties().value(nameParts.at(i));
+ if (!v)
+ return QScriptValue();
+ QBS_ASSERT(v->type() == Value::ItemValueType, return QScriptValue());
+ targetItem = v.staticCast<ItemValue>()->item();
+ QBS_ASSERT(targetItem, return QScriptValue());
+ }
+ return property(targetItem, nameParts.last());
+}
+
+bool Evaluator::boolValue(const Item *item, const QString &name, bool defaultValue,
+ bool *propertyWasSet)
+{
+ QScriptValue v = property(item, name);
+ handleEvaluationError(item, name, v);
+ if (!v.isValid() || v.isUndefined()) {
+ if (propertyWasSet)
+ *propertyWasSet = false;
+ return defaultValue;
+ }
+ if (propertyWasSet)
+ *propertyWasSet = true;
+ return v.toBool();
+}
+
+FileTags Evaluator::fileTagsValue(const Item *item, const QString &name)
+{
+ return FileTags::fromStringList(stringListValue(item, name));
+}
+
+QString Evaluator::stringValue(const Item *item, const QString &name,
+ const QString &defaultValue, bool *propertyWasSet)
+{
+ QScriptValue v = property(item, name);
+ handleEvaluationError(item, name, v);
+ if (!v.isValid() || v.isUndefined()) {
+ if (propertyWasSet)
+ *propertyWasSet = false;
+ return defaultValue;
+ }
+ if (propertyWasSet)
+ *propertyWasSet = true;
+ return v.toString();
+}
+
+static QStringList toStringList(const QScriptValue &scriptValue,
+ const Item *item, const QString &propertyName)
+{
+ if (scriptValue.isString()) {
+ return QStringList(scriptValue.toString());
+ } else if (scriptValue.isArray()) {
+ QStringList lst;
+ int i = 0;
+ forever {
+ QScriptValue elem = scriptValue.property(i++);
+ if (!elem.isValid())
+ break;
+ if (elem.isArray() || elem.isObject()) {
+ // Let's assume all other JS types are convertible to string.
+ throw ErrorInfo(Tr::tr("Expected array element of type String at index %1.")
+ .arg(i - 1),
+ item->property(propertyName)->location());
+ }
+ lst.append(elem.toString());
+ }
+ return lst;
+ }
+ return QStringList();
+}
+
+QStringList Evaluator::stringListValue(const Item *item, const QString &name, bool *propertyWasSet)
+{
+ QScriptValue v = property(item, name);
+ if (propertyWasSet)
+ *propertyWasSet = v.isValid() && !v.isUndefined();
+ handleEvaluationError(item, name, v);
+ return toStringList(v, item, name);
+}
+
+QScriptValue Evaluator::scriptValue(const Item *item)
+{
+ QScriptValue &scriptValue = m_scriptValueMap[item];
+ if (scriptValue.isObject()) {
+ // already initialized
+ return scriptValue;
+ }
+
+ EvaluationData *edata = new EvaluationData;
+ edata->evaluator = this;
+ edata->item = item;
+ edata->item->setPropertyObserver(this);
+
+ scriptValue = m_scriptEngine->newObject(m_scriptClass);
+ edata->attachTo(scriptValue);
+ return scriptValue;
+}
+
+void Evaluator::onItemPropertyChanged(Item *item)
+{
+ EvaluationData *data = EvaluationData::get(m_scriptValueMap.value(item));
+ if (data)
+ data->valueCache.clear();
+}
+
+void Evaluator::onItemDestroyed(Item *item)
+{
+ delete EvaluationData::get(m_scriptValueMap.value(item));
+ m_scriptValueMap.remove(item);
+}
+
+void Evaluator::handleEvaluationError(const Item *item, const QString &name,
+ const QScriptValue &scriptValue)
+{
+ if (Q_LIKELY(!m_scriptEngine->hasErrorOrException(scriptValue)))
+ return;
+ const ValueConstPtr value = item->property(name);
+ CodeLocation location = value ? value->location() : CodeLocation();
+ if (m_scriptEngine->hasUncaughtException()) {
+ throw ErrorInfo(m_scriptEngine->uncaughtException().toString(),
+ CodeLocation(location.fileName(), m_scriptEngine->uncaughtExceptionLineNumber()));
+ }
+ throw ErrorInfo(scriptValue.toString(), location);
+}
+
+QScriptValue Evaluator::fileScope(const FileContextConstPtr &file)
+{
+ QScriptValue &result = m_fileScopeMap[file];
+ if (result.isObject()) {
+ // already initialized
+ return result;
+ }
+
+ if (file->idScope())
+ result = scriptValue(file->idScope());
+ else
+ result = m_scriptEngine->newObject();
+ result.setProperty(QLatin1String("filePath"), file->filePath());
+ result.setProperty(QLatin1String("path"), file->dirPath());
+ m_scriptEngine->import(file->jsImports(), result, result);
+ JsExtensions::setupExtensions(file->jsExtensions(), result);
+ return result;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/evaluator.h b/src/lib/corelib/language/evaluator.h
new file mode 100644
index 000000000..a17605af1
--- /dev/null
+++ b/src/lib/corelib/language/evaluator.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_EVALUATOR_H
+#define QBS_EVALUATOR_H
+
+#include "forward_decls.h"
+#include "itemobserver.h"
+#include <language/scriptengine.h>
+
+#include <QHash>
+#include <QScriptValue>
+
+namespace qbs {
+namespace Internal {
+class FileTags;
+
+class EvaluatorScriptClass;
+
+class Evaluator : private ItemObserver
+{
+ friend class SVConverter;
+
+public:
+ Evaluator(ScriptEngine *scriptEngine, const Logger &logger);
+ virtual ~Evaluator();
+
+ ScriptEngine *engine() const;
+ QScriptValue property(const Item *item, const QString &name);
+ QScriptValue property(const Item *item, const QStringList &nameParts);
+
+ bool boolValue(const Item *item, const QString &name, bool defaultValue = false,
+ bool *propertyWasSet = 0);
+ FileTags fileTagsValue(const Item *item, const QString &name);
+ QString stringValue(const Item *item, const QString &name,
+ const QString &defaultValue = QString(), bool *propertyWasSet = 0);
+ QStringList stringListValue(const Item *item, const QString &name, bool *propertyWasSet = 0);
+
+ QScriptValue scriptValue(const Item *item);
+ QScriptValue fileScope(const FileContextConstPtr &file);
+
+private:
+ void onItemPropertyChanged(Item *item);
+ void onItemDestroyed(Item *item);
+ void handleEvaluationError(const Item *item, const QString &name,
+ const QScriptValue &scriptValue);
+
+ ScriptEngine *m_scriptEngine;
+ EvaluatorScriptClass *m_scriptClass;
+ mutable QHash<const Item *, QScriptValue> m_scriptValueMap;
+ mutable QHash<FileContextConstPtr, QScriptValue> m_fileScopeMap;
+};
+
+inline ScriptEngine *Evaluator::engine() const
+{
+ return m_scriptEngine;
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_EVALUATOR_H
diff --git a/src/lib/corelib/language/evaluatorscriptclass.cpp b/src/lib/corelib/language/evaluatorscriptclass.cpp
new file mode 100644
index 000000000..fb89f53e6
--- /dev/null
+++ b/src/lib/corelib/language/evaluatorscriptclass.cpp
@@ -0,0 +1,577 @@
+/****************************************************************************
+**
+** 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 "evaluatorscriptclass.h"
+
+#include "builtinvalue.h"
+#include "evaluationdata.h"
+#include "evaluator.h"
+#include "filecontext.h"
+#include "item.h"
+#include "scriptengine.h"
+#include "propertydeclaration.h"
+#include <tools/hostosinfo.h>
+#include <tools/qbsassert.h>
+#include <tools/scripttools.h>
+
+#include <QScriptString>
+#include <QScriptValue>
+#include <QDebug>
+#include <QSettings>
+
+namespace qbs {
+namespace Internal {
+
+class SVConverter : ValueHandler
+{
+ EvaluatorScriptClass *const scriptClass;
+ ScriptEngine *const engine;
+ QScriptContext *const scriptContext;
+ const QScriptValue *object;
+ const ValuePtr &valuePtr;
+ const bool inPrototype;
+ char pushedScopesCount;
+
+public:
+ const QScriptString *propertyName;
+ const EvaluationData *data;
+ QScriptValue *result;
+
+ SVConverter(EvaluatorScriptClass *esc, const QScriptValue *obj, const ValuePtr &v,
+ bool _inPrototype)
+ : scriptClass(esc)
+ , engine(static_cast<ScriptEngine *>(esc->engine()))
+ , scriptContext(esc->engine()->currentContext())
+ , object(obj)
+ , valuePtr(v)
+ , inPrototype(_inPrototype)
+ , pushedScopesCount(0)
+ {
+ }
+
+ void start()
+ {
+ valuePtr->apply(this);
+ }
+
+private:
+ void setupConvenienceProperty(const QString &conveniencePropertyName, QScriptValue *extraScope,
+ const QScriptValue &scriptValue)
+ {
+ if (!extraScope->isObject())
+ *extraScope = scriptClass->engine()->newObject();
+ if (!scriptValue.isValid() || scriptValue.isUndefined()) {
+ // If there's no such value, use an empty array to have the convenience property
+ // still available.
+ extraScope->setProperty(conveniencePropertyName, engine->newArray());
+ } else {
+ extraScope->setProperty(conveniencePropertyName, scriptValue);
+ }
+ }
+
+ void pushScope(const QScriptValue &scope)
+ {
+ if (scope.isObject()) {
+ scriptContext->pushScope(scope);
+ ++pushedScopesCount;
+ }
+ }
+
+ void pushItemScopes(const Item *item)
+ {
+ const Item *scope = item->scope();
+ if (scope) {
+ pushItemScopes(scope);
+ pushScope(data->evaluator->scriptValue(scope));
+ }
+ }
+
+ void popScopes()
+ {
+ for (; pushedScopesCount; --pushedScopesCount)
+ scriptContext->popScope();
+ }
+
+ void handle(JSSourceValue *value)
+ {
+ const Item *conditionScopeItem = 0;
+ QScriptValue conditionScope;
+ QScriptValue conditionFileScope;
+ Item *outerItem = data->item->outerItem();
+ for (int i = 0; i < value->alternatives().count(); ++i) {
+ const JSSourceValue::Alternative *alternative = 0;
+ alternative = &value->alternatives().at(i);
+ if (conditionScopeItem != alternative->conditionScopeItem) {
+ conditionScopeItem = alternative->conditionScopeItem;
+ conditionScope = data->evaluator->scriptValue(alternative->conditionScopeItem);
+ QBS_ASSERT(conditionScope.isObject(), return);
+ conditionFileScope = data->evaluator->fileScope(conditionScopeItem->file());
+ }
+ engine->currentContext()->pushScope(conditionFileScope);
+ pushItemScopes(conditionScopeItem);
+ engine->currentContext()->pushScope(conditionScope);
+ const QScriptValue cr = engine->evaluate(alternative->condition);
+ engine->currentContext()->popScope();
+ engine->currentContext()->popScope();
+ popScopes();
+ if (engine->hasErrorOrException(cr)) {
+ *result = cr;
+ return;
+ }
+ if (cr.toBool()) {
+ // condition is true, let's use the value of this alternative
+ if (alternative->value->sourceUsesOuter()) {
+ // Clone value but without alternatives.
+ JSSourceValuePtr outerValue = JSSourceValue::create();
+ outerValue->setFile(value->file());
+ outerValue->setSourceCode(value->sourceCode());
+ outerValue->setBaseValue(value->baseValue());
+ outerValue->setLocation(value->location());
+ outerItem = Item::create(data->item->pool());
+ outerItem->setProperty(propertyName->toString(), outerValue);
+ }
+ value = alternative->value.data();
+ break;
+ }
+ }
+
+ QScriptValue extraScope;
+ if (value->sourceUsesBase()) {
+ QScriptValue baseValue;
+ if (value->baseValue()) {
+ SVConverter converter(scriptClass, object, value->baseValue(), inPrototype);
+ converter.propertyName = propertyName;
+ converter.data = data;
+ converter.result = &baseValue;
+ converter.start();
+ }
+ setupConvenienceProperty(QLatin1String("base"), &extraScope, baseValue);
+ }
+ if (value->sourceUsesOuter() && outerItem)
+ setupConvenienceProperty(QLatin1String("outer"), &extraScope,
+ data->evaluator->property(outerItem, *propertyName));
+
+ pushScope(data->evaluator->fileScope(value->file()));
+ pushItemScopes(data->item);
+ if (inPrototype || !data->item->isModuleInstance()) {
+ // Own properties of module instances must not have the instance itself in the scope.
+ pushScope(*object);
+ }
+ pushScope(extraScope);
+ const CodeLocation valueLocation = value->location();
+ *result = engine->evaluate(value->sourceCode(), valueLocation.fileName(),
+ valueLocation.line());
+ popScopes();
+ }
+
+ void handle(ItemValue *value)
+ {
+ Item *item = value->item();
+ if (!item)
+ qDebug() << "SVConverter got null item" << propertyName->toString();
+ *result = data->evaluator->scriptValue(item);
+ if (!result->isValid())
+ qDebug() << "SVConverter returned invalid script value.";
+ }
+
+ void handle(VariantValue *variantValue)
+ {
+ *result = scriptClass->engine()->toScriptValue(variantValue->value());
+ }
+
+ void handle(BuiltinValue *builtinValue)
+ {
+ *result = scriptClass->scriptValueForBuiltin(builtinValue->builtin());
+ }
+};
+
+bool debugProperties = false;
+
+enum QueryPropertyType
+{
+ QPTDefault,
+ QPTParentProperty
+};
+
+EvaluatorScriptClass::EvaluatorScriptClass(ScriptEngine *scriptEngine, const Logger &logger)
+ : QScriptClass(scriptEngine)
+ , m_logger(logger)
+{
+ m_getNativeSettingBuiltin = scriptEngine->newFunction(js_getNativeSetting, 3);
+ m_getEnvBuiltin = scriptEngine->newFunction(js_getEnv, 1);
+ m_getHostOSBuiltin = scriptEngine->newFunction(js_getHostOS, 1);
+ m_canonicalArchitectureBuiltin = scriptEngine->newFunction(js_canonicalArchitecture, 1);
+}
+
+QScriptClass::QueryFlags EvaluatorScriptClass::queryProperty(const QScriptValue &object,
+ const QScriptString &name,
+ QScriptClass::QueryFlags flags,
+ uint *id)
+{
+ Q_UNUSED(flags);
+
+ // We assume that it's safe to save the result of the query in a member of the scriptclass.
+ // It must be cleared in the property method before doing any further lookup.
+ QBS_ASSERT(m_queryResult.isNull(), return QueryFlags());
+
+ if (debugProperties)
+ m_logger.qbsTrace() << "[SC] queryProperty " << object.objectId() << " " << name;
+
+ EvaluationData *const data = EvaluationData::get(object);
+ const QString nameString = name.toString();
+ if (nameString == QLatin1String("parent")) {
+ *id = QPTParentProperty;
+ m_queryResult.data = data;
+ return QScriptClass::HandlesReadAccess;
+ }
+
+ *id = QPTDefault;
+ if (!data) {
+ if (debugProperties)
+ m_logger.qbsTrace() << "[SC] queryProperty: no data attached";
+ return QScriptClass::QueryFlags();
+ }
+
+ return queryItemProperty(data, nameString);
+}
+
+QScriptClass::QueryFlags EvaluatorScriptClass::queryItemProperty(const EvaluationData *data,
+ const QString &name,
+ bool ignoreParent)
+{
+ for (const Item *item = data->item; item; item = item->prototype()) {
+ m_queryResult.value = item->properties().value(name);
+ if (!m_queryResult.value.isNull()) {
+ m_queryResult.data = data;
+ if (data->item != item)
+ m_queryResult.inPrototype = true;
+ return HandlesReadAccess;
+ }
+ }
+
+ if (!ignoreParent && data->item->parent()) {
+ if (debugProperties)
+ m_logger.qbsTrace() << "[SC] queryProperty: query parent";
+ EvaluationData parentdata = *data;
+ parentdata.item = data->item->parent();
+ const QueryFlags qf = queryItemProperty(&parentdata, name, true);
+ if (qf.testFlag(HandlesReadAccess)) {
+ m_queryResult.data = data;
+ return qf;
+ }
+ }
+
+ if (debugProperties)
+ m_logger.qbsTrace() << "[SC] queryProperty: no such property";
+ return QScriptClass::QueryFlags();
+}
+
+QString EvaluatorScriptClass::resultToString(const QScriptValue &scriptValue)
+{
+ return (scriptValue.isObject()
+ ? QLatin1String("[Object: ")
+ + QString::number(scriptValue.objectId(), 16) + QLatin1Char(']')
+ : scriptValue.toVariant().toString());
+}
+
+Item *EvaluatorScriptClass::findParentOfType(const Item *item, const QString &typeName)
+{
+ for (Item *it = item->parent(); it; it = it->parent()) {
+ if (it->typeName() == typeName)
+ return it;
+ }
+ return 0;
+}
+
+inline void convertToPropertyType(const PropertyDeclaration::Type t, QScriptValue &v)
+{
+ if (v.isUndefined())
+ return;
+ switch (t) {
+ case PropertyDeclaration::UnknownType:
+ case PropertyDeclaration::Variant:
+ case PropertyDeclaration::Verbatim:
+ break;
+ case PropertyDeclaration::Boolean:
+ if (!v.isBool())
+ v = v.toBool();
+ break;
+ case PropertyDeclaration::Integer:
+ if (!v.isNumber())
+ v = v.toNumber();
+ break;
+ case PropertyDeclaration::Path:
+ case PropertyDeclaration::String:
+ if (!v.isString())
+ v = v.toString();
+ break;
+ case PropertyDeclaration::PathList:
+ case PropertyDeclaration::StringList:
+ if (!v.isArray()) {
+ QScriptValue x = v.engine()->newArray(1);
+ x.setProperty(0, v.isString() ? v : v.toString());
+ v = x;
+ }
+ break;
+ }
+}
+
+QScriptValue EvaluatorScriptClass::property(const QScriptValue &object, const QScriptString &name,
+ uint id)
+{
+ const EvaluationData *data = m_queryResult.data;
+ const bool inPrototype = m_queryResult.inPrototype;
+ m_queryResult.data = 0;
+ m_queryResult.inPrototype = false;
+ QBS_ASSERT(data, return QScriptValue());
+
+ const QueryPropertyType qpt = static_cast<QueryPropertyType>(id);
+ if (qpt == QPTParentProperty) {
+ return data->item->parent()
+ ? data->evaluator->scriptValue(data->item->parent())
+ : engine()->undefinedValue();
+ }
+
+ ValuePtr value;
+ m_queryResult.value.swap(value);
+ QBS_ASSERT(value, return QScriptValue());
+ QBS_ASSERT(m_queryResult.isNull(), return QScriptValue());
+
+ if (debugProperties)
+ m_logger.qbsTrace() << "[SC] property " << name;
+
+ QScriptValue result = data->valueCache.value(name);
+ if (result.isValid()) {
+ if (debugProperties)
+ m_logger.qbsTrace() << "[SC] cache hit " << name << ": " << resultToString(result);
+ return result;
+ }
+
+ SVConverter converter(this, &object, value, inPrototype);
+ converter.propertyName = &name;
+ converter.data = data;
+ converter.result = &result;
+ converter.start();
+
+ const PropertyDeclaration decl = data->item->propertyDeclarations().value(name.toString());
+ convertToPropertyType(decl.type, result);
+
+ if (debugProperties)
+ m_logger.qbsTrace() << "[SC] cache miss " << name << ": " << resultToString(result);
+ data->valueCache.insert(name, result);
+ return result;
+}
+
+QScriptValue EvaluatorScriptClass::scriptValueForBuiltin(BuiltinValue::Builtin builtin) const
+{
+ switch (builtin) {
+ case BuiltinValue::GetNativeSettingFunction:
+ return m_getNativeSettingBuiltin;
+ case BuiltinValue::GetEnvFunction:
+ return m_getEnvBuiltin;
+ case BuiltinValue::GetHostOSFunction:
+ return m_getHostOSBuiltin;
+ case BuiltinValue::CanonicalArchitectureFunction:
+ return m_canonicalArchitectureBuiltin;
+ }
+ QBS_ASSERT(!"unhandled builtin", ;);
+ return QScriptValue();
+}
+
+QScriptValue EvaluatorScriptClass::js_getNativeSetting(QScriptContext *context, QScriptEngine *engine)
+{
+ if (Q_UNLIKELY(context->argumentCount() < 1 || context->argumentCount() > 3)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ QLatin1String("getNativeSetting expects between 1 and 3 arguments"));
+ }
+
+ QString key = context->argumentCount() > 1 ? context->argument(1).toString() : QString();
+
+ // We'll let empty string represent the default registry value
+ if (HostOsInfo::isWindowsHost() && key.isEmpty())
+ key = QLatin1String(".");
+
+ QVariant defaultValue = context->argumentCount() > 2 ? context->argument(2).toVariant() : QVariant();
+
+ QSettings settings(context->argument(0).toString(), QSettings::NativeFormat);
+ QVariant value = settings.value(key, defaultValue);
+ return value.isNull() ? engine->undefinedValue() : engine->toScriptValue(value);
+}
+
+QScriptValue EvaluatorScriptClass::js_getEnv(QScriptContext *context, QScriptEngine *engine)
+{
+ if (Q_UNLIKELY(context->argumentCount() < 1)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ QLatin1String("getEnv expects 1 argument"));
+ }
+ const QString name = context->argument(0).toString();
+ ScriptEngine * const e = static_cast<ScriptEngine *>(engine);
+ const QString value = e->environment().value(name);
+ e->addEnvironmentVariable(name, value);
+ return value.isNull() ? engine->undefinedValue() : value;
+}
+
+QScriptValue EvaluatorScriptClass::js_getHostOS(QScriptContext *context, QScriptEngine *engine)
+{
+ Q_UNUSED(context);
+ QStringList hostSystem;
+
+#if defined(Q_OS_AIX)
+ hostSystem << "aix";
+#endif
+#if defined(Q_OS_ANDROID)
+ hostSystem << "android";
+#endif
+#if defined(Q_OS_BLACKBERRY)
+ hostSystem << "blackberry";
+#endif
+#if defined(Q_OS_BSD4)
+ hostSystem << "bsd" << "bsd4";
+#endif
+#if defined(Q_OS_BSDI)
+ hostSystem << "bsdi";
+#endif
+#if defined(Q_OS_CYGWIN)
+ hostSystem << "cygwin";
+#endif
+#if defined(Q_OS_DARWIN)
+ hostSystem << "darwin";
+#endif
+#if defined(Q_OS_DGUX)
+ hostSystem << "dgux";
+#endif
+#if defined(Q_OS_DYNIX)
+ hostSystem << "dynix";
+#endif
+#if defined(Q_OS_FREEBSD)
+ hostSystem << "freebsd";
+#endif
+#if defined(Q_OS_HPUX)
+ hostSystem << "hpux";
+#endif
+#if defined(Q_OS_HURD)
+ hostSystem << "hurd";
+#endif
+#if defined(Q_OS_INTEGRITY)
+ hostSystem << "integrity";
+#endif
+#if defined(Q_OS_IOS)
+ hostSystem << "ios";
+#endif
+#if defined(Q_OS_IRIX)
+ hostSystem << "irix";
+#endif
+#if defined(Q_OS_LINUX)
+ hostSystem << "linux";
+#endif
+#if defined(Q_OS_LYNX)
+ hostSystem << "lynx";
+#endif
+#if defined(Q_OS_MACX)
+ hostSystem << "osx";
+#endif
+#if defined(Q_OS_MSDOS)
+ hostSystem << "msdos";
+#endif
+#if defined(Q_OS_NACL)
+ hostSystem << "nacl";
+#endif
+#if defined(Q_OS_NETBSD)
+ hostSystem << "netbsd";
+#endif
+#if defined(Q_OS_OPENBSD)
+ hostSystem << "openbsd";
+#endif
+#if defined(Q_OS_OS2)
+ hostSystem << "os2";
+#endif
+#if defined(Q_OS_OS2EMX)
+ hostSystem << "os2emx";
+#endif
+#if defined(Q_OS_OSF)
+ hostSystem << "osf";
+#endif
+#if defined(Q_OS_QNX)
+ hostSystem << "qnx";
+#endif
+#if defined(Q_OS_ONX6)
+ hostSystem << "qnx6";
+#endif
+#if defined(Q_OS_RELIANT)
+ hostSystem << "reliant";
+#endif
+#if defined(Q_OS_SCO)
+ hostSystem << "sco";
+#endif
+#if defined(Q_OS_SOLARIS)
+ hostSystem << "solaris";
+#endif
+#if defined(Q_OS_SYMBIAN)
+ hostSystem << "symbian";
+#endif
+#if defined(Q_OS_ULTRIX)
+ hostSystem << "ultrix";
+#endif
+#if defined(Q_OS_UNIX)
+ hostSystem << "unix";
+#endif
+#if defined(Q_OS_UNIXWARE)
+ hostSystem << "unixware";
+#endif
+#if defined(Q_OS_VXWORKS)
+ hostSystem << "vxworks";
+#endif
+#if defined(Q_OS_WIN32)
+ hostSystem << "windows";
+#endif
+#if defined(Q_OS_WINCE)
+ hostSystem << "windowsce";
+#endif
+#if defined(Q_OS_WINPHONE)
+ hostSystem << "windowsphone";
+#endif
+#if defined(Q_OS_WINRT)
+ hostSystem << "winrt";
+#endif
+
+ return engine->toScriptValue(hostSystem);
+}
+
+QScriptValue EvaluatorScriptClass::js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine)
+{
+ if (Q_UNLIKELY(context->argumentCount() < 1)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ QLatin1String("canonicalArchitecture expects 1 argument"));
+ }
+ const QString architecture = context->argument(0).toString();
+ return engine->toScriptValue(HostOsInfo::canonicalArchitecture(architecture));
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/evaluatorscriptclass.h b/src/lib/corelib/language/evaluatorscriptclass.h
new file mode 100644
index 000000000..4803a21b9
--- /dev/null
+++ b/src/lib/corelib/language/evaluatorscriptclass.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_EVALUATORSCRIPTCLASS_H
+#define QBS_EVALUATORSCRIPTCLASS_H
+
+#include "value.h"
+#include "builtinvalue.h"
+#include <logging/logger.h>
+
+#include <QScriptClass>
+
+QT_BEGIN_NAMESPACE
+class QScriptContext;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+class EvaluationData;
+class ScriptEngine;
+
+class EvaluatorScriptClass : public QScriptClass
+{
+public:
+ EvaluatorScriptClass(ScriptEngine *scriptEngine, const Logger &logger);
+
+ QueryFlags queryProperty(const QScriptValue &object,
+ const QScriptString &name,
+ QueryFlags flags, uint *id);
+ QScriptValue property(const QScriptValue &object,
+ const QScriptString &name, uint id);
+
+ QScriptValue scriptValueForBuiltin(BuiltinValue::Builtin builtin) const;
+
+private:
+ QueryFlags queryItemProperty(const EvaluationData *data,
+ const QString &name,
+ bool ignoreParent = false);
+ static QString resultToString(const QScriptValue &scriptValue);
+ static Item *findParentOfType(const Item *item, const QString &typeName);
+ static QScriptValue js_getNativeSetting(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_getEnv(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_getHostOS(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine);
+
+ struct QueryResult
+ {
+ QueryResult()
+ : data(0), inPrototype(false)
+ {}
+
+ bool isNull() const
+ {
+ return !data;
+ }
+
+ const EvaluationData *data;
+ bool inPrototype;
+ ValuePtr value;
+ };
+ QueryResult m_queryResult;
+ Logger m_logger;
+ QScriptValue m_getNativeSettingBuiltin;
+ QScriptValue m_getEnvBuiltin;
+ QScriptValue m_getHostOSBuiltin;
+ QScriptValue m_canonicalArchitectureBuiltin;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_EVALUATORSCRIPTCLASS_H
diff --git a/src/lib/corelib/language/filecontext.cpp b/src/lib/corelib/language/filecontext.cpp
new file mode 100644
index 000000000..570b40c66
--- /dev/null
+++ b/src/lib/corelib/language/filecontext.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** 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 "filecontext.h"
+#include <tools/fileinfo.h>
+
+namespace qbs {
+namespace Internal {
+
+FileContext::FileContext()
+ : m_idScope(0)
+{
+}
+
+FileContextPtr FileContext::create()
+{
+ return FileContextPtr(new FileContext);
+}
+
+QString FileContext::dirPath() const
+{
+ return FileInfo::path(m_filePath);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/filecontext.h b/src/lib/corelib/language/filecontext.h
new file mode 100644
index 000000000..75f93f04c
--- /dev/null
+++ b/src/lib/corelib/language/filecontext.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_FILECONTEXT_H
+#define QBS_FILECONTEXT_H
+
+#include "item.h"
+#include "jsimports.h"
+
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+
+class FileContext
+{
+ friend class ItemReaderASTVisitor;
+
+ FileContext();
+
+public:
+ static FileContextPtr create();
+
+ QString filePath() const;
+ QString dirPath() const;
+ JsImports jsImports() const;
+ QStringList jsExtensions() const;
+
+ Item *idScope() const;
+
+private:
+ QString m_filePath;
+ JsImports m_jsImports;
+ QStringList m_jsExtensions;
+ Item *m_idScope;
+};
+
+inline QString FileContext::filePath() const
+{
+ return m_filePath;
+}
+
+inline JsImports FileContext::jsImports() const
+{
+ return m_jsImports;
+}
+
+inline QStringList FileContext::jsExtensions() const
+{
+ return m_jsExtensions;
+}
+
+inline Item *FileContext::idScope() const
+{
+ return m_idScope;
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_FILECONTEXT_H
diff --git a/src/lib/corelib/language/filetags.cpp b/src/lib/corelib/language/filetags.cpp
new file mode 100644
index 000000000..a5af1f0b2
--- /dev/null
+++ b/src/lib/corelib/language/filetags.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** 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 "filetags.h"
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+
+void FileTag::clear()
+{
+ Id::operator=(Id());
+}
+
+QStringList FileTags::toStringList() const
+{
+ QStringList strlst;
+ foreach (const FileTag &tag, *this)
+ strlst += tag.toString();
+ return strlst;
+}
+
+FileTags FileTags::fromStringList(const QStringList &strings)
+{
+ FileTags result;
+ foreach (const QString &str, strings)
+ result += FileTag(str.toLocal8Bit());
+ return result;
+}
+
+/*!
+ * \return \c{true} if this file tags set has file tags in common with \c{other}.
+ */
+bool FileTags::matches(const FileTags &other) const
+{
+ for (FileTags::const_iterator it = other.begin(); it != other.end(); ++it)
+ if (contains(*it))
+ return true;
+ return false;
+}
+
+LogWriter operator <<(LogWriter w, const FileTags &tags)
+{
+ bool firstLoop = true;
+ w.write('(');
+ foreach (const FileTag &tag, tags) {
+ if (firstLoop)
+ firstLoop = false;
+ else
+ w.write(QLatin1String(", "));
+ w.write(tag.toString());
+ }
+ w.write(')');
+ return w;
+}
+
+QDataStream &operator >>(QDataStream &s, FileTags &tags)
+{
+ int i;
+ s >> i;
+ tags.clear();
+ tags.reserve(i);
+ QVariant v;
+ while (--i >= 0) {
+ s >> v;
+ tags += FileTag::fromSetting(v);
+ }
+ return s;
+}
+
+QDataStream &operator <<(QDataStream &s, const FileTags &tags)
+{
+ s << tags.count();
+ foreach (const FileTag &ft, tags)
+ s << ft.toSetting();
+ return s;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/filetags.h b/src/lib/corelib/language/filetags.h
new file mode 100644
index 000000000..44effc509
--- /dev/null
+++ b/src/lib/corelib/language/filetags.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_FILETAGS_H
+#define QBS_FILETAGS_H
+
+#include <logging/logger.h>
+#include <tools/id.h>
+#include <QDataStream>
+#include <QSet>
+
+namespace qbs {
+namespace Internal {
+
+class FileTag : public Id
+{
+public:
+ FileTag()
+ : Id()
+ {}
+
+ FileTag(const Id &other)
+ : Id(other)
+ {}
+
+ FileTag(const char *str)
+ : Id(str)
+ {}
+
+ explicit FileTag(const QByteArray &ba)
+ : Id(ba)
+ {}
+
+ void clear();
+};
+
+class FileTags : public QSet<FileTag>
+{
+public:
+ QStringList toStringList() const;
+ static FileTags fromStringList(const QStringList &strings);
+ bool matches(const FileTags &other) const;
+};
+
+LogWriter operator <<(LogWriter w, const FileTags &tags);
+QDataStream &operator >>(QDataStream &s, FileTags & tags);
+QDataStream &operator <<(QDataStream &s, const FileTags &tags);
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_FILETAGS_H
+
diff --git a/src/lib/corelib/language/forward_decls.h b/src/lib/corelib/language/forward_decls.h
new file mode 100644
index 000000000..dc7c572df
--- /dev/null
+++ b/src/lib/corelib/language/forward_decls.h
@@ -0,0 +1,120 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_LANG_FORWARD_DECLS_H
+#define QBS_LANG_FORWARD_DECLS_H
+
+#include <QSharedPointer>
+
+namespace qbs {
+namespace Internal {
+
+class Value;
+typedef QSharedPointer<Value> ValuePtr;
+typedef QSharedPointer<const Value> ValueConstPtr;
+
+class ItemValue;
+typedef QSharedPointer<ItemValue> ItemValuePtr;
+typedef QSharedPointer<const ItemValue> ItemValueConstPtr;
+
+class JSSourceValue;
+typedef QSharedPointer<JSSourceValue> JSSourceValuePtr;
+typedef QSharedPointer<const JSSourceValue> JSSourceValueConstPtr;
+
+class VariantValue;
+typedef QSharedPointer<VariantValue> VariantValuePtr;
+typedef QSharedPointer<const VariantValue> VariantValueConstPtr;
+
+class BuiltinValue;
+typedef QSharedPointer<BuiltinValue> BuiltinValuePtr;
+typedef QSharedPointer<const BuiltinValue> BuiltinValueConstPtr;
+
+class FileContext;
+typedef QSharedPointer<FileContext> FileContextPtr;
+typedef QSharedPointer<const FileContext> FileContextConstPtr;
+
+class PropertyMapInternal;
+typedef QSharedPointer<PropertyMapInternal> PropertyMapPtr;
+typedef QSharedPointer<const PropertyMapInternal> PropertyMapConstPtr;
+
+class FileTagger;
+typedef QSharedPointer<FileTagger> FileTaggerPtr;
+typedef QSharedPointer<const FileTagger> FileTaggerConstPtr;
+
+class ResolvedProduct;
+typedef QSharedPointer<ResolvedProduct> ResolvedProductPtr;
+typedef QSharedPointer<const ResolvedProduct> ResolvedProductConstPtr;
+
+class ResolvedProject;
+typedef QSharedPointer<ResolvedProject> ResolvedProjectPtr;
+typedef QSharedPointer<const ResolvedProject> ResolvedProjectConstPtr;
+
+class TopLevelProject;
+typedef QSharedPointer<TopLevelProject> TopLevelProjectPtr;
+typedef QSharedPointer<const TopLevelProject> TopLevelProjectConstPtr;
+
+class ResolvedFileContext;
+typedef QSharedPointer<ResolvedFileContext> ResolvedFileContextPtr;
+typedef QSharedPointer<const ResolvedFileContext> ResolvedFileContextConstPtr;
+
+class Rule;
+typedef QSharedPointer<Rule> RulePtr;
+typedef QSharedPointer<const Rule> RuleConstPtr;
+
+class SourceArtifact;
+typedef QSharedPointer<SourceArtifact> SourceArtifactPtr;
+typedef QSharedPointer<const SourceArtifact> SourceArtifactConstPtr;
+
+class ScriptFunction;
+typedef QSharedPointer<ScriptFunction> ScriptFunctionPtr;
+typedef QSharedPointer<const ScriptFunction> ScriptFunctionConstPtr;
+
+class RuleArtifact;
+typedef QSharedPointer<RuleArtifact> RuleArtifactPtr;
+typedef QSharedPointer<const RuleArtifact> RuleArtifactConstPtr;
+
+class ResolvedModule;
+typedef QSharedPointer<ResolvedModule> ResolvedModulePtr;
+typedef QSharedPointer<const ResolvedModule> ResolvedModuleConstPtr;
+
+class ResolvedGroup;
+typedef QSharedPointer<ResolvedGroup> GroupPtr;
+typedef QSharedPointer<const ResolvedGroup> GroupConstPtr;
+
+class ResolvedTransformer;
+typedef QSharedPointer<ResolvedTransformer> ResolvedTransformerPtr;
+typedef QSharedPointer<const ResolvedTransformer> ResolvedTransformerConstPtr;
+
+class ArtifactProperties;
+typedef QSharedPointer<ArtifactProperties> ArtifactPropertiesPtr;
+typedef QSharedPointer<const ArtifactProperties> ArtifactPropertiesConstPtr;
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_LANG_FORWARD_DECLS_H
diff --git a/src/lib/corelib/language/functiondeclaration.h b/src/lib/corelib/language/functiondeclaration.h
new file mode 100644
index 000000000..656997e7a
--- /dev/null
+++ b/src/lib/corelib/language/functiondeclaration.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_FUNCTIONDECLARATION_H
+#define QBS_FUNCTIONDECLARATION_H
+
+#include <tools/codelocation.h>
+
+namespace qbs {
+namespace Internal {
+
+class FunctionDeclaration
+{
+public:
+ FunctionDeclaration() {}
+
+ void setName(const QString &name) { m_name = name; }
+ const QString &name() const { return m_name; }
+
+ void setSourceCode(const QString &code) { m_sourceCode = code; }
+ const QString &sourceCode() const { return m_sourceCode; }
+
+ void setLocation(const CodeLocation &location) { m_location = location; }
+ const CodeLocation &location() const { return m_location; }
+
+private:
+ QString m_name;
+ QString m_sourceCode;
+ CodeLocation m_location;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_FUNCTIONDECLARATION_H
diff --git a/src/lib/corelib/language/identifiersearch.cpp b/src/lib/corelib/language/identifiersearch.cpp
new file mode 100644
index 000000000..813e87922
--- /dev/null
+++ b/src/lib/corelib/language/identifiersearch.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** 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 "identifiersearch.h"
+#include <parser/qmljsast_p.h>
+
+namespace qbs {
+namespace Internal {
+
+IdentifierSearch::IdentifierSearch()
+{
+}
+
+void IdentifierSearch::start(QbsQmlJS::AST::Node *node)
+{
+ foreach (bool *found, m_requests)
+ *found = false;
+ m_numberOfFoundIds = 0;
+ node->accept(this);
+}
+
+void IdentifierSearch::add(const QString &name, bool *found)
+{
+ m_requests.insert(name, found);
+}
+
+bool IdentifierSearch::preVisit(QbsQmlJS::AST::Node *)
+{
+ return m_numberOfFoundIds < m_requests.count();
+}
+
+bool IdentifierSearch::visit(QbsQmlJS::AST::IdentifierExpression *e)
+{
+ bool *found = m_requests.value(e->name.toString());
+ if (found && !*found) {
+ *found = true;
+ m_numberOfFoundIds++;
+ }
+ return m_numberOfFoundIds < m_requests.count();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/identifiersearch.h b/src/lib/corelib/language/identifiersearch.h
new file mode 100644
index 000000000..f82ee3262
--- /dev/null
+++ b/src/lib/corelib/language/identifiersearch.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_IDENTIFIERSEARCHVISITOR_H
+#define QBS_IDENTIFIERSEARCHVISITOR_H
+
+#include <parser/qmljsastfwd_p.h>
+#include <parser/qmljsastvisitor_p.h>
+#include <QMap>
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+
+class IdentifierSearch : private QbsQmlJS::AST::Visitor
+{
+public:
+ IdentifierSearch();
+ void start(QbsQmlJS::AST::Node *node);
+ void add(const QString &name, bool *found);
+
+private:
+ bool preVisit(QbsQmlJS::AST::Node *);
+ bool visit(QbsQmlJS::AST::IdentifierExpression *e);
+
+ QMap<QString, bool *> m_requests;
+ int m_numberOfFoundIds;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_IDENTIFIERSEARCHVISITOR_H
diff --git a/src/lib/corelib/language/importversion.cpp b/src/lib/corelib/language/importversion.cpp
new file mode 100644
index 000000000..848775016
--- /dev/null
+++ b/src/lib/corelib/language/importversion.cpp
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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 "importversion.h"
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+
+ImportVersion::ImportVersion()
+ : m_major(0), m_minor(0)
+{
+}
+
+ImportVersion ImportVersion::fromString(const QString &str, const CodeLocation &location)
+{
+ QStringList lst = str.split(QLatin1Char('.'));
+ if (Q_UNLIKELY(lst.count() < 1 || lst.count() > 2))
+ throw ErrorInfo(Tr::tr("Wrong number of components in import version."), location);
+ ImportVersion v;
+ int *parts[] = {&v.m_major, &v.m_minor, 0};
+ for (int i = 0; i < lst.count(); ++i) {
+ if (!parts[i])
+ break;
+ bool ok;
+ *parts[i] = lst.at(i).toInt(&ok);
+ if (Q_UNLIKELY(!ok))
+ throw ErrorInfo(Tr::tr("Cannot parse import version."), location);
+ }
+ return v;
+}
+
+bool ImportVersion::operator <(const ImportVersion &rhs) const
+{
+ return m_major < rhs.m_major || (m_major == rhs.m_major && m_minor < rhs.m_minor);
+}
+
+bool ImportVersion::operator ==(const ImportVersion &rhs) const
+{
+ return m_major == rhs.m_major && m_minor == rhs.m_minor;
+}
+
+bool ImportVersion::operator !=(const ImportVersion &rhs) const
+{
+ return !operator ==(rhs);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/importversion.h b/src/lib/corelib/language/importversion.h
new file mode 100644
index 000000000..034258793
--- /dev/null
+++ b/src/lib/corelib/language/importversion.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_IMPORTVERSION_H
+#define QBS_IMPORTVERSION_H
+
+#include <tools/codelocation.h>
+
+namespace qbs {
+namespace Internal {
+
+class ImportVersion
+{
+public:
+ ImportVersion();
+
+ static ImportVersion fromString(const QString &str,
+ const CodeLocation &location = CodeLocation());
+
+ bool operator <(const ImportVersion &rhs) const;
+ bool operator ==(const ImportVersion &rhs) const;
+ bool operator !=(const ImportVersion &rhs) const;
+
+private:
+ int m_major;
+ int m_minor;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_IMPORTVERSION_H
diff --git a/src/lib/corelib/language/item.cpp b/src/lib/corelib/language/item.cpp
new file mode 100644
index 000000000..7b42df5e6
--- /dev/null
+++ b/src/lib/corelib/language/item.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** 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 "item.h"
+#include "itempool.h"
+#include "filecontext.h"
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+
+namespace qbs {
+namespace Internal {
+
+Item::Item(ItemPool *pool)
+ : m_pool(pool)
+ , m_propertyObserver(0)
+ , m_moduleInstance(false)
+ , m_prototype(0)
+ , m_scope(0)
+ , m_outerItem(0)
+ , m_parent(0)
+{
+}
+
+Item::~Item()
+{
+ if (m_propertyObserver)
+ m_propertyObserver->onItemDestroyed(this);
+}
+
+Item *Item::create(ItemPool *pool)
+{
+ return pool->allocateItem();
+}
+
+Item *Item::clone(ItemPool *pool) const
+{
+ Item *dup = create(pool);
+ dup->m_id = m_id;
+ dup->m_typeName = m_typeName;
+ dup->m_location = m_location;
+ dup->m_prototype = m_prototype;
+ dup->m_scope = m_scope;
+ dup->m_outerItem = m_outerItem;
+ dup->m_parent = m_parent;
+ dup->m_children = m_children;
+ dup->m_file = m_file;
+ dup->m_properties = m_properties;
+ dup->m_propertyDeclarations = m_propertyDeclarations;
+ dup->m_functions = m_functions;
+ dup->m_modules = m_modules;
+ return dup;
+}
+
+bool Item::hasProperty(const QString &name) const
+{
+ for (const Item *item = this; item; item = item->m_prototype)
+ if (item->m_properties.contains(name))
+ return true;
+
+ return false;
+}
+
+bool Item::hasOwnProperty(const QString &name) const
+{
+ return m_properties.contains(name);
+}
+
+ValuePtr Item::property(const QString &name) const
+{
+ ValuePtr value;
+ for (const Item *item = this; item; item = item->m_prototype)
+ if ((value = item->m_properties.value(name)))
+ break;
+ return value;
+}
+
+ItemValuePtr Item::itemProperty(const QString &name, bool create)
+{
+ ItemValuePtr result;
+ ValuePtr v = property(name);
+ if (v && v->type() == Value::ItemValueType) {
+ result = v.staticCast<ItemValue>();
+ } else if (create) {
+ result = ItemValue::create(Item::create(m_pool));
+ setProperty(name, result);
+ }
+ return result;
+}
+
+JSSourceValuePtr Item::sourceProperty(const QString &name) const
+{
+ ValuePtr v = property(name);
+ if (!v || v->type() != Value::JSSourceValueType)
+ return JSSourceValuePtr();
+ return v.staticCast<JSSourceValue>();
+}
+
+const PropertyDeclaration Item::propertyDeclaration(const QString &name) const
+{
+ const PropertyDeclaration decl = m_propertyDeclarations.value(name);
+ return (!decl.isValid() && m_prototype) ? m_prototype->propertyDeclaration(name) : decl;
+}
+
+void Item::setPropertyObserver(ItemObserver *observer) const
+{
+ QBS_ASSERT(!observer || !m_propertyObserver, return); // warn if accidentally overwritten
+ m_propertyObserver = observer;
+}
+
+Item *Item::child(const QString &type, bool checkForMultiple) const
+{
+ Item *child = 0;
+ foreach (Item * const currentChild, children()) {
+ if (currentChild->typeName() == type) {
+ if (!checkForMultiple)
+ return currentChild;
+ if (child) {
+ ErrorInfo error(Tr::tr("Multiple instances of item '%1' found where at most one "
+ "is allowed.").arg(type));
+ error.append(Tr::tr("First item"), child->location());
+ error.append(Tr::tr("Second item"), currentChild->location());
+ throw error;
+ }
+ child = currentChild;
+ }
+ }
+ return child;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h
new file mode 100644
index 000000000..866927a0e
--- /dev/null
+++ b/src/lib/corelib/language/item.h
@@ -0,0 +1,282 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ITEM_H
+#define QBS_ITEM_H
+
+#include "forward_decls.h"
+#include "itemobserver.h"
+#include "value.h"
+#include "functiondeclaration.h"
+#include "propertydeclaration.h"
+#include <parser/qmljsmemorypool_p.h>
+#include <tools/codelocation.h>
+#include <tools/error.h>
+#include <tools/weakpointer.h>
+
+#include <QList>
+#include <QMap>
+#include <QSharedPointer>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+
+class ItemPool;
+class ProjectFile;
+
+class Item : public QbsQmlJS::Managed
+{
+ friend class BuiltinDeclarations;
+ friend class ItemPool;
+ friend class ItemReaderASTVisitor;
+ Q_DISABLE_COPY(Item)
+ Item(ItemPool *pool);
+
+public:
+ ~Item();
+
+ struct Module
+ {
+ Module()
+ : item(0)
+ {}
+
+ QStringList name;
+ Item *item;
+ };
+ typedef QList<Module> Modules;
+ typedef QMap<QString, PropertyDeclaration> PropertyDeclarationMap;
+ typedef QMap<QString, ValuePtr> PropertyMap;
+
+ static Item *create(ItemPool *pool);
+ Item *clone(ItemPool *pool) const;
+ ItemPool *pool() const;
+
+ const QString &id() const;
+ const QString &typeName() const;
+ const CodeLocation &location() const;
+ Item *prototype() const;
+ Item *scope() const;
+ bool isModuleInstance() const;
+ Item *outerItem() const;
+ Item *parent() const;
+ const FileContextPtr file() const;
+ QList<Item *> children() const;
+ Item *child(const QString &type, bool checkForMultiple = true) const;
+ const PropertyMap &properties() const;
+ const PropertyDeclarationMap &propertyDeclarations() const;
+ const PropertyDeclaration propertyDeclaration(const QString &name) const;
+ const Modules &modules() const;
+ Modules &modules();
+ const ErrorInfo &error() const { return m_error; }
+
+ bool hasProperty(const QString &name) const;
+ bool hasOwnProperty(const QString &name) const;
+ ValuePtr property(const QString &name) const;
+ ItemValuePtr itemProperty(const QString &name, bool create = false);
+ JSSourceValuePtr sourceProperty(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);
+ void setTypeName(const QString &name);
+ void setLocation(const CodeLocation &location);
+ void setPrototype(Item *prototype);
+ void setFile(const FileContextPtr &file);
+ void setScope(Item *item);
+ void setModuleInstanceFlag(bool b);
+ void setOuterItem(Item *item);
+ void setChildren(const QList<Item *> &children);
+ void setParent(Item *item);
+ static void addChild(Item *parent, Item *child);
+ void setError(const ErrorInfo &error) { m_error = error; }
+
+private:
+ ItemPool *m_pool;
+ mutable ItemObserver *m_propertyObserver;
+ QString m_id;
+ QString m_typeName;
+ CodeLocation m_location;
+ bool m_moduleInstance;
+ Item *m_prototype;
+ Item *m_scope;
+ Item *m_outerItem;
+ Item *m_parent;
+ QList<Item *> m_children;
+ FileContextPtr m_file;
+ PropertyMap m_properties;
+ PropertyDeclarationMap m_propertyDeclarations;
+ QList<FunctionDeclaration> m_functions;
+ Modules m_modules;
+ ErrorInfo m_error; // For SubProject items. May or may not be reported depending on their condition.
+};
+
+inline ItemPool *Item::pool() const
+{
+ return m_pool;
+}
+
+inline const QString &Item::id() const
+{
+ return m_id;
+}
+
+inline const QString &Item::typeName() const
+{
+ return m_typeName;
+}
+
+inline const CodeLocation &Item::location() const
+{
+ return m_location;
+}
+
+inline Item *Item::prototype() const
+{
+ return m_prototype;
+}
+
+inline Item *Item::scope() const
+{
+ return m_scope;
+}
+
+inline bool Item::isModuleInstance() const
+{
+ return m_moduleInstance;
+}
+
+inline Item *Item::outerItem() const
+{
+ return m_outerItem;
+}
+
+inline Item *Item::parent() const
+{
+ return m_parent;
+}
+
+inline const FileContextPtr Item::file() const
+{
+ return m_file;
+}
+
+inline QList<Item *> Item::children() const
+{
+ return m_children;
+}
+
+inline const Item::PropertyMap &Item::properties() const
+{
+ return m_properties;
+}
+
+inline const Item::PropertyDeclarationMap &Item::propertyDeclarations() const
+{
+ return m_propertyDeclarations;
+}
+
+inline void Item::setProperty(const QString &name, const ValuePtr &value)
+{
+ m_properties.insert(name, value);
+ if (m_propertyObserver)
+ m_propertyObserver->onItemPropertyChanged(this);
+}
+
+inline void Item::setPropertyDeclaration(const QString &name,
+ const PropertyDeclaration &declaration)
+{
+ m_propertyDeclarations.insert(name, declaration);
+}
+
+inline void Item::setTypeName(const QString &name)
+{
+ m_typeName = name;
+}
+
+inline void Item::setLocation(const CodeLocation &location)
+{
+ m_location = location;
+}
+
+inline void Item::setPrototype(Item *prototype)
+{
+ m_prototype = prototype;
+}
+
+inline void Item::setFile(const FileContextPtr &file)
+{
+ m_file = file;
+}
+
+inline void Item::setScope(Item *item)
+{
+ m_scope = item;
+}
+
+inline void Item::setModuleInstanceFlag(bool b)
+{
+ m_moduleInstance = b;
+}
+
+inline void Item::setOuterItem(Item *item)
+{
+ m_outerItem = item;
+}
+
+inline void Item::setChildren(const QList<Item *> &children)
+{
+ m_children = children;
+}
+
+inline void Item::setParent(Item *item)
+{
+ m_parent = item;
+}
+
+inline void Item::addChild(Item *parent, Item *child)
+{
+ parent->m_children.append(child);
+ child->setParent(parent);
+}
+
+inline const Item::Modules &Item::modules() const
+{
+ return m_modules;
+}
+
+inline Item::Modules &Item::modules()
+{
+ return m_modules;
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ITEM_H
diff --git a/src/lib/corelib/language/itemdeclaration.cpp b/src/lib/corelib/language/itemdeclaration.cpp
new file mode 100644
index 000000000..e2fdb9330
--- /dev/null
+++ b/src/lib/corelib/language/itemdeclaration.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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 "itemdeclaration.h"
+
+namespace qbs {
+namespace Internal {
+
+ItemDeclaration::ItemDeclaration(const QString &typeName)
+ : m_typeName(typeName)
+{
+}
+
+ItemDeclaration::ItemDeclaration(const qbs::Internal::ItemDeclaration &other)
+ : m_typeName(other.m_typeName)
+ , m_properties(other.m_properties)
+ , m_allowedChildTypes(other.m_allowedChildTypes)
+{
+}
+
+ItemDeclaration &ItemDeclaration::operator<<(const PropertyDeclaration &decl)
+{
+ m_properties.append(decl);
+ return *this;
+}
+
+bool ItemDeclaration::isChildTypeAllowed(const QString &typeName) const
+{
+ return m_allowedChildTypes.contains(typeName);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/itemdeclaration.h b/src/lib/corelib/language/itemdeclaration.h
new file mode 100644
index 000000000..25a0e3fd3
--- /dev/null
+++ b/src/lib/corelib/language/itemdeclaration.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ITEMDECLARATION_H
+#define QBS_ITEMDECLARATION_H
+
+#include "propertydeclaration.h"
+#include <QSet>
+
+namespace qbs {
+namespace Internal {
+
+class ItemDeclaration
+{
+public:
+ ItemDeclaration(const QString &typeName = QString());
+ ItemDeclaration(const ItemDeclaration &other);
+
+ const QString &typeName() const { return m_typeName; }
+
+ typedef QList<PropertyDeclaration> Properties;
+ void setProperties(const Properties &props) { m_properties = props; }
+ const Properties &properties() const { return m_properties; }
+
+ ItemDeclaration &operator<<(const PropertyDeclaration &decl);
+
+ typedef QSet<QString> TypeNames;
+ void setAllowedChildTypes(const TypeNames &typeNames) { m_allowedChildTypes = typeNames; }
+ const TypeNames &allowedChildTypes() const { return m_allowedChildTypes; }
+ bool isChildTypeAllowed(const QString &typeName) const;
+
+private:
+ QString m_typeName;
+ Properties m_properties;
+ TypeNames m_allowedChildTypes;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ITEMDECLARATION_H
diff --git a/src/lib/corelib/language/itemobserver.h b/src/lib/corelib/language/itemobserver.h
new file mode 100644
index 000000000..684b33d14
--- /dev/null
+++ b/src/lib/corelib/language/itemobserver.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ITEMOBSERVER_H
+#define QBS_ITEMOBSERVER_H
+
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+
+class Item;
+
+class ItemObserver
+{
+public:
+ virtual void onItemPropertyChanged(Item *item) = 0;
+ virtual void onItemDestroyed(Item *item) = 0;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ITEMOBSERVER_H
diff --git a/src/lib/corelib/language/itempool.cpp b/src/lib/corelib/language/itempool.cpp
new file mode 100644
index 000000000..a07022c3f
--- /dev/null
+++ b/src/lib/corelib/language/itempool.cpp
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** 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 "itempool.h"
+#include "item.h"
+
+namespace qbs {
+namespace Internal {
+
+ItemPool::ItemPool()
+{
+}
+
+ItemPool::~ItemPool()
+{
+ for (ItemVector::const_iterator it = m_items.constBegin(); it != m_items.constEnd(); ++it)
+ (*it)->~Item();
+}
+
+Item *ItemPool::allocateItem()
+{
+ Item *item = new (&m_pool) Item(this);
+ m_items.push_back(item);
+ return item;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/itempool.h b/src/lib/corelib/language/itempool.h
new file mode 100644
index 000000000..04d7bbb40
--- /dev/null
+++ b/src/lib/corelib/language/itempool.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ITEMPOOL_H
+#define QBS_ITEMPOOL_H
+
+#include <parser/qmljsmemorypool_p.h>
+
+#include <QList>
+
+namespace qbs {
+namespace Internal {
+
+class Item;
+
+class ItemPool
+{
+ Q_DISABLE_COPY(ItemPool)
+public:
+ ItemPool();
+ ~ItemPool();
+
+ Item *allocateItem();
+
+private:
+ QbsQmlJS::MemoryPool m_pool;
+ typedef QList<Item *> ItemVector;
+ ItemVector m_items;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ITEMPOOL_H
diff --git a/src/lib/corelib/language/itemreader.cpp b/src/lib/corelib/language/itemreader.cpp
new file mode 100644
index 000000000..ba4859899
--- /dev/null
+++ b/src/lib/corelib/language/itemreader.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** 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 "itemreader.h"
+#include "asttools.h"
+#include "itemreaderastvisitor.h"
+#include <logging/translator.h>
+#include <parser/qmljsengine_p.h>
+#include <parser/qmljslexer_p.h>
+#include <parser/qmljsparser_p.h>
+#include <tools/error.h>
+#include <QExplicitlySharedDataPointer>
+#include <QFile>
+#include <QFileInfo>
+#include <QSharedData>
+#include <QTextStream>
+
+namespace qbs {
+namespace Internal {
+
+class ASTCacheValueData : public QSharedData
+{
+ Q_DISABLE_COPY(ASTCacheValueData)
+public:
+ ASTCacheValueData()
+ : ast(0)
+ , processing(false)
+ {
+ }
+
+ QString code;
+ QbsQmlJS::Engine engine;
+ QbsQmlJS::AST::UiProgram *ast;
+ bool processing;
+};
+
+class ASTCacheValue
+{
+public:
+ ASTCacheValue()
+ : d(new ASTCacheValueData)
+ {
+ }
+
+ ASTCacheValue(const ASTCacheValue &other)
+ : d(other.d)
+ {
+ }
+
+ void setProcessingFlag(bool b) { d->processing = b; }
+ bool isProcessing() const { return d->processing; }
+
+ void setCode(const QString &code) { d->code = code; }
+ QString code() const { return d->code; }
+
+ QbsQmlJS::Engine *engine() const { return &d->engine; }
+
+ void setAst(QbsQmlJS::AST::UiProgram *ast) { d->ast = ast; }
+ QbsQmlJS::AST::UiProgram *ast() const { return d->ast; }
+ bool isValid() const { return d->ast; }
+
+private:
+ QExplicitlySharedDataPointer<ASTCacheValueData> d;
+};
+
+class ItemReader::ASTCache : public QHash<QString, ASTCacheValue> {};
+
+
+ItemReader::ItemReader(BuiltinDeclarations *builtins, const Logger &logger)
+ : m_pool(0)
+ , m_builtins(builtins)
+ , m_logger(logger)
+ , m_astCache(new ASTCache)
+{
+}
+
+ItemReader::~ItemReader()
+{
+ delete m_astCache;
+}
+
+void ItemReader::setSearchPaths(const QStringList &searchPaths)
+{
+ m_searchPaths = searchPaths;
+}
+
+void ItemReader::pushExtraSearchPaths(const QStringList &extraSearchPaths)
+{
+ m_extraSearchPaths.push(extraSearchPaths);
+}
+
+void ItemReader::popExtraSearchPaths()
+{
+ m_extraSearchPaths.pop();
+}
+
+QStringList ItemReader::searchPaths() const
+{
+ QStringList paths = m_searchPaths;
+ if (!m_extraSearchPaths.isEmpty())
+ paths += m_extraSearchPaths.top();
+ return paths;
+}
+
+void ItemReader::cacheDirectoryEntries(const QString &dirPath, const QStringList &entries)
+{
+ m_directoryEntries.insert(dirPath, entries);
+}
+
+bool ItemReader::findDirectoryEntries(const QString &dirPath, QStringList *entries) const
+{
+ const QHash<QString, QStringList>::ConstIterator it = m_directoryEntries.constFind(dirPath);
+ if (it == m_directoryEntries.constEnd())
+ return false;
+ *entries = it.value();
+ return true;
+}
+
+Item *ItemReader::readFile(const QString &filePath)
+{
+ Item * const item = internalReadFile(filePath).rootItem;
+ return item;
+}
+
+QSet<QString> ItemReader::filesRead() const
+{
+ return m_filesRead;
+}
+
+ItemReaderResult ItemReader::internalReadFile(const QString &filePath)
+{
+ ASTCacheValue &cacheValue = (*m_astCache)[filePath];
+ if (cacheValue.isValid()) {
+ if (Q_UNLIKELY(cacheValue.isProcessing()))
+ throw ErrorInfo(Tr::tr("Loop detected when importing '%1'.").arg(filePath));
+ } else {
+ QFile file(filePath);
+ if (Q_UNLIKELY(!file.open(QFile::ReadOnly)))
+ throw ErrorInfo(Tr::tr("Couldn't open '%1'.").arg(filePath));
+
+ m_filesRead.insert(filePath);
+ const QString code = QTextStream(&file).readAll();
+ QbsQmlJS::Lexer lexer(cacheValue.engine());
+ lexer.setCode(code, 1);
+ QbsQmlJS::Parser parser(cacheValue.engine());
+
+ file.close();
+ if (!parser.parse()) {
+ QList<QbsQmlJS::DiagnosticMessage> parserMessages = parser.diagnosticMessages();
+ if (Q_UNLIKELY(!parserMessages.isEmpty())) {
+ ErrorInfo err;
+ foreach (const QbsQmlJS::DiagnosticMessage &msg, parserMessages)
+ err.append(msg.message, toCodeLocation(filePath, msg.loc));
+ throw err;
+ }
+ }
+
+ cacheValue.setCode(code);
+ cacheValue.setAst(parser.ast());
+ }
+
+ ItemReaderResult result;
+ ItemReaderASTVisitor itemReader(this, &result);
+ itemReader.setFilePath(QFileInfo(filePath).absoluteFilePath());
+ itemReader.setSourceCode(cacheValue.code());
+ cacheValue.setProcessingFlag(true);
+ cacheValue.ast()->accept(&itemReader);
+ cacheValue.setProcessingFlag(false);
+ return result;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/itemreader.h b/src/lib/corelib/language/itemreader.h
new file mode 100644
index 000000000..266c30f93
--- /dev/null
+++ b/src/lib/corelib/language/itemreader.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ITEMREADER_H
+#define QBS_ITEMREADER_H
+
+#include "forward_decls.h"
+#include <logging/logger.h>
+
+#include <QHash>
+#include <QSet>
+#include <QStack>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+
+class BuiltinDeclarations;
+class Item;
+class ItemPool;
+
+struct ItemReaderResult
+{
+ ItemReaderResult()
+ : rootItem(0)
+ {}
+
+ Item *rootItem;
+ typedef QHash<const Item *, QSet<JSSourceValuePtr> > SourceValuesPerItem;
+ SourceValuesPerItem conditionalValuesPerScopeItem;
+};
+
+/*
+ * Reads a qbs file and creates a tree of Item objects.
+ *
+ * In this stage the following steps are performed:
+ * - The QML/JS parser creates the AST.
+ * - The AST is converted to a tree of Item objects.
+ *
+ * This class is also responsible for the QMLish inheritance semantics.
+ */
+class ItemReader
+{
+ friend class ItemReaderASTVisitor;
+public:
+ ItemReader(BuiltinDeclarations *builtins, const Logger &logger);
+ ~ItemReader();
+
+ BuiltinDeclarations *builtins() const { return m_builtins; }
+ Logger logger() const { return m_logger; }
+
+ void setPool(ItemPool *pool) { m_pool = pool; }
+ void setSearchPaths(const QStringList &searchPaths);
+ void pushExtraSearchPaths(const QStringList &extraSearchPaths);
+ void popExtraSearchPaths();
+ QStringList searchPaths() const;
+
+ Item *readFile(const QString &filePath);
+
+ QSet<QString> filesRead() const;
+
+private:
+ ItemReaderResult internalReadFile(const QString &filePath);
+
+ void cacheDirectoryEntries(const QString &dirPath, const QStringList &entries);
+ bool findDirectoryEntries(const QString &dirPath, QStringList *entries) const;
+
+ ItemPool *m_pool;
+ BuiltinDeclarations *m_builtins;
+ Logger m_logger;
+ QStringList m_searchPaths;
+ QStack<QStringList> m_extraSearchPaths;
+ QHash<const Item *, QSet<JSSourceValuePtr> > m_conditionalValuesPerScopeItem;
+
+ class ASTCache;
+ ASTCache *m_astCache;
+ QSet<QString> m_filesRead;
+ QHash<QString, QStringList> m_directoryEntries;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ITEMREADER_H
diff --git a/src/lib/corelib/language/itemreaderastvisitor.cpp b/src/lib/corelib/language/itemreaderastvisitor.cpp
new file mode 100644
index 000000000..a08a73648
--- /dev/null
+++ b/src/lib/corelib/language/itemreaderastvisitor.cpp
@@ -0,0 +1,643 @@
+/****************************************************************************
+**
+** 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 "itemreaderastvisitor.h"
+#include "asttools.h"
+#include "builtindeclarations.h"
+#include "identifiersearch.h"
+#include "itemreader.h"
+#include <jsextensions/jsextensions.h>
+#include <parser/qmljsast_p.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/qbsassert.h>
+#include <tools/qttools.h>
+#include <logging/translator.h>
+
+#include <QDirIterator>
+#include <QFileInfo>
+#include <QStringList>
+
+using namespace QbsQmlJS;
+
+namespace qbs {
+namespace Internal {
+
+ItemReaderASTVisitor::ItemReaderASTVisitor(ItemReader *reader, ItemReaderResult *result)
+ : m_reader(reader)
+ , m_readerResult(result)
+ , m_languageVersion(ImportVersion::fromString(reader->builtins()->languageVersion()))
+ , m_item(0)
+ , m_sourceValue(0)
+{
+}
+
+ItemReaderASTVisitor::~ItemReaderASTVisitor()
+{
+}
+
+bool ItemReaderASTVisitor::visit(AST::UiProgram *ast)
+{
+ Q_UNUSED(ast);
+ m_sourceValue.clear();
+ m_file = FileContext::create();
+ m_file->m_filePath = m_filePath;
+
+ if (Q_UNLIKELY(!ast->members->member))
+ throw ErrorInfo(Tr::tr("No root item found in %1.").arg(m_filePath));
+
+ return true;
+}
+
+bool ItemReaderASTVisitor::addPrototype(const QString &fileName, const QString &filePath,
+ const QString &as, bool needsCheck)
+{
+ if (needsCheck && fileName.size() <= 4)
+ return false;
+
+ const QString componentName = fileName.left(fileName.size() - 4);
+ // ### validate componentName
+
+ if (needsCheck && !componentName.at(0).isUpper())
+ return false;
+
+ QStringList prototypeName;
+ if (!as.isEmpty())
+ prototypeName.append(as);
+ prototypeName.append(componentName);
+ m_typeNameToFile.insert(prototypeName, filePath);
+ return true;
+}
+
+void ItemReaderASTVisitor::collectPrototypes(const QString &path, const QString &as)
+{
+ QStringList fileNames; // Yes, file *names*.
+ if (m_reader->findDirectoryEntries(path, &fileNames)) {
+ foreach (const QString &fileName, fileNames)
+ addPrototype(fileName, path + QLatin1Char('/') + fileName, as, false);
+ return;
+ }
+
+ QDirIterator dirIter(path, QStringList("*.qbs"));
+ while (dirIter.hasNext()) {
+ const QString filePath = dirIter.next();
+ const QString fileName = dirIter.fileName();
+ if (addPrototype(fileName, filePath, as, true))
+ fileNames << fileName;
+ }
+ m_reader->cacheDirectoryEntries(path, fileNames);
+}
+
+bool ItemReaderASTVisitor::visit(AST::UiImportList *uiImportList)
+{
+ foreach (const QString &searchPath, m_reader->searchPaths())
+ collectPrototypes(searchPath + QLatin1String("/imports"), QString());
+
+ const QString path = FileInfo::path(m_filePath);
+
+ // files in the same directory are available as prototypes
+ collectPrototypes(path, QString());
+
+ QSet<QString> importAsNames;
+ QHash<QString, JsImport> jsImports;
+
+ for (const AST::UiImportList *it = uiImportList; it; it = it->next) {
+ const AST::UiImport *const import = it->import;
+
+ QStringList importUri;
+ bool isBase = false;
+ if (import->importUri) {
+ importUri = toStringList(import->importUri);
+ isBase = (importUri.size() == 1 && importUri.first() == QLatin1String("qbs"))
+ || (importUri.size() == 2 && importUri.first() == QLatin1String("qbs")
+ && importUri.last() == QLatin1String("base"));
+ if (isBase)
+ checkImportVersion(import->versionToken);
+ else if (import->versionToken.length)
+ m_reader->logger().printWarning(ErrorInfo(Tr::tr("Superfluous version specification."),
+ toCodeLocation(import->versionToken)));
+ }
+
+ QString as;
+ if (isBase) {
+ if (Q_UNLIKELY(!import->importId.isNull())) {
+ throw ErrorInfo(Tr::tr("Import of qbs.base must have no 'as <Name>'"),
+ toCodeLocation(import->importIdToken));
+ }
+ } else {
+ if (importUri.count() == 2 && importUri.first() == QLatin1String("qbs")) {
+ const QString extensionName = importUri.last();
+ if (JsExtensions::hasExtension(extensionName)) {
+ if (Q_UNLIKELY(!import->importId.isNull())) {
+ throw ErrorInfo(Tr::tr("Import of built-in extension '%1' "
+ "must not have 'as' specifier.").arg(extensionName));
+ }
+ if (Q_UNLIKELY(m_file->m_jsExtensions.contains(extensionName))) {
+ m_reader->logger().printWarning(Tr::tr("Built-in extension '%1' already "
+ "imported.").arg(extensionName));
+ } else {
+ m_file->m_jsExtensions << extensionName;
+ }
+ continue;
+ }
+ }
+
+ if (import->importId.isNull()) {
+ if (!import->fileName.isNull()) {
+ throw ErrorInfo(Tr::tr("File imports require 'as <Name>'"),
+ toCodeLocation(import->importToken));
+ }
+ if (importUri.isEmpty()) {
+ throw ErrorInfo(Tr::tr("Invalid import URI."),
+ toCodeLocation(import->importToken));
+ }
+ as = importUri.last();
+ } else {
+ as = import->importId.toString();
+ }
+
+ if (Q_UNLIKELY(importAsNames.contains(as))) {
+ throw ErrorInfo(Tr::tr("Can't import into the same name more than once."),
+ toCodeLocation(import->importIdToken));
+ }
+ if (Q_UNLIKELY(JsExtensions::hasExtension(as))) {
+ throw ErrorInfo(Tr::tr("Cannot reuse the name of built-in extension '%1'.")
+ .arg(as));
+ }
+ importAsNames.insert(as);
+ }
+
+ if (!import->fileName.isNull()) {
+ QString name = FileInfo::resolvePath(path, import->fileName.toString());
+
+ QFileInfo fi(name);
+ if (Q_UNLIKELY(!fi.exists()))
+ throw ErrorInfo(Tr::tr("Can't find imported file %0.").arg(name),
+ CodeLocation(m_filePath, import->fileNameToken.startLine,
+ import->fileNameToken.startColumn));
+ name = fi.canonicalFilePath();
+ if (fi.isDir()) {
+ collectPrototypes(name, as);
+ } else {
+ if (name.endsWith(".js", Qt::CaseInsensitive)) {
+ JsImport &jsImport = jsImports[as];
+ jsImport.scopeName = as;
+ jsImport.fileNames.append(name);
+ jsImport.location = toCodeLocation(import->firstSourceLocation());
+ } else if (name.endsWith(".qbs", Qt::CaseInsensitive)) {
+ m_typeNameToFile.insert(QStringList(as), name);
+ } else {
+ throw ErrorInfo(Tr::tr("Can only import .qbs and .js files"),
+ CodeLocation(m_filePath, import->fileNameToken.startLine,
+ import->fileNameToken.startColumn));
+ }
+ }
+ } else if (!importUri.isEmpty()) {
+ const QString importPath = isBase
+ ? QLatin1String("qbs/base") : importUri.join(QDir::separator());
+ bool found = m_typeNameToFile.contains(importUri);
+ if (!found) {
+ foreach (const QString &searchPath, m_reader->searchPaths()) {
+ const QFileInfo fi(FileInfo::resolvePath(
+ FileInfo::resolvePath(searchPath, "imports"), importPath));
+ if (fi.isDir()) {
+ // ### versioning, qbsdir file, etc.
+ const QString &resultPath = fi.absoluteFilePath();
+ collectPrototypes(resultPath, as);
+
+ QDirIterator dirIter(resultPath, QStringList("*.js"));
+ while (dirIter.hasNext()) {
+ dirIter.next();
+ JsImport &jsImport = jsImports[as];
+ if (jsImport.scopeName.isNull()) {
+ jsImport.scopeName = as;
+ jsImport.location = toCodeLocation(import->firstSourceLocation());
+ }
+ jsImport.fileNames.append(dirIter.filePath());
+ }
+ found = true;
+ break;
+ }
+ }
+ }
+ if (Q_UNLIKELY(!found)) {
+ throw ErrorInfo(Tr::tr("import %1 not found").arg(importUri.join(".")),
+ toCodeLocation(import->fileNameToken));
+ }
+ }
+ }
+
+ for (QHash<QString, JsImport>::const_iterator it = jsImports.constBegin();
+ it != jsImports.constEnd(); ++it)
+ {
+ m_file->m_jsImports += it.value();
+ }
+
+ return false;
+}
+
+bool ItemReaderASTVisitor::visit(AST::UiObjectDefinition *ast)
+{
+ const QString typeName = ast->qualifiedTypeNameId->name.toString();
+
+ Item *item = Item::create(m_reader->m_pool);
+ item->m_file = m_file;
+ item->m_parent = m_item;
+ item->m_typeName = typeName;
+ item->m_location = ::qbs::Internal::toCodeLocation(m_file->filePath(),
+ ast->qualifiedTypeNameId->identifierToken);
+
+ if (m_item) {
+ // Add this item to the children of the parent item.
+ m_item->m_children += item;
+ } else {
+ // This is the root item.
+ m_item = item;
+ m_readerResult->rootItem = item;
+ }
+
+ if (ast->initializer) {
+ qSwap(m_item, item);
+ ast->initializer->accept(this);
+ qSwap(m_item, item);
+ }
+
+ m_reader->m_builtins->setupItemForBuiltinType(item);
+
+ if (item->typeName() != QLatin1String("Properties")
+ && item->typeName() != QLatin1String("SubProject")) {
+ setupAlternatives(item);
+ }
+
+ // resolve inheritance
+ const QStringList fullTypeName = toStringList(ast->qualifiedTypeNameId);
+ const QString baseTypeFileName = m_typeNameToFile.value(fullTypeName);
+ if (!baseTypeFileName.isEmpty()) {
+ const ItemReaderResult baseFile = m_reader->internalReadFile(baseTypeFileName);
+ mergeItem(item, baseFile.rootItem, baseFile);
+ if (baseFile.rootItem->m_file->m_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.
+ ensureIdScope(item->m_file);
+ baseFile.rootItem->m_file->m_idScope->setPrototype(item->m_file->m_idScope);
+ }
+ }
+
+ return false;
+}
+
+void ItemReaderASTVisitor::checkDuplicateBinding(Item *item, const QStringList &bindingName,
+ const AST::SourceLocation &sourceLocation)
+{
+ if (Q_UNLIKELY(item->properties().contains(bindingName.last()))) {
+ QString msg = Tr::tr("Duplicate binding for '%1'");
+ throw ErrorInfo(msg.arg(bindingName.join(".")),
+ qbs::Internal::toCodeLocation(m_file->filePath(), sourceLocation));
+ }
+}
+
+bool ItemReaderASTVisitor::visit(AST::UiPublicMember *ast)
+{
+ PropertyDeclaration p;
+ if (Q_UNLIKELY(ast->name.isEmpty()))
+ throw ErrorInfo(Tr::tr("public member without name"));
+ if (Q_UNLIKELY(ast->memberType.isEmpty()))
+ throw ErrorInfo(Tr::tr("public member without type"));
+ if (Q_UNLIKELY(ast->type == AST::UiPublicMember::Signal))
+ throw ErrorInfo(Tr::tr("public member with signal type not supported"));
+ p.name = ast->name.toString();
+ p.type = PropertyDeclaration::propertyTypeFromString(ast->memberType.toString());
+ if (p.type == PropertyDeclaration::UnknownType)
+ throw ErrorInfo(Tr::tr("Unknown type '%1' in property declaration.")
+ .arg(ast->memberType.toString()), toCodeLocation(ast->typeToken));
+ if (ast->typeModifier.compare(QLatin1String("list")))
+ p.flags |= PropertyDeclaration::ListProperty;
+ else if (Q_UNLIKELY(!ast->typeModifier.isEmpty()))
+ throw ErrorInfo(Tr::tr("public member with type modifier '%1' not supported").arg(
+ ast->typeModifier.toString()));
+
+ m_item->m_propertyDeclarations.insert(p.name, p);
+
+ JSSourceValuePtr value = JSSourceValue::create();
+ value->setFile(m_file);
+ if (ast->statement) {
+ m_sourceValue.swap(value);
+ visitStatement(ast->statement);
+ m_sourceValue.swap(value);
+ const QStringList bindingName(p.name);
+ checkDuplicateBinding(m_item, bindingName, ast->colonToken);
+ }
+
+ m_item->m_properties.insert(p.name, value);
+ return false;
+}
+
+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")) {
+ AST::ExpressionStatement *expStmt =
+ AST::cast<AST::ExpressionStatement *>(ast->statement);
+ if (Q_UNLIKELY(!expStmt))
+ throw ErrorInfo(Tr::tr("id: must be followed by identifier"));
+ AST::IdentifierExpression *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();
+ ensureIdScope(m_file);
+ m_file->m_idScope->m_properties[m_item->m_id] = ItemValue::create(m_item);
+ return false;
+ }
+
+ JSSourceValuePtr value = JSSourceValue::create();
+ value->setFile(m_file);
+ m_sourceValue.swap(value);
+ visitStatement(ast->statement);
+ m_sourceValue.swap(value);
+
+ Item *targetItem = targetItemForBinding(m_item, bindingName, value->location());
+ checkDuplicateBinding(targetItem, bindingName, ast->qualifiedId->identifierToken);
+ targetItem->m_properties.insert(bindingName.last(), value);
+ return false;
+}
+
+bool ItemReaderASTVisitor::visit(AST::FunctionDeclaration *ast)
+{
+ FunctionDeclaration f;
+ if (Q_UNLIKELY(ast->name.isNull()))
+ throw ErrorInfo(Tr::tr("function decl without name"));
+ f.setName(ast->name.toString());
+
+ // remove the name
+ QString funcNoName = textOf(m_sourceCode, ast);
+ funcNoName.replace(QRegExp("^(\\s*function\\s*)\\w*"), "(\\1");
+ funcNoName.append(")");
+ f.setSourceCode(funcNoName);
+
+ f.setLocation(toCodeLocation(ast->firstSourceLocation()));
+ m_item->m_functions += f;
+ return false;
+}
+
+bool ItemReaderASTVisitor::visitStatement(AST::Statement *statement)
+{
+ QBS_CHECK(statement);
+ QBS_CHECK(m_sourceValue);
+
+ QString sourceCode = textOf(m_sourceCode, statement);
+ if (AST::cast<AST::Block *>(statement)) {
+ // rewrite blocks to be able to use return statements in property assignments
+ sourceCode.prepend("(function()");
+ sourceCode.append(")()");
+ m_sourceValue->m_hasFunctionForm = true;
+ }
+
+ m_sourceValue->setSourceCode(sourceCode);
+ m_sourceValue->setLocation(toCodeLocation(statement->firstSourceLocation()));
+
+ IdentifierSearch idsearch;
+ idsearch.add(QLatin1String("base"), &m_sourceValue->m_sourceUsesBase);
+ idsearch.add(QLatin1String("outer"), &m_sourceValue->m_sourceUsesOuter);
+ idsearch.start(statement);
+ return false;
+}
+
+CodeLocation ItemReaderASTVisitor::toCodeLocation(AST::SourceLocation location) const
+{
+ return CodeLocation(m_filePath, location.startLine, location.startColumn);
+}
+
+Item *ItemReaderASTVisitor::targetItemForBinding(Item *item,
+ const QStringList &bindingName,
+ const CodeLocation &bindingLocation)
+{
+ Item *targetItem = item;
+ const int c = bindingName.count() - 1;
+ for (int i = 0; i < c; ++i) {
+ ValuePtr v = targetItem->m_properties.value(bindingName.at(i));
+ if (!v) {
+ Item *newItem = Item::create(m_reader->m_pool);
+ v = ItemValue::create(newItem);
+ targetItem->m_properties.insert(bindingName.at(i), v);
+ }
+ if (Q_UNLIKELY(v->type() != Value::ItemValueType)) {
+ QString msg = Tr::tr("Binding to non-item property.");
+ throw ErrorInfo(msg, bindingLocation);
+ }
+ ItemValuePtr jsv = v.staticCast<ItemValue>();
+ targetItem = jsv->item();
+ }
+ return targetItem;
+}
+
+void ItemReaderASTVisitor::checkImportVersion(const AST::SourceLocation &versionToken) const
+{
+ if (!versionToken.length)
+ return;
+ const QString importVersionString = m_sourceCode.mid(versionToken.offset, versionToken.length);
+ const ImportVersion importVersion
+ = ImportVersion::fromString(importVersionString, toCodeLocation(versionToken));
+ if (Q_UNLIKELY(importVersion != m_languageVersion))
+ throw ErrorInfo(Tr::tr("Incompatible qbs version %1. This is qbs %2.").arg(
+ importVersionString, m_reader->builtins()->languageVersion()),
+ toCodeLocation(versionToken));
+}
+
+void ItemReaderASTVisitor::mergeItem(Item *dst, const Item *src,
+ const ItemReaderResult &baseFile)
+{
+ if (!src->typeName().isEmpty())
+ dst->setTypeName(src->typeName());
+
+ 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 (QMap<QString, ValuePtr>::const_iterator it = src->m_properties.constBegin();
+ it != src->m_properties.constEnd(); ++it)
+ {
+ ValuePtr &v = dst->m_properties[it.key()];
+ if (v) {
+ if (v->type() == it.value()->type()) {
+ if (v->type() == Value::JSSourceValueType) {
+ JSSourceValuePtr sv = v.staticCast<JSSourceValue>();
+ while (sv->baseValue())
+ sv = sv->baseValue();
+ const JSSourceValuePtr baseValue = it.value().staticCast<JSSourceValue>();
+ sv->setBaseValue(baseValue);
+ for (QList<JSSourceValue::Alternative>::iterator it
+ = sv->m_alternatives.begin(); it != sv->m_alternatives.end(); ++it) {
+ JSSourceValue::Alternative &alternative = *it;
+ alternative.value->setBaseValue(baseValue);
+ }
+ } else if (v->type() == Value::ItemValueType) {
+ QBS_CHECK(v.staticCast<ItemValue>()->item());
+ QBS_CHECK(it.value().staticCast<const ItemValue>()->item());
+ mergeItem(v.staticCast<ItemValue>()->item(),
+ it.value().staticCast<const ItemValue>()->item(),
+ baseFile);
+ } else {
+ QBS_CHECK(!"unexpected value type");
+ }
+ }
+ } else {
+ v = it.value();
+ }
+ }
+
+ for (QMap<QString, PropertyDeclaration>::const_iterator it
+ = src->m_propertyDeclarations.constBegin();
+ it != src->m_propertyDeclarations.constEnd(); ++it) {
+ dst->m_propertyDeclarations[it.key()] = it.value();
+ }
+ foreach (const JSSourceValuePtr &valueWithAlternatives,
+ baseFile.conditionalValuesPerScopeItem.value(src)) {
+ replaceConditionScopes(valueWithAlternatives, dst);
+ }
+}
+
+void ItemReaderASTVisitor::ensureIdScope(const FileContextPtr &file)
+{
+ if (!file->m_idScope) {
+ file->m_idScope = Item::create(m_reader->m_pool);
+ file->m_idScope->m_typeName = QLatin1String("IdScope");
+ }
+}
+
+void ItemReaderASTVisitor::setupAlternatives(Item *item)
+{
+ QList<Item *>::iterator it = item->m_children.begin();
+ while (it != item->m_children.end()) {
+ Item *child = *it;
+ if (child->typeName() == QLatin1String("Properties")) {
+ handlePropertiesBlock(item, child);
+ it = item->m_children.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+void ItemReaderASTVisitor::replaceConditionScopes(const JSSourceValuePtr &value,
+ Item *newScope)
+{
+ for (QList<JSSourceValue::Alternative>::iterator it
+ = value->m_alternatives.begin(); it != value->m_alternatives.end(); ++it)
+ it->conditionScopeItem = newScope;
+}
+
+class PropertiesBlockConverter
+{
+public:
+ PropertiesBlockConverter(const QString &condition, Item *propertiesBlockContainer,
+ const Item *propertiesBlock,
+ QSet<JSSourceValuePtr> *valuesWithAlternatives)
+ : m_propertiesBlockContainer(propertiesBlockContainer)
+ , m_propertiesBlock(propertiesBlock)
+ , m_valuesWithAlternatives(valuesWithAlternatives)
+ {
+ m_alternative.condition = condition;
+ m_alternative.conditionScopeItem = propertiesBlockContainer;
+ }
+
+ void operator()()
+ {
+ apply(m_propertiesBlockContainer, m_propertiesBlock);
+ }
+
+private:
+ JSSourceValue::Alternative m_alternative;
+ Item *m_propertiesBlockContainer;
+ const Item *m_propertiesBlock;
+ QSet<JSSourceValuePtr> *m_valuesWithAlternatives;
+
+ void apply(Item *a, const Item *b)
+ {
+ for (QMap<QString, ValuePtr>::const_iterator it = b->properties().constBegin();
+ it != b->properties().constEnd(); ++it) {
+ if (b == m_propertiesBlock && it.key() == QLatin1String("condition"))
+ continue;
+ if (it.value()->type() == Value::ItemValueType) {
+ apply(a->itemProperty(it.key(), true)->item(),
+ it.value().staticCast<ItemValue>()->item());
+ } else if (it.value()->type() == Value::JSSourceValueType) {
+ ValuePtr aval = a->property(it.key());
+ if (Q_UNLIKELY(aval && aval->type() != Value::JSSourceValueType))
+ throw ErrorInfo(Tr::tr("Incompatible value type in unconditional value at %1.").arg(
+ aval->location().toString()));
+ apply(it.key(), a, aval.staticCast<JSSourceValue>(),
+ it.value().staticCast<JSSourceValue>());
+ } else {
+ QBS_CHECK(!"Unexpected value type in conditional value.");
+ }
+ }
+ }
+
+ void apply(const QString &propertyName, Item *item, JSSourceValuePtr value,
+ const JSSourceValuePtr &conditionalValue)
+ {
+ QBS_ASSERT(!value || value->file() == conditionalValue->file(), return);
+ if (!value) {
+ value = JSSourceValue::create();
+ value->setFile(conditionalValue->file());
+ item->setProperty(propertyName, value);
+ value->setSourceCode(QLatin1String("undefined"));
+ }
+ m_alternative.value = conditionalValue;
+ value->addAlternative(m_alternative);
+ m_valuesWithAlternatives->insert(value);
+ }
+};
+
+void ItemReaderASTVisitor::handlePropertiesBlock(Item *item, const Item *block)
+{
+ ValuePtr value = block->property(QLatin1String("condition"));
+ if (Q_UNLIKELY(!value))
+ throw ErrorInfo(Tr::tr("Properties.condition must be provided."),
+ block->location());
+ if (Q_UNLIKELY(value->type() != Value::JSSourceValueType))
+ throw ErrorInfo(Tr::tr("Properties.condition must be a value binding."),
+ block->location());
+ JSSourceValuePtr srcval = value.staticCast<JSSourceValue>();
+ const QString condition = srcval->sourceCode();
+ PropertiesBlockConverter convertBlock(condition, item, block,
+ &m_readerResult->conditionalValuesPerScopeItem[item]);
+ convertBlock();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/itemreaderastvisitor.h b/src/lib/corelib/language/itemreaderastvisitor.h
new file mode 100644
index 000000000..409b5104e
--- /dev/null
+++ b/src/lib/corelib/language/itemreaderastvisitor.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ITEMREADERASTVISITOR_H
+#define QBS_ITEMREADERASTVISITOR_H
+
+#include "importversion.h"
+#include "item.h"
+#include "filecontext.h"
+#include <parser/qmljsastvisitor_p.h>
+#include <QHash>
+
+namespace qbs {
+namespace Internal {
+
+class ItemReader;
+struct ItemReaderResult;
+
+class ItemReaderASTVisitor : public QbsQmlJS::AST::Visitor
+{
+public:
+ ItemReaderASTVisitor(ItemReader *reader, ItemReaderResult *result);
+ ~ItemReaderASTVisitor();
+
+ void setFilePath(const QString &filePath) { m_filePath = filePath; }
+ void setSourceCode(const QString &sourceCode) { m_sourceCode = sourceCode; }
+
+ bool visit(QbsQmlJS::AST::UiProgram *ast);
+ bool visit(QbsQmlJS::AST::UiImportList *uiImportList);
+ bool visit(QbsQmlJS::AST::UiObjectDefinition *ast);
+ bool visit(QbsQmlJS::AST::UiPublicMember *ast);
+ bool visit(QbsQmlJS::AST::UiScriptBinding *ast);
+ bool visit(QbsQmlJS::AST::FunctionDeclaration *ast);
+
+private:
+ bool visitStatement(QbsQmlJS::AST::Statement *statement);
+ CodeLocation toCodeLocation(QbsQmlJS::AST::SourceLocation location) const;
+ void checkDuplicateBinding(Item *item, const QStringList &bindingName,
+ const QbsQmlJS::AST::SourceLocation &sourceLocation);
+ Item *targetItemForBinding(Item *item, const QStringList &binding,
+ const CodeLocation &bindingLocation);
+ void checkImportVersion(const QbsQmlJS::AST::SourceLocation &versionToken) const;
+ static void mergeItem(Item *dst, const Item *src,
+ const ItemReaderResult &baseFile);
+ void ensureIdScope(const FileContextPtr &file);
+ void setupAlternatives(Item *item);
+ static void replaceConditionScopes(const JSSourceValuePtr &value, Item *newScope);
+ void handlePropertiesBlock(Item *item, const Item *block);
+ void collectPrototypes(const QString &path, const QString &as);
+ bool addPrototype(const QString &fileName, const QString &filePath, const QString &as,
+ bool needsCheck);
+
+ ItemReader *m_reader;
+ ItemReaderResult *m_readerResult;
+ const ImportVersion m_languageVersion;
+ QString m_filePath;
+ QString m_sourceCode;
+ FileContextPtr m_file;
+ QHash<QStringList, QString> m_typeNameToFile;
+ Item *m_item;
+ JSSourceValuePtr m_sourceValue;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ITEMREADERASTVISITOR_H
diff --git a/src/lib/corelib/language/jsimports.h b/src/lib/corelib/language/jsimports.h
new file mode 100644
index 000000000..4e0ef9130
--- /dev/null
+++ b/src/lib/corelib/language/jsimports.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_JSIMPORTS_H
+#define QBS_JSIMPORTS_H
+
+#include <tools/codelocation.h>
+#include <QSet>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+
+/**
+ * Represents JavaScript import of the form
+ * import 'fileOrDirectory' as scopeName
+ *
+ * There can be several filenames per scope
+ * if we import a whole directory.
+ */
+class JsImport
+{
+public:
+ QString scopeName;
+ QStringList fileNames;
+ CodeLocation location;
+};
+
+typedef QList<JsImport> JsImports;
+
+inline bool operator==(const JsImport &jsi1, const JsImport &jsi2)
+{
+ return jsi1.scopeName == jsi2.scopeName && jsi1.fileNames.toSet() == jsi2.fileNames.toSet();
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_JSIMPORTS_H
diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp
new file mode 100644
index 000000000..92622787f
--- /dev/null
+++ b/src/lib/corelib/language/language.cpp
@@ -0,0 +1,1124 @@
+/****************************************************************************
+**
+** 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 "language.h"
+
+#include "artifactproperties.h"
+#include "scriptengine.h"
+#include <buildgraph/artifact.h>
+#include <buildgraph/productbuilddata.h>
+#include <buildgraph/projectbuilddata.h>
+#include <buildgraph/rulegraph.h> // TODO: Move to language?
+#include <jsextensions/jsextensions.h>
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+#include <tools/error.h>
+#include <tools/propertyfinder.h>
+#include <tools/persistence.h>
+#include <tools/qbsassert.h>
+
+#include <QDir>
+#include <QDirIterator>
+#include <QMap>
+#include <QMutexLocker>
+#include <QScriptValue>
+
+QT_BEGIN_NAMESPACE
+inline QDataStream& operator>>(QDataStream &stream, qbs::Internal::JsImport &jsImport)
+{
+ stream >> jsImport.scopeName
+ >> jsImport.fileNames
+ >> jsImport.location;
+ return stream;
+}
+
+inline QDataStream& operator<<(QDataStream &stream, const qbs::Internal::JsImport &jsImport)
+{
+ return stream << jsImport.scopeName
+ << jsImport.fileNames
+ << jsImport.location;
+}
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+FileTagger::FileTagger(const QStringList &patterns, const FileTags &fileTags)
+ : m_fileTags(fileTags)
+{
+ setPatterns(patterns);
+}
+
+void FileTagger::setPatterns(const QStringList &patterns)
+{
+ m_patterns.clear();
+ foreach (const QString &pattern, patterns) {
+ QBS_CHECK(!pattern.isEmpty());
+ m_patterns << QRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard);
+ }
+}
+
+/*!
+ * \class FileTagger
+ * \brief The \c FileTagger class maps 1:1 to the respective item in a qbs source file.
+ */
+void FileTagger::load(PersistentPool &pool)
+{
+ setPatterns(pool.idLoadStringList());
+ pool.stream() >> m_fileTags;
+}
+
+void FileTagger::store(PersistentPool &pool) const
+{
+ QStringList patterns;
+ foreach (const QRegExp &regExp, m_patterns)
+ patterns << regExp.pattern();
+ pool.storeStringList(patterns);
+ pool.stream() << m_fileTags;
+}
+
+/*!
+ * \class SourceArtifact
+ * \brief The \c SourceArtifact class represents a source file.
+ * Everything except the file path is inherited from the surrounding \c ResolvedGroup.
+ * (TODO: Not quite true. Artifacts in transformers will be generated by the transformer, but are
+ * still represented as source artifacts. We may or may not want to change this; if we do,
+ * SourceArtifact could simply have a back pointer to the group in addition to the file path.)
+ * \sa ResolvedGroup
+ */
+void SourceArtifact::load(PersistentPool &pool)
+{
+ pool.stream() >> absoluteFilePath;
+ pool.stream() >> fileTags;
+ pool.stream() >> overrideFileTags;
+ properties = pool.idLoadS<PropertyMapInternal>();
+}
+
+void SourceArtifact::store(PersistentPool &pool) const
+{
+ pool.stream() << absoluteFilePath;
+ pool.stream() << fileTags;
+ pool.stream() << overrideFileTags;
+ pool.store(properties);
+}
+
+void SourceWildCards::load(PersistentPool &pool)
+{
+ prefix = pool.idLoadString();
+ patterns = pool.idLoadStringList();
+ excludePatterns = pool.idLoadStringList();
+ pool.loadContainerS(files);
+}
+
+void SourceWildCards::store(PersistentPool &pool) const
+{
+ pool.storeString(prefix);
+ pool.storeStringList(patterns);
+ pool.storeStringList(excludePatterns);
+ pool.storeContainer(files);
+}
+
+/*!
+ * \class ResolvedGroup
+ * \brief The \c ResolvedGroup class corresponds to the Group item in a qbs source file.
+ */
+
+ /*!
+ * \variable ResolvedGroup::files
+ * \brief The files listed in the group item's "files" binding.
+ * Note that these do not include expanded wildcards.
+ */
+
+/*!
+ * \variable ResolvedGroup::wildcards
+ * \brief Represents the wildcard elements in this group's "files" binding.
+ * If no wildcards are specified there, this variable is null.
+ * \sa SourceWildCards
+ */
+
+/*!
+ * \brief Returns all files specified in the group item as source artifacts.
+ * This includes the expanded list of wildcards.
+ */
+QList<SourceArtifactPtr> ResolvedGroup::allFiles() const
+{
+ QList<SourceArtifactPtr> lst = files;
+ if (wildcards)
+ lst.append(wildcards->files);
+ return lst;
+}
+
+void ResolvedGroup::load(PersistentPool &pool)
+{
+ name = pool.idLoadString();
+ pool.stream()
+ >> enabled
+ >> location;
+ prefix = pool.idLoadString();
+ pool.loadContainerS(files);
+ wildcards = pool.idLoadS<SourceWildCards>();
+ properties = pool.idLoadS<PropertyMapInternal>();
+ pool.stream()
+ >> fileTags
+ >> overrideTags;
+}
+
+void ResolvedGroup::store(PersistentPool &pool) const
+{
+ pool.storeString(name);
+ pool.stream()
+ << enabled
+ << location;
+ pool.storeString(prefix);
+ pool.storeContainer(files);
+ pool.store(wildcards);
+ pool.store(properties);
+ pool.stream()
+ << fileTags
+ << overrideTags;
+}
+
+/*!
+ * \class RuleArtifact
+ * \brief The \c RuleArtifact class represents an Artifact item encountered in the context
+ * of a Rule item.
+ * When applying the rule, one \c Artifact object will be constructed from each \c RuleArtifact
+ * object. During that process, the \c RuleArtifact's bindings are evaluated and the results
+ * are inserted into the corresponding \c Artifact's properties.
+ * \sa Rule
+ */
+void RuleArtifact::load(PersistentPool &pool)
+{
+ Q_UNUSED(pool);
+ pool.stream()
+ >> fileName
+ >> fileTags
+ >> alwaysUpdated;
+
+ int i;
+ pool.stream() >> i;
+ bindings.clear();
+ bindings.reserve(i);
+ Binding binding;
+ for (; --i >= 0;) {
+ pool.stream() >> binding.name >> binding.code >> binding.location;
+ bindings += binding;
+ }
+}
+
+void RuleArtifact::store(PersistentPool &pool) const
+{
+ Q_UNUSED(pool);
+ pool.stream()
+ << fileName
+ << fileTags
+ << alwaysUpdated;
+
+ pool.stream() << bindings.count();
+ for (int i = bindings.count(); --i >= 0;) {
+ const Binding &binding = bindings.at(i);
+ pool.stream() << binding.name << binding.code << binding.location;
+ }
+}
+
+void ResolvedFileContext::load(PersistentPool &pool)
+{
+ filePath = pool.idLoadString();
+ jsExtensions = pool.idLoadStringList();
+ pool.stream() >> jsImports;
+}
+
+void ResolvedFileContext::store(PersistentPool &pool) const
+{
+ pool.storeString(filePath);
+ pool.storeStringList(jsExtensions);
+ pool.stream() << jsImports;
+}
+
+bool operator==(const ResolvedFileContext &a, const ResolvedFileContext &b)
+{
+ if (&a == &b)
+ return true;
+ if (!!&a != !!&b)
+ return false;
+ return a.filePath == b.filePath
+ && a.jsExtensions == b.jsExtensions
+ && a.jsImports == b.jsImports;
+}
+
+
+/*!
+ * \class ScriptFunction
+ * \brief The \c ScriptFunction class represents the JavaScript code found in the "prepare" binding
+ * of a \c Rule or \c Transformer item in a qbs file.
+ * \sa Rule
+ * \sa ResolvedTransformer
+ */
+
+ /*!
+ * \variable ScriptFunction::script
+ * \brief The actual Javascript code, taken verbatim from the qbs source file.
+ */
+
+ /*!
+ * \variable ScriptFunction::location
+ * \brief The exact location of the script in the qbs source file.
+ * This is mostly needed for diagnostics.
+ */
+
+void ScriptFunction::load(PersistentPool &pool)
+{
+ pool.stream()
+ >> sourceCode
+ >> argumentNames
+ >> location;
+ fileContext = pool.idLoadS<ResolvedFileContext>();
+}
+
+void ScriptFunction::store(PersistentPool &pool) const
+{
+ pool.stream()
+ << sourceCode
+ << argumentNames
+ << location;
+ pool.store(fileContext);
+}
+
+bool operator==(const ScriptFunction &a, const ScriptFunction &b)
+{
+ if (&a == &b)
+ return true;
+ if (!!&a != !!&b)
+ return false;
+ return a.sourceCode == b.sourceCode
+ && a.location == b.location
+ && *a.fileContext == *b.fileContext;
+}
+
+void ResolvedModule::load(PersistentPool &pool)
+{
+ name = pool.idLoadString();
+ moduleDependencies = pool.idLoadStringList();
+ setupBuildEnvironmentScript = pool.idLoadS<ScriptFunction>();
+ setupRunEnvironmentScript = pool.idLoadS<ScriptFunction>();
+}
+
+void ResolvedModule::store(PersistentPool &pool) const
+{
+ pool.storeString(name);
+ pool.storeStringList(moduleDependencies);
+ pool.store(setupBuildEnvironmentScript);
+ pool.store(setupRunEnvironmentScript);
+}
+
+bool operator==(const ResolvedModule &m1, const ResolvedModule &m2)
+{
+ if (&m1 == &m2)
+ return true;
+ if (!!&m1 != !!&m2)
+ return false;
+ return m1.name == m2.name
+ && m1.moduleDependencies.toSet() == m2.moduleDependencies.toSet()
+ && *m1.setupBuildEnvironmentScript == *m2.setupBuildEnvironmentScript
+ && *m1.setupRunEnvironmentScript == *m2.setupRunEnvironmentScript;
+}
+
+static bool modulesAreEqual(const ResolvedModuleConstPtr &m1, const ResolvedModuleConstPtr &m2)
+{
+ return *m1 == *m2;
+}
+
+QString Rule::toString() const
+{
+ return QLatin1Char('[') + inputs.toStringList().join(QLatin1String(",")) + QLatin1String(" -> ")
+ + outputFileTags().toStringList().join(QLatin1String(",")) + QLatin1Char(']');
+}
+
+FileTags Rule::outputFileTags() const
+{
+ FileTags result;
+ foreach (const RuleArtifactConstPtr &artifact, artifacts)
+ result.unite(artifact->fileTags);
+ return result;
+}
+
+void Rule::load(PersistentPool &pool)
+{
+ script = pool.idLoadS<ScriptFunction>();
+ module = pool.idLoadS<ResolvedModule>();
+ pool.stream()
+ >> inputs
+ >> auxiliaryInputs
+ >> usings
+ >> explicitlyDependsOn
+ >> multiplex;
+
+ pool.loadContainerS(artifacts);
+}
+
+void Rule::store(PersistentPool &pool) const
+{
+ pool.store(script);
+ pool.store(module);
+ pool.stream()
+ << inputs
+ << auxiliaryInputs
+ << usings
+ << explicitlyDependsOn
+ << multiplex;
+
+ pool.storeContainer(artifacts);
+}
+
+ResolvedProduct::ResolvedProduct()
+ : enabled(true)
+{
+}
+
+ResolvedProduct::~ResolvedProduct()
+{
+}
+
+/*!
+ * \brief Returns all files of all groups as source artifacts.
+ * This includes the expanded list of wildcards.
+ */
+QList<SourceArtifactPtr> ResolvedProduct::allFiles() const
+{
+ QList<SourceArtifactPtr> lst;
+ foreach (const GroupConstPtr &group, groups)
+ lst += group->allFiles();
+ return lst;
+}
+
+/*!
+ * \brief Returns all files of all enabled groups as source artifacts.
+ * \sa ResolvedProduct::allFiles()
+ */
+QList<SourceArtifactPtr> ResolvedProduct::allEnabledFiles() const
+{
+ QList<SourceArtifactPtr> lst;
+ foreach (const GroupConstPtr &group, groups) {
+ if (group->enabled)
+ lst += group->allFiles();
+ }
+ return lst;
+}
+
+FileTags ResolvedProduct::fileTagsForFileName(const QString &fileName) const
+{
+ FileTags result;
+ foreach (FileTaggerConstPtr tagger, fileTaggers) {
+ foreach (const QRegExp &pattern, tagger->patterns()) {
+ if (FileInfo::globMatches(pattern, fileName)) {
+ result.unite(tagger->fileTags());
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+void ResolvedProduct::load(PersistentPool &pool)
+{
+ pool.stream()
+ >> enabled
+ >> fileTags
+ >> additionalFileTags
+ >> name
+ >> targetName
+ >> sourceDirectory
+ >> destinationDirectory
+ >> location;
+ properties = pool.idLoadS<PropertyMapInternal>();
+ pool.loadContainerS(rules);
+ pool.loadContainerS(dependencies);
+ pool.loadContainerS(fileTaggers);
+ pool.loadContainerS(modules);
+ pool.loadContainerS(transformers);
+ pool.loadContainerS(groups);
+ pool.loadContainerS(artifactProperties);
+ buildData.reset(pool.idLoad<ProductBuildData>());
+}
+
+void ResolvedProduct::store(PersistentPool &pool) const
+{
+ pool.stream()
+ << enabled
+ << fileTags
+ << additionalFileTags
+ << name
+ << targetName
+ << sourceDirectory
+ << destinationDirectory
+ << location;
+
+ pool.store(properties);
+ pool.storeContainer(rules);
+ pool.storeContainer(dependencies);
+ pool.storeContainer(fileTaggers);
+ pool.storeContainer(modules);
+ pool.storeContainer(transformers);
+ pool.storeContainer(groups);
+ pool.storeContainer(artifactProperties);
+ pool.store(buildData.data());
+}
+
+QList<const ResolvedModule*> topSortModules(const QHash<const ResolvedModule*, QList<const ResolvedModule*> > &moduleChildren,
+ const QList<const ResolvedModule*> &modules,
+ QSet<QString> &seenModuleNames)
+{
+ QList<const ResolvedModule*> result;
+ foreach (const ResolvedModule *m, modules) {
+ if (m->name.isNull())
+ continue;
+ result.append(topSortModules(moduleChildren, moduleChildren.value(m), seenModuleNames));
+ if (!seenModuleNames.contains(m->name)) {
+ seenModuleNames.insert(m->name);
+ result.append(m);
+ }
+ }
+ return result;
+}
+
+static QScriptValue js_getEnv(QScriptContext *context, QScriptEngine *engine)
+{
+ if (Q_UNLIKELY(context->argumentCount() < 1))
+ return context->throwError(QScriptContext::SyntaxError,
+ QLatin1String("getEnv expects 1 argument"));
+ QVariant v = engine->property("_qbs_procenv");
+ QProcessEnvironment *procenv = reinterpret_cast<QProcessEnvironment*>(v.value<void*>());
+ return engine->toScriptValue(procenv->value(context->argument(0).toString()));
+}
+
+static QScriptValue js_putEnv(QScriptContext *context, QScriptEngine *engine)
+{
+ if (Q_UNLIKELY(context->argumentCount() < 2))
+ return context->throwError(QScriptContext::SyntaxError,
+ QLatin1String("putEnv expects 2 arguments"));
+ QVariant v = engine->property("_qbs_procenv");
+ QProcessEnvironment *procenv = reinterpret_cast<QProcessEnvironment*>(v.value<void*>());
+ procenv->insert(context->argument(0).toString(), context->argument(1).toString());
+ return engine->undefinedValue();
+}
+
+enum EnvType
+{
+ BuildEnv, RunEnv
+};
+
+static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType envType,
+ const QList<ResolvedModuleConstPtr> &modules,
+ const PropertyMapConstPtr &productConfiguration,
+ TopLevelProject *project,
+ const QProcessEnvironment &env)
+{
+ QProcessEnvironment procenv = env;
+
+ // Copy the environment of the platform configuration to the process environment.
+ const QVariantMap &platformEnv = project->platformEnvironment;
+ for (QVariantMap::const_iterator it = platformEnv.constBegin(); it != platformEnv.constEnd(); ++it)
+ procenv.insert(it.key(), it.value().toString());
+
+ QMap<QString, const ResolvedModule *> moduleMap;
+ foreach (const ResolvedModuleConstPtr &module, modules)
+ moduleMap.insert(module->name, module.data());
+
+ QHash<const ResolvedModule*, QList<const ResolvedModule*> > moduleParents;
+ QHash<const ResolvedModule*, QList<const ResolvedModule*> > moduleChildren;
+ foreach (ResolvedModuleConstPtr module, modules) {
+ foreach (const QString &moduleName, module->moduleDependencies) {
+ const ResolvedModule * const depmod = moduleMap.value(moduleName);
+ QBS_ASSERT(depmod, return env);
+ moduleParents[depmod].append(module.data());
+ moduleChildren[module.data()].append(depmod);
+ }
+ }
+
+ QList<const ResolvedModule *> rootModules;
+ foreach (ResolvedModuleConstPtr module, modules) {
+ if (moduleParents.value(module.data()).isEmpty()) {
+ QBS_ASSERT(module, return env);
+ rootModules.append(module.data());
+ }
+ }
+
+ {
+ QVariant v;
+ v.setValue<void*>(&procenv);
+ engine->setProperty("_qbs_procenv", v);
+ }
+
+ engine->clearImportsCache();
+ QScriptValue scope = engine->newObject();
+
+ const QScriptValue getEnvValue = engine->newFunction(js_getEnv, 1);
+ const QScriptValue putEnvValue = engine->newFunction(js_putEnv, 1);
+
+ // TODO: Remove in 1.3
+ scope.setProperty("getenv", getEnvValue);
+ scope.setProperty("putenv", putEnvValue);
+
+ scope.setProperty("getEnv", getEnvValue);
+ scope.setProperty("putEnv", putEnvValue);
+
+ QSet<QString> seenModuleNames;
+ QList<const ResolvedModule *> topSortedModules = topSortModules(moduleChildren, rootModules, seenModuleNames);
+ foreach (const ResolvedModule *module, topSortedModules) {
+ if ((envType == BuildEnv && module->setupBuildEnvironmentScript->sourceCode.isEmpty()) ||
+ (envType == RunEnv && module->setupBuildEnvironmentScript->sourceCode.isEmpty()
+ && module->setupRunEnvironmentScript->sourceCode.isEmpty()))
+ continue;
+
+ ScriptFunctionConstPtr setupScript;
+ if (envType == BuildEnv) {
+ setupScript = module->setupBuildEnvironmentScript;
+ } else {
+ if (!module->setupRunEnvironmentScript)
+ setupScript = module->setupBuildEnvironmentScript;
+ else
+ setupScript = module->setupRunEnvironmentScript;
+ }
+
+ // handle imports
+ engine->import(setupScript->fileContext->jsImports, scope, scope);
+ JsExtensions::setupExtensions(setupScript->fileContext->jsExtensions, scope);
+
+ // expose properties of direct module dependencies
+ QScriptValue scriptValue;
+ QVariantMap productModules = productConfiguration->value().value("modules").toMap();
+ foreach (const ResolvedModule * const depmod, moduleChildren.value(module)) {
+ scriptValue = engine->newObject();
+ QVariantMap moduleCfg = productModules.value(depmod->name).toMap();
+ for (QVariantMap::const_iterator it = moduleCfg.constBegin(); it != moduleCfg.constEnd(); ++it)
+ scriptValue.setProperty(it.key(), engine->toScriptValue(it.value()));
+ scope.setProperty(depmod->name, scriptValue);
+ }
+
+ // expose the module's properties
+ QVariantMap moduleCfg = productModules.value(module->name).toMap();
+ for (QVariantMap::const_iterator it = moduleCfg.constBegin(); it != moduleCfg.constEnd(); ++it)
+ scope.setProperty(it.key(), engine->toScriptValue(it.value()));
+
+ QScriptContext *ctx = engine->currentContext();
+ ctx->pushScope(scope);
+ scriptValue = engine->evaluate(setupScript->sourceCode + QLatin1String("()"));
+ ctx->popScope();
+ if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue))) {
+ QString envTypeStr = (envType == BuildEnv ? "build" : "run");
+ throw ErrorInfo(QString("Error while setting up %1 environment: %2").arg(envTypeStr, scriptValue.toString()));
+ }
+ }
+
+ engine->setProperty("_qbs_procenv", QVariant());
+ return procenv;
+}
+
+void ResolvedProduct::setupBuildEnvironment(ScriptEngine *engine, const QProcessEnvironment &env) const
+{
+ if (!buildEnvironment.isEmpty())
+ return;
+
+ buildEnvironment = getProcessEnvironment(engine, BuildEnv, modules, properties,
+ topLevelProject(), env);
+}
+
+void ResolvedProduct::setupRunEnvironment(ScriptEngine *engine, const QProcessEnvironment &env) const
+{
+ if (!runEnvironment.isEmpty())
+ return;
+
+ runEnvironment = getProcessEnvironment(engine, RunEnv, modules, properties,
+ topLevelProject(), env);
+}
+
+const QList<RuleConstPtr> &ResolvedProduct::topSortedRules() const
+{
+ QBS_CHECK(buildData);
+ if (buildData->topSortedRules.isEmpty()) {
+ FileTags productFileTags = fileTags;
+ productFileTags += additionalFileTags;
+ RuleGraph ruleGraph;
+ ruleGraph.build(rules, productFileTags);
+// ruleGraph.dump();
+ buildData->topSortedRules = ruleGraph.topSorted();
+// int i=0;
+// foreach (RulePtr r, m_topSortedRules)
+// qDebug() << ++i << r->toString() << (void*)r.data();
+ }
+ return buildData->topSortedRules;
+}
+
+TopLevelProject *ResolvedProduct::topLevelProject() const
+{
+ return project->topLevelProject();
+}
+
+static QStringList findGeneratedFiles(const Artifact *base, const FileTags &tags)
+{
+ QStringList result;
+ foreach (const Artifact *parent, base->parents) {
+ if (tags.isEmpty() || parent->fileTags.matches(tags))
+ result << parent->filePath();
+ }
+
+ if (result.isEmpty() || tags.isEmpty())
+ foreach (const Artifact *parent, base->parents)
+ result << findGeneratedFiles(parent, tags);
+
+ return result;
+}
+
+QStringList ResolvedProduct::generatedFiles(const QString &baseFile, const FileTags &tags) const
+{
+ ProductBuildData *data = buildData.data();
+ if (!data)
+ return QStringList();
+
+ foreach (const Artifact *art, data->artifacts) {
+ if (art->filePath() == baseFile)
+ return findGeneratedFiles(art, tags);
+ }
+ return QStringList();
+}
+
+ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(0)
+{
+}
+
+TopLevelProject *ResolvedProject::topLevelProject()
+{
+ if (m_topLevelProject)
+ return m_topLevelProject;
+ TopLevelProject *tlp = dynamic_cast<TopLevelProject *>(this);
+ if (tlp) {
+ m_topLevelProject = tlp;
+ return m_topLevelProject;
+ }
+ QBS_CHECK(!parentProject.isNull());
+ m_topLevelProject = parentProject->topLevelProject();
+ return m_topLevelProject;
+}
+
+QList<ResolvedProjectPtr> ResolvedProject::allSubProjects() const
+{
+ QList<ResolvedProjectPtr> projectList = subProjects;
+ foreach (const ResolvedProjectConstPtr &subProject, subProjects)
+ projectList << subProject->allSubProjects();
+ return projectList;
+}
+
+QList<ResolvedProductPtr> ResolvedProject::allProducts() const
+{
+ QList<ResolvedProductPtr> productList = products;
+ foreach (const ResolvedProjectConstPtr &subProject, subProjects)
+ productList << subProject->allProducts();
+ return productList;
+}
+
+void ResolvedProject::load(PersistentPool &pool)
+{
+ name = pool.idLoadString();
+ int count;
+ pool.stream()
+ >> location
+ >> enabled
+ >> count;
+ products.clear();
+ products.reserve(count);
+ for (; --count >= 0;) {
+ ResolvedProductPtr rProduct = pool.idLoadS<ResolvedProduct>();
+ if (rProduct->buildData) {
+ foreach (Artifact * const a, rProduct->buildData->artifacts)
+ a->product = rProduct;
+ }
+ products.append(rProduct);
+ }
+
+ pool.stream() >> count;
+ subProjects.clear();
+ subProjects.reserve(count);
+ for (; --count >= 0;) {
+ ResolvedProjectPtr p = pool.idLoadS<ResolvedProject>();
+ subProjects.append(p);
+ }
+
+ pool.stream() >> m_projectProperties;
+}
+
+void ResolvedProject::store(PersistentPool &pool) const
+{
+ pool.storeString(name);
+ pool.stream()
+ << location
+ << enabled
+ << products.count();
+ foreach (const ResolvedProductConstPtr &product, products)
+ pool.store(product);
+ pool.stream() << subProjects.count();
+ foreach (const ResolvedProjectConstPtr &project, subProjects)
+ pool.store(project);
+ pool.stream() << m_projectProperties;
+}
+
+
+TopLevelProject::TopLevelProject() : locked(false)
+{
+}
+
+TopLevelProject::~TopLevelProject()
+{
+}
+
+QString TopLevelProject::deriveId(const QVariantMap &config)
+{
+ const QVariantMap qbsProperties = config.value(QLatin1String("qbs")).toMap();
+ const QString buildVariant = qbsProperties.value(QLatin1String("buildVariant")).toString();
+ const QString profile = qbsProperties.value(QLatin1String("profile")).toString();
+ return profile + QLatin1Char('-') + buildVariant;
+}
+
+QString TopLevelProject::deriveBuildDirectory(const QString &buildRoot, const QString &id)
+{
+ return buildRoot + QLatin1Char('/') + id;
+}
+
+void TopLevelProject::setBuildConfiguration(const QVariantMap &config)
+{
+ m_buildConfiguration = config;
+ m_id = deriveId(config);
+}
+
+QString TopLevelProject::buildGraphFilePath() const
+{
+ return ProjectBuildData::deriveBuildGraphFilePath(buildDirectory, id());
+}
+
+void TopLevelProject::store(const Logger &logger) const
+{
+ // TODO: Use progress observer here.
+
+ if (!buildData)
+ return;
+ if (!buildData->isDirty) {
+ logger.qbsDebug() << "[BG] build graph is unchanged in project " << id() << ".";
+ return;
+ }
+ const QString fileName = buildGraphFilePath();
+ logger.qbsDebug() << "[BG] storing: " << fileName;
+ PersistentPool pool(logger);
+ PersistentPool::HeadData headData;
+ headData.projectConfig = buildConfiguration();
+ pool.setHeadData(headData);
+ pool.setupWriteStream(fileName);
+ store(pool);
+ buildData->isDirty = false;
+}
+
+void TopLevelProject::load(PersistentPool &pool)
+{
+ ResolvedProject::load(pool);
+ pool.stream() >> m_id;
+ pool.stream() >> platformEnvironment;
+ pool.stream() >> usedEnvironment;
+ pool.stream() >> fileExistsResults;
+ pool.stream() >> fileLastModifiedResults;
+ QHash<QString, QString> envHash;
+ pool.stream() >> envHash;
+ for (QHash<QString, QString>::const_iterator i = envHash.begin(); i != envHash.end(); ++i)
+ environment.insert(i.key(), i.value());
+ pool.stream() >> buildSystemFiles;
+ buildData.reset(pool.idLoad<ProjectBuildData>());
+ QBS_CHECK(buildData);
+ buildData->isDirty = false;
+}
+
+void TopLevelProject::store(PersistentPool &pool) const
+{
+ ResolvedProject::store(pool);
+ pool.stream() << m_id;
+ pool.stream() << platformEnvironment << usedEnvironment << fileExistsResults
+ << fileLastModifiedResults;
+ QHash<QString, QString> envHash;
+ foreach (const QString &key, environment.keys())
+ envHash.insert(key, environment.value(key));
+ pool.stream() << envHash;
+ pool.stream() << buildSystemFiles;
+ pool.store(buildData.data());
+}
+
+/*!
+ * \class SourceWildCards
+ * \brief Objects of the \c SourceWildCards class result from giving wildcards in a
+ * \c ResolvedGroup's "files" binding.
+ * \sa ResolvedGroup
+ */
+
+/*!
+ * \variable SourceWildCards::prefix
+ * \brief Inherited from the \c ResolvedGroup
+ * \sa ResolvedGroup
+ */
+
+/*!
+ * \variable SourceWildCards::patterns
+ * \brief All elements of the \c ResolvedGroup's "files" binding that contain wildcards.
+ * \sa ResolvedGroup
+ */
+
+/*!
+ * \variable SourceWildCards::excludePatterns
+ * \brief Corresponds to the \c ResolvedGroup's "excludeFiles" binding.
+ * \sa ResolvedGroup
+ */
+
+/*!
+ * \variable SourceWildCards::files
+ * \brief The \c SourceArtifacts resulting from the expanded list of matching files.
+ */
+
+QSet<QString> SourceWildCards::expandPatterns(const GroupConstPtr &group,
+ const QString &baseDir) const
+{
+ QSet<QString> files = expandPatterns(group, patterns, baseDir);
+ files -= expandPatterns(group, excludePatterns, baseDir);
+ return files;
+}
+
+QSet<QString> SourceWildCards::expandPatterns(const GroupConstPtr &group,
+ const QStringList &patterns, const QString &baseDir) const
+{
+ QSet<QString> files;
+ foreach (QString pattern, patterns) {
+ pattern.prepend(prefix);
+ pattern.replace('\\', '/');
+ QStringList parts = pattern.split(QLatin1Char('/'), QString::SkipEmptyParts);
+ if (FileInfo::isAbsolute(pattern)) {
+ QString rootDir;
+ if (HostOsInfo::isWindowsHost()) {
+ rootDir = parts.takeFirst();
+ if (!rootDir.endsWith(QLatin1Char('/')))
+ rootDir.append(QLatin1Char('/'));
+ } else {
+ rootDir = QLatin1Char('/');
+ }
+ expandPatterns(files, group, parts, rootDir);
+ } else {
+ expandPatterns(files, group, parts, baseDir);
+ }
+ }
+
+ return files;
+}
+
+void SourceWildCards::expandPatterns(QSet<QString> &result, const GroupConstPtr &group,
+ const QStringList &parts,
+ const QString &baseDir) const
+{
+ QStringList changed_parts = parts;
+ bool recursive = false;
+ QString part = changed_parts.takeFirst();
+
+ while (part == QLatin1String("**")) {
+ recursive = true;
+
+ if (changed_parts.isEmpty()) {
+ part = QLatin1String("*");
+ break;
+ }
+
+ part = changed_parts.takeFirst();
+ }
+
+ const bool isDir = !changed_parts.isEmpty();
+
+ const QString &filePattern = part;
+ const QDirIterator::IteratorFlags itFlags = recursive
+ ? QDirIterator::Subdirectories
+ : QDirIterator::NoIteratorFlags;
+ QDir::Filters itFilters = isDir
+ ? QDir::Dirs
+ : QDir::Files;
+
+ if (isDir && !FileInfo::isPattern(filePattern))
+ itFilters |= QDir::Hidden;
+ if (filePattern != QLatin1String("..") && filePattern != QLatin1String("."))
+ itFilters |= QDir::NoDotAndDotDot;
+
+ QDirIterator it(baseDir, QStringList(filePattern), itFilters, itFlags);
+ while (it.hasNext()) {
+ const QString filePath = it.next();
+ QBS_ASSERT(FileInfo(filePath).isDir() == isDir, break);
+ if (isDir)
+ expandPatterns(result, group, changed_parts, filePath);
+ else
+ result += QDir::cleanPath(filePath);
+ }
+}
+
+void ResolvedTransformer::load(PersistentPool &pool)
+{
+ module = pool.idLoadS<ResolvedModule>();
+ pool.stream() >> inputs;
+ pool.loadContainerS(outputs);
+ transform = pool.idLoadS<ScriptFunction>();
+ pool.stream() >> explicitlyDependsOn;
+}
+
+void ResolvedTransformer::store(PersistentPool &pool) const
+{
+ pool.store(module);
+ pool.stream() << inputs;
+ pool.storeContainer(outputs);
+ pool.store(transform);
+ pool.stream() << explicitlyDependsOn;
+}
+
+
+template<typename T> QMap<QString, T> listToMap(const QList<T> &list)
+{
+ QMap<QString, T> map;
+ foreach (const T &elem, list)
+ map.insert(keyFromElem(elem), elem);
+ return map;
+}
+
+template<typename T> bool listsAreEqual(const QList<T> &l1, const QList<T> &l2)
+{
+ if (l1.count() != l2.count())
+ return false;
+ const QMap<QString, T> map1 = listToMap(l1);
+ const QMap<QString, T> map2 = listToMap(l2);
+ foreach (const QString &key, map1.keys()) {
+ const T value2 = map2.value(key);
+ if (!value2)
+ return false;
+ if (*map1.value(key) != *value2)
+ return false;
+ }
+ return true;
+}
+
+QString keyFromElem(const SourceArtifactPtr &sa) { return sa->absoluteFilePath; }
+QString keyFromElem(const ResolvedTransformerConstPtr &t) { return t->transform->sourceCode; }
+QString keyFromElem(const RulePtr &r) { return r->toString(); }
+QString keyFromElem(const ArtifactPropertiesPtr &ap) {
+ return ap->fileTagsFilter().toStringList().join(QLatin1String(","));
+}
+
+bool operator==(const SourceArtifact &sa1, const SourceArtifact &sa2)
+{
+ if (&sa1 == &sa2)
+ return true;
+ if (!!&sa1 != !!&sa2)
+ return false;
+ return sa1.absoluteFilePath == sa2.absoluteFilePath
+ && sa1.fileTags == sa2.fileTags
+ && sa1.overrideFileTags == sa2.overrideFileTags
+ && sa1.properties->value() == sa2.properties->value();
+}
+
+bool sourceArtifactListsAreEqual(const QList<SourceArtifactPtr> &l1,
+ const QList<SourceArtifactPtr> &l2)
+{
+ return listsAreEqual(l1, l2);
+}
+
+bool operator==(const ResolvedTransformer &t1, const ResolvedTransformer &t2)
+{
+ return modulesAreEqual(t1.module, t2.module)
+ && t1.inputs.toSet() == t2.inputs.toSet()
+ && sourceArtifactListsAreEqual(t1.outputs, t2.outputs)
+ && *t1.transform == *t2.transform
+ && t1.explicitlyDependsOn == t2.explicitlyDependsOn;
+}
+
+bool transformerListsAreEqual(const QList<ResolvedTransformerConstPtr> &l1,
+ const QList<ResolvedTransformerConstPtr> &l2)
+{
+ return listsAreEqual(l1, l2);
+}
+
+bool operator==(const Rule &r1, const Rule &r2)
+{
+ if (&r1 == &r2)
+ return true;
+ if (!&r1 != !&r2)
+ return false;
+ if (r1.artifacts.count() != r2.artifacts.count())
+ return false;
+ for (int i = 0; i < r1.artifacts.count(); ++i) {
+ if (*r1.artifacts.at(i) != *r2.artifacts.at(i))
+ return false;
+ }
+
+ return r1.module->name == r2.module->name
+ && r1.script->sourceCode == r2.script->sourceCode
+ && r1.inputs == r2.inputs
+ && r1.auxiliaryInputs == r2.auxiliaryInputs
+ && r1.usings == r2.usings
+ && r1.explicitlyDependsOn == r2.explicitlyDependsOn
+ && r1.multiplex == r2.multiplex;
+}
+
+bool ruleListsAreEqual(const QList<RulePtr> &l1, const QList<RulePtr> &l2)
+{
+ return listsAreEqual(l1, l2);
+}
+
+bool operator==(const RuleArtifact &a1, const RuleArtifact &a2)
+{
+ if (&a1 == &a2)
+ return true;
+ if (!&a1 != !&a2)
+ return false;
+ return a1.fileName == a2.fileName
+ && a1.fileTags == a2.fileTags
+ && a1.alwaysUpdated == a2.alwaysUpdated
+ && a1.bindings.toList().toSet() == a2.bindings.toList().toSet();
+}
+
+bool operator==(const RuleArtifact::Binding &b1, const RuleArtifact::Binding &b2)
+{
+ return b1.code == b2.code && b1.name == b2.name;
+}
+
+uint qHash(const RuleArtifact::Binding &b)
+{
+ return qHash(qMakePair(b.code, b.name.join(QLatin1String(","))));
+}
+
+bool artifactPropertyListsAreEqual(const QList<ArtifactPropertiesPtr> &l1,
+ const QList<ArtifactPropertiesPtr> &l2)
+{
+ return listsAreEqual(l1, l2);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h
new file mode 100644
index 000000000..d359686b1
--- /dev/null
+++ b/src/lib/corelib/language/language.h
@@ -0,0 +1,464 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_LANGUAGE_H
+#define QBS_LANGUAGE_H
+
+#include "filetags.h"
+#include "forward_decls.h"
+#include "jsimports.h"
+#include "propertymapinternal.h"
+#include <buildgraph/forward_decls.h>
+#include <tools/codelocation.h>
+#include <tools/fileinfo.h>
+#include <tools/persistentobject.h>
+#include <tools/settings.h>
+#include <tools/weakpointer.h>
+
+#include <QByteArray>
+#include <QDataStream>
+#include <QHash>
+#include <QProcessEnvironment>
+#include <QRegExp>
+#include <QScriptProgram>
+#include <QScriptValue>
+#include <QScopedPointer>
+#include <QSet>
+#include <QString>
+#include <QStringList>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+class QScriptEngine;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+class BuildGraphLoader;
+
+class FileTagger : public PersistentObject
+{
+public:
+ static FileTaggerPtr create() { return FileTaggerPtr(new FileTagger); }
+ static FileTaggerPtr create(const QStringList &patterns, const FileTags &fileTags) {
+ return FileTaggerPtr(new FileTagger(patterns, fileTags));
+ }
+
+ const QList<QRegExp> &patterns() const { return m_patterns; }
+ const FileTags &fileTags() const { return m_fileTags; }
+
+private:
+ FileTagger(const QStringList &patterns, const FileTags &fileTags);
+ FileTagger() {}
+
+ void setPatterns(const QStringList &patterns);
+
+ void load(PersistentPool &);
+ void store(PersistentPool &) const;
+
+ QList<QRegExp> m_patterns;
+ FileTags m_fileTags;
+};
+
+class RuleArtifact : public PersistentObject
+{
+public:
+ static RuleArtifactPtr create() { return RuleArtifactPtr(new RuleArtifact); }
+
+ QString fileName;
+ FileTags fileTags;
+ bool alwaysUpdated;
+
+ class Binding
+ {
+ public:
+ QStringList name;
+ QString code;
+ CodeLocation location;
+ };
+
+ QVector<Binding> bindings;
+
+private:
+ RuleArtifact()
+ : alwaysUpdated(true)
+ {}
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+uint qHash(const RuleArtifact::Binding &b);
+bool operator==(const RuleArtifact::Binding &b1, const RuleArtifact::Binding &b2);
+inline bool operator!=(const RuleArtifact::Binding &b1, const RuleArtifact::Binding &b2) {
+ return !(b1 == b2);
+}
+bool operator==(const RuleArtifact &a1, const RuleArtifact &a2);
+inline bool operator!=(const RuleArtifact &a1, const RuleArtifact &a2) { return !(a1 == a2); }
+
+class SourceArtifact : public PersistentObject
+{
+public:
+ static SourceArtifactPtr create() { return SourceArtifactPtr(new SourceArtifact); }
+
+ QString absoluteFilePath;
+ FileTags fileTags;
+ bool overrideFileTags;
+ PropertyMapPtr properties;
+
+private:
+ SourceArtifact() : overrideFileTags(true) {}
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+bool operator==(const SourceArtifact &sa1, const SourceArtifact &sa2);
+inline bool operator!=(const SourceArtifact &sa1, const SourceArtifact &sa2) {
+ return !(sa1 == sa2);
+}
+
+bool sourceArtifactListsAreEqual(const QList<SourceArtifactPtr> &l1,
+ const QList<SourceArtifactPtr> &l2);
+
+class SourceWildCards : public PersistentObject
+{
+public:
+ typedef QSharedPointer<SourceWildCards> Ptr;
+ typedef QSharedPointer<const SourceWildCards> ConstPtr;
+
+ static Ptr create() { return Ptr(new SourceWildCards); }
+
+ QSet<QString> expandPatterns(const GroupConstPtr &group, const QString &baseDir) const;
+
+ // TODO: Use back pointer to Group instead?
+ QString prefix;
+
+ QStringList patterns;
+ QStringList excludePatterns;
+ QList<SourceArtifactPtr> files;
+
+private:
+ SourceWildCards() {}
+
+ QSet<QString> expandPatterns(const GroupConstPtr &group, const QStringList &patterns,
+ const QString &baseDir) const;
+ void expandPatterns(QSet<QString> &result, const GroupConstPtr &group,
+ const QStringList &parts, const QString &baseDir) const;
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+
+class ResolvedGroup : public PersistentObject
+{
+public:
+ static GroupPtr create() { return GroupPtr(new ResolvedGroup); }
+
+ CodeLocation location;
+
+ QString name;
+ bool enabled;
+ QString prefix;
+ QList<SourceArtifactPtr> files;
+ SourceWildCards::Ptr wildcards;
+ PropertyMapPtr properties;
+ FileTags fileTags;
+ bool overrideTags;
+
+ QList<SourceArtifactPtr> allFiles() const;
+
+private:
+ ResolvedGroup()
+ : enabled(true)
+ {}
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+
+class ResolvedFileContext : public PersistentObject
+{
+public:
+ static ResolvedFileContextPtr create()
+ {
+ return ResolvedFileContextPtr(new ResolvedFileContext);
+ }
+
+ QString filePath;
+ QStringList jsExtensions;
+ JsImports jsImports;
+
+private:
+ ResolvedFileContext() {}
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+
+bool operator==(const ResolvedFileContext &a, const ResolvedFileContext &b);
+inline bool operator!=(const ResolvedFileContext &a, const ResolvedFileContext &b)
+{ return !(a == b); }
+
+class ScriptFunction : public PersistentObject
+{
+public:
+ static ScriptFunctionPtr create() { return ScriptFunctionPtr(new ScriptFunction); }
+
+ QString sourceCode;
+ QStringList argumentNames;
+ CodeLocation location;
+ ResolvedFileContextConstPtr fileContext;
+ mutable QScriptValue scriptFunction; // cache
+
+private:
+ ScriptFunction() {}
+
+ void load(PersistentPool &);
+ void store(PersistentPool &) const;
+};
+
+bool operator==(const ScriptFunction &a, const ScriptFunction &b);
+inline bool operator!=(const ScriptFunction &a, const ScriptFunction &b) { return !(a == b); }
+
+class ResolvedModule : public PersistentObject
+{
+public:
+ static ResolvedModulePtr create() { return ResolvedModulePtr(new ResolvedModule); }
+
+ QString name;
+ QStringList moduleDependencies;
+ ScriptFunctionPtr setupBuildEnvironmentScript;
+ ScriptFunctionPtr setupRunEnvironmentScript;
+
+private:
+ ResolvedModule() {}
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+bool operator==(const ResolvedModule &m1, const ResolvedModule &m2);
+inline bool operator!=(const ResolvedModule &m1, const ResolvedModule &m2) { return !(m1 == m2); }
+
+/**
+ * Per default each rule is a "non-multiplex rule".
+ *
+ * A "multiplex rule" creates one transformer that takes all
+ * input artifacts with the matching input file tag and creates
+ * one or more artifacts. (e.g. linker rule)
+ *
+ * A "non-multiplex rule" creates one transformer per matching input file.
+ */
+class Rule : public PersistentObject
+{
+public:
+ static RulePtr create() { return RulePtr(new Rule); }
+
+ ResolvedModuleConstPtr module;
+ ScriptFunctionPtr script;
+ FileTags inputs;
+ FileTags auxiliaryInputs;
+ FileTags usings;
+ FileTags explicitlyDependsOn;
+ bool multiplex;
+ QList<RuleArtifactPtr> artifacts;
+
+ // members that we don't need to save
+ int ruleGraphId;
+
+ QString toString() const;
+ FileTags outputFileTags() const;
+
+private:
+ Rule() : multiplex(false), ruleGraphId(-1) {}
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+bool operator==(const Rule &r1, const Rule &r2);
+inline bool operator!=(const Rule &r1, const Rule &r2) { return !(r1 == r2); }
+bool ruleListsAreEqual(const QList<RulePtr> &l1, const QList<RulePtr> &l2);
+
+class ResolvedTransformer : public PersistentObject
+{
+public:
+ static ResolvedTransformerPtr create()
+ {
+ return ResolvedTransformerPtr(new ResolvedTransformer);
+ }
+
+ ResolvedModuleConstPtr module;
+ QStringList inputs;
+ QList<SourceArtifactPtr> outputs;
+ ScriptFunctionPtr transform;
+ FileTags explicitlyDependsOn;
+
+private:
+ ResolvedTransformer() {}
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+
+bool operator==(const ResolvedTransformer &t1, const ResolvedTransformer &t2);
+inline bool operator!=(const ResolvedTransformer &t1, const ResolvedTransformer &t2) {
+ return !(t1 == t2);
+}
+bool transformerListsAreEqual(const QList<ResolvedTransformerConstPtr> &l1,
+ const QList<ResolvedTransformerConstPtr> &l2);
+
+class TopLevelProject;
+class ScriptEngine;
+
+class ResolvedProduct : public PersistentObject
+{
+public:
+ static ResolvedProductPtr create() { return ResolvedProductPtr(new ResolvedProduct); }
+
+ ~ResolvedProduct();
+
+ bool enabled;
+ FileTags fileTags;
+ FileTags additionalFileTags;
+ QString name;
+ QString targetName;
+ QString sourceDirectory;
+ QString destinationDirectory;
+ CodeLocation location;
+ WeakPointer<ResolvedProject> project;
+ PropertyMapPtr properties;
+ QSet<RulePtr> rules;
+ QSet<ResolvedProductPtr> dependencies;
+ QList<FileTaggerConstPtr> fileTaggers;
+ QList<ResolvedModuleConstPtr> modules;
+ QList<ResolvedTransformerConstPtr> transformers;
+ QList<GroupPtr> groups;
+ QList<ArtifactPropertiesPtr> artifactProperties;
+ QScopedPointer<ProductBuildData> buildData;
+
+ mutable QProcessEnvironment buildEnvironment; // must not be saved
+ mutable QProcessEnvironment runEnvironment; // must not be saved
+ QHash<QString, QString> executablePathCache;
+
+ QList<SourceArtifactPtr> allFiles() const;
+ QList<SourceArtifactPtr> allEnabledFiles() const;
+ FileTags fileTagsForFileName(const QString &fileName) const;
+ void setupBuildEnvironment(ScriptEngine *scriptEngine, const QProcessEnvironment &env) const;
+ void setupRunEnvironment(ScriptEngine *scriptEngine, const QProcessEnvironment &env) const;
+
+ const QList<RuleConstPtr> &topSortedRules() const;
+ TopLevelProject *topLevelProject() const;
+
+ QStringList generatedFiles(const QString &baseFile, const FileTags &tags) const;
+
+private:
+ ResolvedProduct();
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+};
+
+class ResolvedProject : public PersistentObject
+{
+public:
+ static ResolvedProjectPtr create() { return ResolvedProjectPtr(new ResolvedProject); }
+
+ QString name;
+ CodeLocation location;
+ bool enabled;
+ QList<ResolvedProductPtr> products;
+ QList<ResolvedProjectPtr> subProjects;
+ WeakPointer<ResolvedProject> parentProject;
+
+ void setProjectProperties(const QVariantMap &config) { m_projectProperties = config; }
+ const QVariantMap &projectProperties() const { return m_projectProperties; }
+
+ TopLevelProject *topLevelProject();
+ QList<ResolvedProjectPtr> allSubProjects() const;
+ QList<ResolvedProductPtr> allProducts() const;
+
+protected:
+ ResolvedProject();
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+private:
+ QVariantMap m_projectProperties;
+ TopLevelProject *m_topLevelProject;
+};
+
+class TopLevelProject : public ResolvedProject
+{
+ friend class BuildGraphLoader;
+public:
+ ~TopLevelProject();
+
+ static TopLevelProjectPtr create() { return TopLevelProjectPtr(new TopLevelProject); }
+
+ static QString deriveId(const QVariantMap &config);
+ static QString deriveBuildDirectory(const QString &buildRoot, const QString &id);
+
+ QString buildDirectory; // Not saved
+ QProcessEnvironment environment;
+ QVariantMap platformEnvironment;
+ QHash<QString, QString> usedEnvironment; // Environment variables requested by the project while resolving.
+ QHash<QString, bool> fileExistsResults; // Results of calls to "File.exists()".
+ QHash<QString, FileTime> fileLastModifiedResults; // Results of calls to "File.lastModified()".
+ QScopedPointer<ProjectBuildData> buildData;
+ bool locked;
+
+ QSet<QString> buildSystemFiles;
+
+ void setBuildConfiguration(const QVariantMap &config);
+ const QVariantMap &buildConfiguration() const { return m_buildConfiguration; }
+ QString id() const { return m_id; }
+
+ QString buildGraphFilePath() const;
+ void store(const Logger &logger) const;
+
+private:
+ TopLevelProject();
+
+ void load(PersistentPool &pool);
+ void store(PersistentPool &pool) const;
+
+ QString m_id;
+ QVariantMap m_buildConfiguration;
+};
+
+bool artifactPropertyListsAreEqual(const QList<ArtifactPropertiesPtr> &l1,
+ const QList<ArtifactPropertiesPtr> &l2);
+
+} // namespace Internal
+} // namespace qbs
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(qbs::Internal::JsImport, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(qbs::Internal::RuleArtifact::Binding, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+#endif // QBS_LANGUAGE_H
diff --git a/src/lib/corelib/language/language.pri b/src/lib/corelib/language/language.pri
new file mode 100644
index 000000000..09d7e9a1b
--- /dev/null
+++ b/src/lib/corelib/language/language.pri
@@ -0,0 +1,70 @@
+HEADERS += \
+ $$PWD/artifactproperties.h \
+ $$PWD/asttools.h \
+ $$PWD/builtindeclarations.h \
+ $$PWD/builtinvalue.h \
+ $$PWD/evaluationdata.h \
+ $$PWD/evaluator.h \
+ $$PWD/evaluatorscriptclass.h \
+ $$PWD/filecontext.h \
+ $$PWD/filetags.h \
+ $$PWD/forward_decls.h \
+ $$PWD/functiondeclaration.h \
+ $$PWD/identifiersearch.h \
+ $$PWD/importversion.h \
+ $$PWD/item.h \
+ $$PWD/itemdeclaration.h \
+ $$PWD/itemobserver.h \
+ $$PWD/itempool.h \
+ $$PWD/itemreader.h \
+ $$PWD/itemreaderastvisitor.h \
+ $$PWD/jsimports.h \
+ $$PWD/language.h \
+ $$PWD/loader.h \
+ $$PWD/moduleloader.h \
+ $$PWD/preparescriptobserver.h \
+ $$PWD/projectresolver.h \
+ $$PWD/property.h \
+ $$PWD/propertydeclaration.h \
+ $$PWD/propertymapinternal.h \
+ $$PWD/scriptengine.h \
+ $$PWD/scriptpropertyobserver.h \
+ $$PWD/value.h
+
+SOURCES += \
+ $$PWD/artifactproperties.cpp \
+ $$PWD/asttools.cpp \
+ $$PWD/builtindeclarations.cpp \
+ $$PWD/builtinvalue.cpp \
+ $$PWD/evaluator.cpp \
+ $$PWD/evaluatorscriptclass.cpp \
+ $$PWD/filecontext.cpp \
+ $$PWD/filetags.cpp \
+ $$PWD/identifiersearch.cpp \
+ $$PWD/importversion.cpp \
+ $$PWD/item.cpp \
+ $$PWD/itemdeclaration.cpp \
+ $$PWD/itempool.cpp \
+ $$PWD/itemreader.cpp \
+ $$PWD/itemreaderastvisitor.cpp \
+ $$PWD/language.cpp \
+ $$PWD/loader.cpp \
+ $$PWD/moduleloader.cpp \
+ $$PWD/preparescriptobserver.cpp \
+ $$PWD/projectresolver.cpp \
+ $$PWD/propertydeclaration.cpp \
+ $$PWD/propertymapinternal.cpp \
+ $$PWD/scriptengine.cpp \
+ $$PWD/value.cpp
+
+all_tests {
+ HEADERS += $$PWD/tst_language.h
+ SOURCES += $$PWD/tst_language.cpp
+ OTHER_FILES += $$PWD/testdata/*
+}
+
+!qbs_no_dev_install {
+ language_headers.files = $$PWD/forward_decls.h
+ language_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/language
+ INSTALLS += language_headers
+}
diff --git a/src/lib/corelib/language/loader.cpp b/src/lib/corelib/language/loader.cpp
new file mode 100644
index 000000000..8a3f3c98f
--- /dev/null
+++ b/src/lib/corelib/language/loader.cpp
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** 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 "loader.h"
+
+#include "builtindeclarations.h"
+#include "item.h"
+#include "moduleloader.h"
+#include "projectresolver.h"
+#include <logging/translator.h>
+#include <tools/fileinfo.h>
+#include <tools/progressobserver.h>
+#include <tools/qbsassert.h>
+#include <tools/setupprojectparameters.h>
+
+#include <QDir>
+
+namespace qbs {
+namespace Internal {
+
+Loader::Loader(ScriptEngine *engine, const Logger &logger)
+ : m_logger(logger)
+ , m_progressObserver(0)
+ , m_builtins(new BuiltinDeclarations)
+ , m_moduleLoader(new ModuleLoader(engine, m_builtins, logger))
+ , m_projectResolver(new ProjectResolver(m_moduleLoader, m_builtins, logger))
+ , m_engine(engine)
+{
+}
+
+Loader::~Loader()
+{
+ delete m_projectResolver;
+ delete m_moduleLoader;
+ delete m_builtins;
+}
+
+void Loader::setProgressObserver(ProgressObserver *observer)
+{
+ m_progressObserver = observer;
+ m_moduleLoader->setProgressObserver(observer);
+ m_projectResolver->setProgressObserver(observer);
+}
+
+void Loader::setSearchPaths(const QStringList &_searchPaths)
+{
+ QStringList searchPaths;
+ foreach (const QString &searchPath, _searchPaths) {
+ if (!FileInfo::exists(searchPath)) {
+ m_logger.qbsWarning() << Tr::tr("Search path '%1' does not exist.")
+ .arg(QDir::toNativeSeparators(searchPath));
+ } else {
+ searchPaths += searchPath;
+ }
+ }
+
+ m_moduleLoader->setSearchPaths(searchPaths);
+}
+
+TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters &parameters)
+{
+ QBS_CHECK(QFileInfo(parameters.projectFilePath()).isAbsolute());
+
+ m_engine->setEnvironment(parameters.environment());
+ m_engine->clearExceptions();
+
+ // At this point, we cannot set a sensible total effort, because we know nothing about
+ // the project yet. That's why we use a placeholder here, so the user at least
+ // sees that an operation is starting. The real total effort will be set later when
+ // we have enough information.
+ if (m_progressObserver) {
+ m_progressObserver->initialize(Tr::tr("Resolving project for configuration %1")
+ .arg(TopLevelProject::deriveId(parameters.buildConfigurationTree())), 1);
+ }
+
+ ModuleLoaderResult loadResult
+ = m_moduleLoader->load(parameters.projectFilePath(),
+ parameters.overriddenValuesTree(),
+ parameters.buildConfigurationTree(),
+ true);
+ const TopLevelProjectPtr project = m_projectResolver->resolve(loadResult, parameters);
+
+ // E.g. if the top-level project is disabled.
+ if (m_progressObserver)
+ m_progressObserver->setFinished();
+
+ return project;
+}
+
+QByteArray Loader::qmlTypeInfo()
+{
+ return m_builtins->qmlTypeInfo();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/loader.h b/src/lib/corelib/language/loader.h
new file mode 100644
index 000000000..64e3a2ad6
--- /dev/null
+++ b/src/lib/corelib/language/loader.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_LOADER_H
+#define QBS_LOADER_H
+
+#include "forward_decls.h"
+#include <tools/qbs_export.h>
+#include <logging/logger.h>
+
+#include <QStringList>
+
+namespace qbs {
+class Settings;
+class SetupProjectParameters;
+namespace Internal {
+class BuiltinDeclarations;
+class Logger;
+class ModuleLoader;
+class ProgressObserver;
+class ScriptEngine;
+class ProjectResolver;
+
+class QBS_EXPORT Loader // FIXME: Exported for qbs-qmltypes
+{
+public:
+ Loader(ScriptEngine *engine, const Logger &logger);
+ ~Loader();
+
+ void setProgressObserver(ProgressObserver *observer);
+ void setSearchPaths(const QStringList &searchPaths);
+ TopLevelProjectPtr loadProject(const SetupProjectParameters &parameters);
+ QByteArray qmlTypeInfo();
+
+private:
+ Logger m_logger;
+ ProgressObserver *m_progressObserver;
+ BuiltinDeclarations *m_builtins;
+ ModuleLoader *m_moduleLoader;
+ ProjectResolver *m_projectResolver;
+ ScriptEngine * const m_engine;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_LOADER_H
diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp
new file mode 100644
index 000000000..a0c06bb61
--- /dev/null
+++ b/src/lib/corelib/language/moduleloader.cpp
@@ -0,0 +1,1143 @@
+/****************************************************************************
+**
+** 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 "moduleloader.h"
+
+#include "builtindeclarations.h"
+#include "builtinvalue.h"
+#include "evaluator.h"
+#include "filecontext.h"
+#include "item.h"
+#include "itemreader.h"
+#include "scriptengine.h"
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/hostosinfo.h>
+#include <tools/progressobserver.h>
+#include <tools/qbsassert.h>
+#include <tools/qttools.h>
+
+#include <QDebug>
+#include <QDir>
+#include <QDirIterator>
+
+namespace qbs {
+namespace Internal {
+
+class ModuleLoader::ItemModuleList : public QList<Item::Module> {};
+
+const QString moduleSearchSubDir = QLatin1String("modules");
+
+ModuleLoader::ModuleLoader(ScriptEngine *engine, BuiltinDeclarations *builtins,
+ const Logger &logger)
+ : m_engine(engine)
+ , m_pool(0)
+ , m_logger(logger)
+ , m_progressObserver(0)
+ , m_reader(new ItemReader(builtins, logger))
+ , m_evaluator(new Evaluator(engine, logger))
+{
+}
+
+ModuleLoader::~ModuleLoader()
+{
+ delete m_evaluator;
+ delete m_reader;
+}
+
+void ModuleLoader::setProgressObserver(ProgressObserver *progressObserver)
+{
+ m_progressObserver = progressObserver;
+}
+
+static void addExtraModuleSearchPath(QStringList &list, const QString &searchPath)
+{
+ list += FileInfo::resolvePath(searchPath, moduleSearchSubDir);
+}
+
+void ModuleLoader::setSearchPaths(const QStringList &searchPaths)
+{
+ m_reader->setSearchPaths(searchPaths);
+
+ m_moduleDirListCache.clear();
+ m_moduleSearchPaths.clear();
+ foreach (const QString &path, searchPaths)
+ addExtraModuleSearchPath(m_moduleSearchPaths, path);
+}
+
+ModuleLoaderResult ModuleLoader::load(const QString &filePath,
+ const QVariantMap &overriddenProperties, const QVariantMap &buildConfigProperties,
+ bool wrapWithProjectItem)
+{
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[MODLDR] load" << filePath;
+ m_overriddenProperties = overriddenProperties;
+ m_buildConfigProperties = buildConfigProperties;
+ m_validItemPropertyNamesPerItem.clear();
+ m_disabledItems.clear();
+
+ ModuleLoaderResult result;
+ m_pool = result.itemPool.data();
+ m_reader->setPool(m_pool);
+
+ Item *root = m_reader->readFile(filePath);
+ if (!root)
+ return ModuleLoaderResult();
+
+ if (wrapWithProjectItem && root->typeName() != QLatin1String("Project"))
+ root = wrapWithProject(root);
+
+ handleProject(&result, root, QSet<QString>() << QDir::cleanPath(filePath));
+ result.root = root;
+ result.qbsFiles = m_reader->filesRead();
+ return result;
+}
+
+class PropertyDeclarationCheck : public ValueHandler
+{
+ const QHash<Item *, QSet<QString> > &m_validItemPropertyNamesPerItem;
+ const QSet<Item *> &m_disabledItems;
+ Item *m_parentItem;
+ QString m_currentName;
+public:
+ PropertyDeclarationCheck(const QHash<Item *, QSet<QString> > &validItemPropertyNamesPerItem,
+ const QSet<Item *> &disabledItems)
+ : m_validItemPropertyNamesPerItem(validItemPropertyNamesPerItem)
+ , m_disabledItems(disabledItems)
+ , m_parentItem(0)
+ {
+ }
+
+ void operator()(Item *item)
+ {
+ handleItem(item);
+ }
+
+private:
+ void handle(JSSourceValue *value)
+ {
+ if (!m_parentItem->propertyDeclaration(m_currentName).isValid()) {
+ throw ErrorInfo(Tr::tr("Property '%1' is not declared.").arg(m_currentName),
+ value->location());
+ }
+ }
+
+ void handle(ItemValue *value)
+ {
+ if (!value->item()->isModuleInstance()
+ && !m_validItemPropertyNamesPerItem.value(m_parentItem).contains(m_currentName)
+ && m_parentItem->file()
+ && !m_parentItem->file()->idScope()->hasProperty(m_currentName)) {
+ throw ErrorInfo(Tr::tr("Item '%1' is not declared. "
+ "Did you forget to add a Depends item?").arg(m_currentName),
+ value->location().isValid() ? value->location()
+ : m_parentItem->location());
+ }
+
+ handleItem(value->item());
+ }
+
+ void handleItem(Item *item)
+ {
+ if (m_disabledItems.contains(item) || item->typeName() == QLatin1String("SubProject"))
+ return;
+
+ Item *oldParentItem = m_parentItem;
+ for (Item::PropertyMap::const_iterator it = item->properties().constBegin();
+ it != item->properties().constEnd(); ++it) {
+ if (item->propertyDeclaration(it.key()).isValid())
+ continue;
+ m_currentName = it.key();
+ m_parentItem = item;
+ it.value()->apply(this);
+ }
+ m_parentItem = oldParentItem;
+ foreach (Item *child, item->children())
+ handleItem(child);
+ }
+
+ void handle(VariantValue *) { /* only created internally - no need to check */ }
+ void handle(BuiltinValue *) { /* only created internally - no need to check */ }
+};
+
+void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, Item *item,
+ const QSet<QString> &referencedFilePaths)
+{
+ if (!checkItemCondition(item))
+ return;
+ ProjectContext projectContext;
+ projectContext.result = loadResult;
+ projectContext.localModuleSearchPath = FileInfo::resolvePath(item->file()->dirPath(),
+ moduleSearchSubDir);
+
+ ProductContext dummyProductContext;
+ dummyProductContext.project = &projectContext;
+ loadBaseModule(&dummyProductContext, item);
+ overrideItemProperties(item, QLatin1String("project"), m_overriddenProperties);
+
+ projectContext.extraSearchPaths = readExtraSearchPaths(item);
+ m_reader->pushExtraSearchPaths(projectContext.extraSearchPaths);
+ projectContext.item = item;
+ ItemValuePtr itemValue = ItemValue::create(item);
+ projectContext.scope = Item::create(m_pool);
+ projectContext.scope->setProperty(QLatin1String("project"), itemValue);
+
+ foreach (Item *child, item->children()) {
+ child->setScope(projectContext.scope);
+ if (child->typeName() == QLatin1String("Product")) {
+ handleProduct(&projectContext, child);
+ } else if (child->typeName() == QLatin1String("SubProject")) {
+ handleSubProject(&projectContext, child, referencedFilePaths);
+ } else if (child->typeName() == QLatin1String("Project")) {
+ copyProperties(item, child);
+ handleProject(loadResult, child, referencedFilePaths);
+ }
+ }
+
+ const QString projectFileDirPath = FileInfo::path(item->file()->filePath());
+ const QStringList refs = m_evaluator->stringListValue(item, QLatin1String("references"));
+ foreach (const QString &filePath, refs) {
+ QString absReferencePath = FileInfo::resolvePath(projectFileDirPath, filePath);
+ if (FileInfo(absReferencePath).isDir()) {
+ QString qbsFilePath;
+ QDirIterator dit(absReferencePath, QStringList(QLatin1String("*.qbs")));
+ while (dit.hasNext()) {
+ if (!qbsFilePath.isEmpty()) {
+ throw ErrorInfo(Tr::tr("Referenced directory '%1' contains more than one "
+ "qbs file.").arg(absReferencePath),
+ item->property(QLatin1String("references"))->location());
+ }
+ qbsFilePath = dit.next();
+ }
+ if (qbsFilePath.isEmpty()) {
+ throw ErrorInfo(Tr::tr("Referenced directory '%1' does not contain a qbs file.")
+ .arg(absReferencePath),
+ item->property(QLatin1String("references"))->location());
+ }
+ absReferencePath = qbsFilePath;
+ }
+ if (referencedFilePaths.contains(absReferencePath))
+ throw ErrorInfo(Tr::tr("Cycle detected while referencing file '%1'.").arg(filePath),
+ item->property(QLatin1String("references"))->location());
+ Item *subItem = m_reader->readFile(absReferencePath);
+ subItem->setScope(projectContext.scope);
+ subItem->setParent(projectContext.item);
+ QList<Item *> projectChildren = projectContext.item->children();
+ projectChildren += subItem;
+ projectContext.item->setChildren(projectChildren);
+ if (subItem->typeName() == "Product") {
+ handleProduct(&projectContext, subItem);
+ } else if (subItem->typeName() == "Project") {
+ copyProperties(item, subItem);
+ handleProject(loadResult, subItem,
+ QSet<QString>(referencedFilePaths) << absReferencePath);
+ } else {
+ throw ErrorInfo(Tr::tr("The top-level item of a file in a \"references\" list must be "
+ "a Product or a Project, but it is \"%1\".").arg(subItem->typeName()),
+ subItem->location());
+ }
+ }
+
+ checkItemTypes(item);
+
+ PropertyDeclarationCheck check(m_validItemPropertyNamesPerItem, m_disabledItems);
+ check(item);
+
+ m_reader->popExtraSearchPaths();
+}
+
+void ModuleLoader::handleProduct(ProjectContext *projectContext, Item *item)
+{
+ checkCancelation();
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[MODLDR] handleProduct " << item->file()->filePath();
+
+ ProductContext productContext;
+ productContext.project = projectContext;
+ bool extraSearchPathsSet = false;
+ const QStringList extraSearchPaths = readExtraSearchPaths(item, &extraSearchPathsSet);
+ if (extraSearchPathsSet) { // Inherit from project if not set in product itself.
+ productContext.extraSearchPaths = extraSearchPaths;
+ m_reader->pushExtraSearchPaths(extraSearchPaths);
+ } else {
+ productContext.extraSearchPaths = projectContext->extraSearchPaths;
+ }
+ productContext.item = item;
+ ItemValuePtr itemValue = ItemValue::create(item);
+ productContext.scope = Item::create(m_pool);
+ productContext.scope->setProperty(QLatin1String("product"), itemValue);
+ productContext.scope->setScope(projectContext->scope);
+ DependsContext dependsContext;
+ dependsContext.product = &productContext;
+ dependsContext.productDependencies = &productContext.info.usedProducts;
+ setScopeForDescendants(item, productContext.scope);
+ resolveDependencies(&dependsContext, item);
+ if (!checkItemCondition(item))
+ return;
+ createAdditionalModuleInstancesInProduct(&productContext);
+
+ foreach (Item *child, item->children()) {
+ if (child->typeName() == QLatin1String("Group"))
+ handleGroup(&productContext, child);
+ else if (child->typeName() == QLatin1String("Artifact"))
+ handleArtifact(&productContext, child);
+ else if (child->typeName() == QLatin1String("Export"))
+ deferExportItem(&productContext, child);
+ else if (child->typeName() == QLatin1String("Probe"))
+ resolveProbe(item, child);
+ }
+
+ mergeExportItems(&productContext);
+ projectContext->result->productInfos.insert(item, productContext.info);
+ if (extraSearchPathsSet)
+ m_reader->popExtraSearchPaths();
+}
+
+void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext, Item *item,
+ const QSet<QString> &referencedFilePaths)
+{
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[MODLDR] handleSubProject " << item->file()->filePath();
+
+ Item * const propertiesItem = item->child(QLatin1String("Properties"));
+ bool subProjectEnabled = true;
+ if (propertiesItem)
+ subProjectEnabled = checkItemCondition(propertiesItem);
+ if (!subProjectEnabled)
+ return;
+
+ const QString projectFileDirPath = FileInfo::path(item->file()->filePath());
+ const QString relativeFilePath = m_evaluator->property(item,
+ QLatin1String("filePath")).toString();
+ QString subProjectFilePath = FileInfo::resolvePath(projectFileDirPath, relativeFilePath);
+ if (referencedFilePaths.contains(subProjectFilePath))
+ throw ErrorInfo(Tr::tr("Cycle detected while loading subproject file '%1'.")
+ .arg(relativeFilePath), item->location());
+ Item *loadedItem = m_reader->readFile(subProjectFilePath);
+ if (loadedItem->typeName() == QLatin1String("Product"))
+ loadedItem = wrapWithProject(loadedItem);
+ const bool inheritProperties
+ = m_evaluator->boolValue(item, QLatin1String("inheritProperties"), true);
+
+ if (inheritProperties)
+ copyProperties(item->parent(), loadedItem);
+ if (propertiesItem) {
+ const Item::PropertyMap &overriddenProperties = propertiesItem->properties();
+ for (Item::PropertyMap::ConstIterator it = overriddenProperties.constBegin();
+ it != overriddenProperties.constEnd(); ++it) {
+ loadedItem->setProperty(it.key(), overriddenProperties.value(it.key()));
+ }
+ }
+
+ if (loadedItem->typeName() != QLatin1String("Project")) {
+ ErrorInfo error;
+ error.append(Tr::tr("Expected Project item, but encountered '%1'.")
+ .arg(loadedItem->typeName()), loadedItem->location());
+ const ValuePtr &filePathProperty = item->properties().value(QLatin1String("filePath"));
+ error.append(Tr::tr("The problematic file was referenced from here."),
+ filePathProperty->location());
+ throw error;
+ }
+
+ Item::addChild(item, loadedItem);
+ item->setScope(projectContext->scope);
+ handleProject(projectContext->result, loadedItem,
+ QSet<QString>(referencedFilePaths) << subProjectFilePath);
+}
+
+void ModuleLoader::createAdditionalModuleInstancesInProduct(ProductContext *productContext)
+{
+ Item::Modules modulesToCheck;
+ QSet<QStringList> modulesInProduct;
+ foreach (const Item::Module &module, productContext->item->modules()) {
+ modulesInProduct += module.name;
+ modulesToCheck += module.item->prototype()->modules();
+ }
+ while (!modulesToCheck.isEmpty()) {
+ Item::Module module = modulesToCheck.takeFirst();
+ if (modulesInProduct.contains(module.name))
+ continue;
+ modulesInProduct += module.name;
+ modulesToCheck += module.item->prototype()->modules();
+ Item *instance = Item::create(m_pool);
+ instantiateModule(productContext, productContext->item, instance, module.item->prototype(),
+ module.name);
+ module.item = instance;
+ productContext->item->modules().append(module);
+ }
+}
+
+void ModuleLoader::handleGroup(ProductContext *productContext, Item *item)
+{
+ checkCancelation();
+ propagateModulesFromProduct(productContext, item);
+ checkItemCondition(item);
+}
+
+void ModuleLoader::handleArtifact(ProductContext *productContext, Item *item)
+{
+ checkCancelation();
+ propagateModulesFromProduct(productContext, item);
+}
+
+void ModuleLoader::deferExportItem(ModuleLoader::ProductContext *productContext, Item *item)
+{
+ productContext->exportItems.append(item);
+}
+
+static void mergeProperty(Item *dst, const QString &name, const ValuePtr &value)
+{
+ if (value->type() == Value::ItemValueType) {
+ Item *valueItem = value.staticCast<ItemValue>()->item();
+ if (!valueItem)
+ return;
+ Item *subItem = dst->itemProperty(name, true)->item();
+ for (QMap<QString, ValuePtr>::const_iterator it = valueItem->properties().constBegin();
+ it != valueItem->properties().constEnd(); ++it)
+ mergeProperty(subItem, it.key(), it.value());
+ } else {
+ dst->setProperty(name, value);
+ }
+}
+
+void ModuleLoader::mergeExportItems(ModuleLoader::ProductContext *productContext)
+{
+ Item *merged = Item::create(productContext->item->pool());
+ merged->setTypeName(QLatin1String("Export"));
+ QSet<Item *> exportItems;
+ foreach (Item *exportItem, productContext->exportItems) {
+ checkCancelation();
+ if (Q_UNLIKELY(productContext->filesWithExportItem.contains(exportItem->file())))
+ throw ErrorInfo(Tr::tr("Multiple Export items in one product are prohibited."),
+ exportItem->location());
+ merged->setLocation(exportItem->location());
+ productContext->filesWithExportItem += exportItem->file();
+ exportItems.insert(exportItem);
+ foreach (Item *child, exportItem->children())
+ Item::addChild(merged, child);
+ for (QMap<QString, ValuePtr>::const_iterator it = exportItem->properties().constBegin();
+ it != exportItem->properties().constEnd(); ++it) {
+ mergeProperty(merged, it.key(), it.value());
+ }
+ }
+
+ QList<Item *> children = productContext->item->children();
+ for (int i = 0; i < children.count();) {
+ if (exportItems.contains(children.at(i)))
+ children.removeAt(i);
+ else
+ ++i;
+ }
+ productContext->item->setChildren(children);
+ Item::addChild(productContext->item, merged);
+
+ DependsContext dependsContext;
+ dependsContext.product = productContext;
+ dependsContext.productDependencies = &productContext->info.usedProductsFromExportItem;
+ resolveDependencies(&dependsContext, merged);
+}
+
+void ModuleLoader::propagateModulesFromProduct(ProductContext *productContext, Item *item)
+{
+ for (Item::Modules::const_iterator it = productContext->item->modules().constBegin();
+ it != productContext->item->modules().constEnd(); ++it)
+ {
+ Item::Module m = *it;
+ Item *targetItem = moduleInstanceItem(item, m.name);
+ targetItem->setPrototype(m.item);
+ targetItem->setModuleInstanceFlag(true);
+ targetItem->setScope(m.item->scope());
+ targetItem->modules() = m.item->modules();
+
+ // "parent" should point to the group/artifact parent
+ targetItem->setParent(item->parent());
+
+ // the outer item of a module is the product's instance of it
+ targetItem->setOuterItem(m.item); // ### Is this always the same as the scope item?
+
+ m.item = targetItem;
+ item->modules() += m;
+ }
+}
+
+void ModuleLoader::resolveDependencies(DependsContext *dependsContext, Item *item)
+{
+ loadBaseModule(dependsContext->product, item);
+
+ // Resolve all Depends items.
+ typedef QHash<Item *, ItemModuleList> ModuleHash;
+ ModuleHash loadedModules;
+ ProductDependencyResults productDependencies;
+ foreach (Item *child, item->children())
+ if (child->typeName() == QLatin1String("Depends"))
+ resolveDependsItem(dependsContext, item, child, &loadedModules[child],
+ &productDependencies);
+
+ QSet<QString> loadedModuleNames;
+ foreach (const ItemModuleList &moduleList, loadedModules) {
+ foreach (const Item::Module &module, moduleList) {
+ const QString fullName = fullModuleName(module.name);
+ if (loadedModuleNames.contains(fullName)) {
+ m_logger.printWarning(ErrorInfo(Tr::tr("Duplicate dependency '%1'.").arg(fullName),
+ item->location()));
+ continue;
+ }
+ loadedModuleNames.insert(fullName);
+ item->modules() += module;
+ resolveProbes(module.item);
+ }
+ }
+
+ foreach (const ProductDependencyResult &pd, productDependencies)
+ dependsContext->productDependencies->append(pd.second);
+}
+
+void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *item,
+ Item *dependsItem, ItemModuleList *moduleResults,
+ ProductDependencyResults *productResults)
+{
+ checkCancelation();
+ if (!checkItemCondition(dependsItem)) {
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "Depends item disabled, ignoring.";
+ return;
+ }
+ const QString name = m_evaluator->property(dependsItem, "name").toString();
+ const QStringList nameParts = name.split('.');
+ if (Q_UNLIKELY(nameParts.count() > 2)) {
+ QString msg = Tr::tr("There cannot be more than one dot in a module name.");
+ throw ErrorInfo(msg, dependsItem->location());
+ }
+
+ QString superModuleName;
+ QStringList submodules = m_evaluator->stringListValue(dependsItem, QLatin1String("submodules"));
+ if (nameParts.count() == 2) {
+ if (Q_UNLIKELY(!submodules.isEmpty()))
+ throw ErrorInfo(Tr::tr("Depends.submodules cannot be used if name contains a dot."),
+ dependsItem->location());
+ superModuleName = nameParts.first();
+ submodules += nameParts.last();
+ }
+ if (Q_UNLIKELY(submodules.count() > 1 && !dependsItem->id().isEmpty())) {
+ QString msg = Tr::tr("A Depends item with more than one module cannot have an id.");
+ throw ErrorInfo(msg, dependsItem->location());
+ }
+ if (superModuleName.isEmpty()) {
+ if (submodules.isEmpty())
+ submodules += name;
+ else
+ superModuleName = name;
+ }
+
+ QStringList moduleNames;
+ foreach (const QString &submoduleName, submodules)
+ moduleNames += submoduleName;
+
+ Item::Module result;
+ foreach (const QString &moduleName, moduleNames) {
+ QStringList qualifiedModuleName(moduleName);
+ if (!superModuleName.isEmpty())
+ qualifiedModuleName.prepend(superModuleName);
+ const bool isRequired
+ = m_evaluator->boolValue(dependsItem, QLatin1String("required"));
+ Item *moduleItem = loadModule(dependsContext->product, item, dependsItem->location(),
+ dependsItem->id(), qualifiedModuleName, false, isRequired);
+ if (moduleItem) {
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "module loaded: " << fullModuleName(qualifiedModuleName);
+ result.name = qualifiedModuleName;
+ result.item = moduleItem;
+ moduleResults->append(result);
+ } else {
+ ModuleLoaderResult::ProductInfo::Dependency dependency;
+ dependency.name = moduleName;
+ dependency.required = m_evaluator->property(item, QLatin1String("required")).toBool();
+ dependency.failureMessage
+ = m_evaluator->property(item, QLatin1String("failureMessage")).toString();
+ productResults->append(ProductDependencyResult(dependsItem, dependency));
+ }
+ }
+}
+
+Item *ModuleLoader::moduleInstanceItem(Item *item, const QStringList &moduleName)
+{
+ Item *instance = item;
+ for (int i = 0; i < moduleName.count(); ++i) {
+ const QString &moduleNameSegment = moduleName.at(i);
+ m_validItemPropertyNamesPerItem[instance].insert(moduleNameSegment);
+ bool createNewItem = true;
+ const ValuePtr v = instance->properties().value(moduleName.at(i));
+ if (v && v->type() == Value::ItemValueType) {
+ const ItemValuePtr iv = v.staticCast<ItemValue>();
+ if (iv->item()) {
+ createNewItem = false;
+ instance = iv->item();
+ }
+ }
+ if (createNewItem) {
+ Item *newItem = Item::create(m_pool);
+ instance->setProperty(moduleNameSegment, ItemValue::create(newItem));
+ instance = newItem;
+ }
+ }
+ QBS_ASSERT(moduleName.isEmpty() || instance != item, return 0);
+ return instance;
+}
+
+Item *ModuleLoader::loadModule(ProductContext *productContext, Item *item,
+ const CodeLocation &dependsItemLocation,
+ const QString &moduleId, const QStringList &moduleName, bool isBaseModule, bool isRequired)
+{
+ Item *moduleInstance = moduleId.isEmpty()
+ ? moduleInstanceItem(item, moduleName)
+ : moduleInstanceItem(item, QStringList(moduleId));
+ if (!moduleInstance->typeName().isNull()) {
+ // already handled
+ return moduleInstance;
+ }
+
+ QStringList moduleSearchPaths(productContext->project->localModuleSearchPath);
+ foreach (const QString &searchPath, productContext->extraSearchPaths)
+ addExtraModuleSearchPath(moduleSearchPaths, searchPath);
+ bool cacheHit;
+ Item *modulePrototype = searchAndLoadModuleFile(productContext, dependsItemLocation,
+ moduleName, moduleSearchPaths, isRequired, &cacheHit);
+ if (!modulePrototype)
+ return 0;
+ if (!cacheHit && isBaseModule)
+ setupBaseModulePrototype(modulePrototype);
+ instantiateModule(productContext, item, moduleInstance, modulePrototype, moduleName);
+ callValidateScript(moduleInstance);
+ return moduleInstance;
+}
+
+// It's not necessarily an error if we don't find a required module with the given name,
+// because the dependency could refer to a product instead.
+Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext,
+ const CodeLocation &dependsItemLocation, const QStringList &moduleName,
+ const QStringList &extraSearchPaths, bool isRequired, bool *cacheHit)
+{
+ QStringList searchPaths = extraSearchPaths;
+ searchPaths.append(m_moduleSearchPaths);
+
+ bool triedToLoadModule = moduleName.count() > 1;
+ const QString fullName = fullModuleName(moduleName);
+ foreach (const QString &path, searchPaths) {
+ const QString dirPath = findExistingModulePath(path, moduleName);
+ if (dirPath.isEmpty())
+ continue;
+ QStringList moduleFileNames = m_moduleDirListCache.value(dirPath);
+ if (moduleFileNames.isEmpty()) {
+ QDirIterator dirIter(dirPath, QStringList(QLatin1String("*.qbs")));
+ while (dirIter.hasNext())
+ moduleFileNames += dirIter.next();
+
+ m_moduleDirListCache.insert(dirPath, moduleFileNames);
+ }
+ foreach (const QString &filePath, moduleFileNames) {
+ triedToLoadModule = true;
+ Item *module = loadModuleFile(productContext, fullName,
+ moduleName.count() == 1
+ && moduleName.first() == QLatin1String("qbs"),
+ filePath, cacheHit);
+ if (module)
+ return module;
+ }
+ }
+
+ if (!isRequired) {
+ if (m_logger.traceEnabled()) {
+ m_logger.qbsTrace() << "Non-required module '" << fullName << "' not found."
+ << "Creating dummy module for presence check.";
+ }
+ Item * const module = Item::create(m_pool);
+ module->setFile(FileContext::create());
+ module->setProperty(QLatin1String("present"), VariantValue::create(false));
+ return module;
+ }
+
+ if (Q_UNLIKELY(triedToLoadModule))
+ throw ErrorInfo(Tr::tr("Module %1 could not be loaded.").arg(fullName),
+ dependsItemLocation);
+
+ return 0;
+}
+
+// returns QVariant::Invalid for types that do not need conversion
+static QVariant::Type variantType(PropertyDeclaration::Type t)
+{
+ switch (t) {
+ case PropertyDeclaration::UnknownType:
+ break;
+ case PropertyDeclaration::Boolean:
+ return QVariant::Bool;
+ case PropertyDeclaration::Integer:
+ return QVariant::Int;
+ case PropertyDeclaration::Path:
+ return QVariant::String;
+ case PropertyDeclaration::PathList:
+ return QVariant::StringList;
+ case PropertyDeclaration::String:
+ return QVariant::String;
+ case PropertyDeclaration::StringList:
+ return QVariant::StringList;
+ case PropertyDeclaration::Variant:
+ break;
+ case PropertyDeclaration::Verbatim:
+ return QVariant::String;
+ }
+ return QVariant::Invalid;
+}
+
+static QVariant convertToPropertyType(const QVariant &v, PropertyDeclaration::Type t,
+ const QStringList &namePrefix, const QString &key)
+{
+ if (v.isNull() || !v.isValid())
+ return v;
+ const QVariant::Type vt = variantType(t);
+ if (vt == QVariant::Invalid)
+ return v;
+
+ // Handle the foo,bar,bla stringlist syntax.
+ if (t == PropertyDeclaration::StringList && v.type() == QVariant::String)
+ return v.toString().split(QLatin1Char(','));
+
+ QVariant c = v;
+ if (!c.convert(vt)) {
+ QStringList name = namePrefix;
+ name << key;
+ throw ErrorInfo(Tr::tr("Value '%1' of property '%2' has incompatible type.")
+ .arg(v.toString(), name.join(QLatin1String("."))));
+ }
+ return c;
+}
+
+static PropertyDeclaration firstValidPropertyDeclaration(Item *item, const QString &name)
+{
+ PropertyDeclaration decl;
+ do {
+ decl = item->propertyDeclarations().value(name);
+ if (decl.isValid())
+ return decl;
+ item = item->prototype();
+ } while (item);
+ return decl;
+}
+
+Item *ModuleLoader::loadModuleFile(ProductContext *productContext, const QString &fullModuleName,
+ bool isBaseModule, const QString &filePath, bool *cacheHit)
+{
+ checkCancelation();
+ Item *module = productContext->moduleItemCache.value(filePath);
+ if (module) {
+ m_logger.qbsTrace() << "[LDR] loadModuleFile cache hit for " << filePath;
+ *cacheHit = true;
+ return module;
+ }
+
+ module = productContext->project->moduleItemCache.value(filePath);
+ if (module) {
+ m_logger.qbsTrace() << "[LDR] loadModuleFile returns clone for " << filePath;
+ *cacheHit = true;
+ return module->clone(m_pool);
+ }
+
+ m_logger.qbsTrace() << "[LDR] loadModuleFile " << filePath;
+ *cacheHit = false;
+ module = m_reader->readFile(filePath);
+ if (!isBaseModule) {
+ DependsContext dependsContext;
+ dependsContext.product = productContext;
+ dependsContext.productDependencies = &productContext->info.usedProducts;
+ resolveDependencies(&dependsContext, module);
+ }
+ if (!checkItemCondition(module)) {
+ m_logger.qbsTrace() << "[LDR] module condition is false";
+ return 0;
+ }
+
+ // Module properties that are defined in the profile are used as default values.
+ const QVariantMap profileModuleProperties
+ = m_buildConfigProperties.value(fullModuleName).toMap();
+ for (QVariantMap::const_iterator vmit = profileModuleProperties.begin();
+ vmit != profileModuleProperties.end(); ++vmit)
+ {
+ if (Q_UNLIKELY(!module->hasProperty(vmit.key())))
+ throw ErrorInfo(Tr::tr("Unknown property: %1.%2").arg(fullModuleName, vmit.key()));
+ const PropertyDeclaration decl = firstValidPropertyDeclaration(module, vmit.key());
+ module->setProperty(vmit.key(),
+ VariantValue::create(convertToPropertyType(vmit.value(), decl.type,
+ QStringList(fullModuleName), vmit.key())));
+ }
+
+ productContext->moduleItemCache.insert(filePath, module);
+ productContext->project->moduleItemCache.insert(filePath, module);
+ return module;
+}
+
+void ModuleLoader::loadBaseModule(ProductContext *productContext, Item *item)
+{
+ const QStringList baseModuleName(QLatin1String("qbs"));
+ Item::Module baseModuleDesc;
+ baseModuleDesc.name = baseModuleName;
+ baseModuleDesc.item = loadModule(productContext, item, CodeLocation(), QString(),
+ baseModuleName, true, true);
+ if (Q_UNLIKELY(!baseModuleDesc.item))
+ throw ErrorInfo(Tr::tr("Cannot load base qbs module."));
+ item->modules() += baseModuleDesc;
+}
+
+void ModuleLoader::setupBaseModulePrototype(Item *prototype)
+{
+ prototype->setProperty(QLatin1String("getNativeSetting"),
+ 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"),
+ BuiltinValue::create(BuiltinValue::CanonicalArchitectureFunction));
+}
+
+static void collectItemsWithId_impl(Item *item, QList<Item *> *result)
+{
+ if (!item->id().isEmpty())
+ result->append(item);
+ foreach (Item *child, item->children())
+ collectItemsWithId_impl(child, result);
+}
+
+static QList<Item *> collectItemsWithId(Item *item)
+{
+ QList<Item *> result;
+ collectItemsWithId_impl(item, &result);
+ return result;
+}
+
+void ModuleLoader::instantiateModule(ProductContext *productContext, Item *instanceScope,
+ Item *moduleInstance, Item *modulePrototype,
+ const QStringList &moduleName)
+{
+ const QString fullName = fullModuleName(moduleName);
+ modulePrototype->setProperty(QLatin1String("name"),
+ VariantValue::create(fullName));
+
+ moduleInstance->setPrototype(modulePrototype);
+ moduleInstance->setFile(modulePrototype->file());
+ moduleInstance->setLocation(modulePrototype->location());
+ moduleInstance->setTypeName(modulePrototype->typeName());
+
+ // create module scope
+ Item *moduleScope = Item::create(m_pool);
+ moduleScope->setScope(instanceScope);
+ copyProperty(QLatin1String("project"), productContext->project->scope, moduleScope);
+ copyProperty(QLatin1String("product"), productContext->scope, moduleScope);
+ moduleInstance->setScope(moduleScope);
+ moduleInstance->setModuleInstanceFlag(true);
+
+ QHash<Item *, Item *> prototypeInstanceMap;
+ prototypeInstanceMap[modulePrototype] = moduleInstance;
+
+ // create instances for every child of the prototype
+ createChildInstances(productContext, moduleInstance, modulePrototype, &prototypeInstanceMap);
+
+ // create ids from from the prototype in the instance
+ if (modulePrototype->file()->idScope()) {
+ foreach (Item *itemWithId, collectItemsWithId(modulePrototype)) {
+ Item *idProto = itemWithId;
+ Item *idInstance = prototypeInstanceMap.value(idProto);
+ QBS_ASSERT(idInstance, continue);
+ ItemValuePtr idInstanceValue = ItemValue::create(idInstance);
+ moduleScope->setProperty(itemWithId->id(), idInstanceValue);
+ }
+ }
+
+ // create module instances for the dependencies of this module
+ foreach (Item::Module m, modulePrototype->modules()) {
+ Item *depinst = moduleInstanceItem(moduleInstance, m.name);
+ const bool safetyCheck = true;
+ if (safetyCheck) {
+ Item *obj = moduleInstance;
+ for (int i = 0; i < m.name.count(); ++i) {
+ ItemValuePtr iv = obj->itemProperty(m.name.at(i));
+ QBS_CHECK(iv);
+ obj = iv->item();
+ QBS_CHECK(obj);
+ }
+ QBS_CHECK(obj == depinst);
+ }
+ depinst->setPrototype(m.item);
+ depinst->setFile(m.item->file());
+ depinst->setLocation(m.item->location());
+ depinst->setTypeName(m.item->typeName());
+ depinst->setScope(moduleInstance);
+ m.item = depinst;
+ moduleInstance->modules() += m;
+ }
+
+ // override module properties given on the command line
+ const QVariantMap userModuleProperties = m_overriddenProperties.value(fullName).toMap();
+ for (QVariantMap::const_iterator vmit = userModuleProperties.begin();
+ vmit != userModuleProperties.end(); ++vmit) {
+ if (Q_UNLIKELY(!moduleInstance->hasProperty(vmit.key()))) {
+ throw ErrorInfo(Tr::tr("Unknown property: %1.%2")
+ .arg(fullModuleName(moduleName), vmit.key()));
+ }
+ const PropertyDeclaration decl = firstValidPropertyDeclaration(moduleInstance, vmit.key());
+ moduleInstance->setProperty(vmit.key(),
+ VariantValue::create(convertToPropertyType(vmit.value(), decl.type, moduleName,
+ vmit.key())));
+ }
+}
+
+void ModuleLoader::createChildInstances(ProductContext *productContext, Item *instance,
+ Item *prototype,
+ QHash<Item *, Item *> *prototypeInstanceMap) const
+{
+ foreach (Item *childPrototype, prototype->children()) {
+ Item *childInstance = Item::create(m_pool);
+ prototypeInstanceMap->insert(childPrototype, childInstance);
+ childInstance->setPrototype(childPrototype);
+ childInstance->setTypeName(childPrototype->typeName());
+ childInstance->setFile(childPrototype->file());
+ childInstance->setLocation(childPrototype->location());
+ childInstance->setScope(productContext->scope);
+ Item::addChild(instance, childInstance);
+ createChildInstances(productContext, childInstance, childPrototype, prototypeInstanceMap);
+ }
+}
+
+void ModuleLoader::resolveProbes(Item *item)
+{
+ foreach (Item *child, item->children())
+ if (child->typeName() == QLatin1String("Probe"))
+ resolveProbe(item, child);
+}
+
+void ModuleLoader::resolveProbe(Item *parent, Item *probe)
+{
+ const JSSourceValueConstPtr configureScript = probe->sourceProperty(QLatin1String("configure"));
+ if (Q_UNLIKELY(!configureScript))
+ throw ErrorInfo(Tr::tr("Probe.configure must be set."), probe->location());
+ typedef QPair<QString, QScriptValue> ProbeProperty;
+ QList<ProbeProperty> probeBindings;
+ for (Item *obj = probe; obj; obj = obj->prototype()) {
+ foreach (const QString &name, obj->properties().keys()) {
+ if (name == QLatin1String("configure"))
+ continue;
+ QScriptValue sv = m_evaluator->property(probe, name);
+ if (Q_UNLIKELY(m_evaluator->engine()->hasErrorOrException(sv))) {
+ ValuePtr value = obj->property(name);
+ throw ErrorInfo(sv.toString(), value ? value->location() : CodeLocation());
+ }
+ probeBindings += ProbeProperty(name, sv);
+ }
+ }
+ QScriptValue scope = m_engine->newObject();
+ m_engine->currentContext()->pushScope(m_evaluator->scriptValue(parent));
+ m_engine->currentContext()->pushScope(scope);
+ m_engine->currentContext()->pushScope(m_evaluator->fileScope(configureScript->file()));
+ foreach (const ProbeProperty &b, probeBindings)
+ scope.setProperty(b.first, b.second);
+ QScriptValue sv = m_engine->evaluate(configureScript->sourceCode());
+ if (Q_UNLIKELY(m_engine->hasErrorOrException(sv)))
+ throw ErrorInfo(sv.toString(), configureScript->location());
+ foreach (const ProbeProperty &b, probeBindings) {
+ const QVariant newValue = scope.property(b.first).toVariant();
+ if (newValue != b.second.toVariant())
+ probe->setProperty(b.first, VariantValue::create(newValue));
+ }
+ m_engine->currentContext()->popScope();
+ m_engine->currentContext()->popScope();
+ m_engine->currentContext()->popScope();
+}
+
+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)));
+ }
+}
+
+bool ModuleLoader::checkItemCondition(Item *item)
+{
+ if (m_evaluator->boolValue(item, QLatin1String("condition"), true))
+ return true;
+ m_disabledItems += item;
+ return false;
+}
+
+void ModuleLoader::checkItemTypes(Item *item)
+{
+ if (Q_UNLIKELY(!item->typeName().isEmpty()
+ && !m_reader->builtins()->containsType(item->typeName()))) {
+ const QString msg = Tr::tr("Unexpected item type '%1'.");
+ throw ErrorInfo(msg.arg(item->typeName()), item->location());
+ }
+
+ const ItemDeclaration decl = m_reader->builtins()->declarationsForType(item->typeName());
+ foreach (Item *child, item->children()) {
+ if (child->typeName().isEmpty())
+ continue;
+ checkItemTypes(child);
+ if (!decl.isChildTypeAllowed(child->typeName()))
+ throw ErrorInfo(Tr::tr("Items of type '%1' cannot contain items of type '%2'.")
+ .arg(item->typeName(), child->typeName()), item->location());
+ }
+}
+
+void ModuleLoader::callValidateScript(Item *module)
+{
+ m_evaluator->boolValue(module, QLatin1String("validate"));
+}
+
+QStringList ModuleLoader::readExtraSearchPaths(Item *item, bool *wasSet)
+{
+ QStringList result;
+ const QString propertyName = QLatin1String("qbsSearchPaths");
+ const QStringList paths = m_evaluator->stringListValue(item, propertyName, wasSet);
+ const ValueConstPtr prop = item->property(propertyName);
+ foreach (const QString &path, paths)
+ result += FileInfo::resolvePath(FileInfo::path(prop->location().fileName()), path);
+ return result;
+}
+
+void ModuleLoader::copyProperties(const Item *sourceProject, Item *targetProject)
+{
+ if (!sourceProject)
+ return;
+ const QList<PropertyDeclaration> &builtinProjectProperties
+ = m_reader->builtins()->declarationsForType(QLatin1String("Project")).properties();
+ QSet<QString> builtinProjectPropertyNames;
+ foreach (const PropertyDeclaration &p, builtinProjectProperties)
+ builtinProjectPropertyNames << p.name;
+
+ for (Item::PropertyDeclarationMap::ConstIterator it
+ = sourceProject->propertyDeclarations().constBegin();
+ it != sourceProject->propertyDeclarations().constEnd(); ++it) {
+
+ // We must not inherit built-in properties such as "name",
+ // but "qbsSearchPaths" is an exception.
+ if (it.key() == QLatin1String("qbsSearchPaths")) {
+ const JSSourceValueConstPtr &v
+ = targetProject->property(it.key()).dynamicCast<const JSSourceValue>();
+ QBS_ASSERT(v, continue);
+ if (v->sourceCode() == QLatin1String("undefined"))
+ copyProperty(it.key(), sourceProject, targetProject);
+ continue;
+ }
+
+ if (builtinProjectPropertyNames.contains(it.key()))
+ continue;
+
+ if (targetProject->properties().contains(it.key()))
+ continue; // Ignore stuff the target project already has.
+
+ targetProject->setPropertyDeclaration(it.key(), it.value());
+ copyProperty(it.key(), sourceProject, targetProject);
+ }
+}
+
+Item *ModuleLoader::wrapWithProject(Item *item)
+{
+ Item *prj = Item::create(item->pool());
+ prj->setChildren(QList<Item *>() << item);
+ item->setParent(prj);
+ prj->setTypeName("Project");
+ prj->setFile(item->file());
+ prj->setLocation(item->location());
+ m_reader->builtins()->setupItemForBuiltinType(prj);
+ return prj;
+}
+
+QString ModuleLoader::findExistingModulePath(const QString &searchPath,
+ const QStringList &moduleName)
+{
+ QString dirPath = searchPath;
+ foreach (const QString &moduleNamePart, moduleName) {
+ dirPath = FileInfo::resolvePath(dirPath, moduleNamePart);
+ if (!FileInfo::exists(dirPath) || !FileInfo::isFileCaseCorrect(dirPath))
+ return QString();
+ }
+ return dirPath;
+}
+
+void ModuleLoader::copyProperty(const QString &propertyName, const Item *source,
+ Item *destination)
+{
+ destination->setProperty(propertyName, source->property(propertyName));
+}
+
+void ModuleLoader::setScopeForDescendants(Item *item, Item *scope)
+{
+ foreach (Item *child, item->children()) {
+ child->setScope(scope);
+ setScopeForDescendants(child, scope);
+ }
+}
+
+QString ModuleLoader::fullModuleName(const QStringList &moduleName)
+{
+ // Currently the same as the module sub directory.
+ // ### Might be nicer to be a valid JS identifier.
+#if QT_VERSION >= 0x050000
+ return moduleName.join(QLatin1Char('/'));
+#else
+ return moduleName.join(QLatin1String("/"));
+#endif
+}
+
+void ModuleLoader::overrideItemProperties(Item *item, const QString &buildConfigKey,
+ const QVariantMap &buildConfig)
+{
+ const QVariant buildConfigValue = buildConfig.value(buildConfigKey);
+ if (buildConfigValue.isNull())
+ return;
+ const QVariantMap overridden = buildConfigValue.toMap();
+ for (QVariantMap::const_iterator it = overridden.constBegin(); it != overridden.constEnd();
+ ++it) {
+ const PropertyDeclaration decl = item->propertyDeclarations().value(it.key());
+ if (!decl.isValid()) {
+ throw ErrorInfo(
+ Tr::tr("Unknown property: %1.%2").arg(buildConfigKey, it.key()));
+ }
+ item->setProperty(it.key(),
+ VariantValue::create(convertToPropertyType(it.value(), decl.type,
+ QStringList(buildConfigKey), it.key())));
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h
new file mode 100644
index 000000000..f6f62a2c3
--- /dev/null
+++ b/src/lib/corelib/language/moduleloader.h
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_MODULELOADER_H
+#define QBS_MODULELOADER_H
+
+#include "forward_decls.h"
+#include "itempool.h"
+#include <logging/logger.h>
+
+#include <QMap>
+#include <QSet>
+#include <QStringList>
+#include <QVariantMap>
+
+QT_BEGIN_NAMESPACE
+class QScriptContext;
+class QScriptEngine;
+QT_END_NAMESPACE
+
+namespace qbs {
+
+class CodeLocation;
+
+namespace Internal {
+
+class BuiltinDeclarations;
+class Evaluator;
+class Item;
+class ItemReader;
+class ProgressObserver;
+class ScriptEngine;
+
+struct ModuleLoaderResult
+{
+ ModuleLoaderResult()
+ : itemPool(new ItemPool), root(0)
+ {}
+
+ struct ProductInfo
+ {
+ struct Dependency
+ {
+ QString name;
+ bool required;
+ QString failureMessage;
+ };
+
+ QList<Dependency> usedProducts;
+ QList<Dependency> usedProductsFromExportItem;
+ };
+
+ QSharedPointer<ItemPool> itemPool;
+ Item *root;
+ QHash<Item *, ProductInfo> productInfos;
+ QSet<QString> qbsFiles;
+};
+
+/*
+ * Loader stage II. Responsible for
+ * - loading modules and module dependencies,
+ * - project references,
+ * - Probe items.
+ */
+class ModuleLoader
+{
+public:
+ ModuleLoader(ScriptEngine *engine, BuiltinDeclarations *builtins, const Logger &logger);
+ ~ModuleLoader();
+
+ void setProgressObserver(ProgressObserver *progressObserver);
+ 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);
+
+ static QString fullModuleName(const QStringList &moduleName);
+ static void overrideItemProperties(Item *item, const QString &buildConfigKey,
+ const QVariantMap &buildConfig);
+
+private:
+ class ContextBase
+ {
+ public:
+ ContextBase()
+ : item(0), scope(0)
+ {}
+
+ Item *item;
+ Item *scope;
+ QStringList extraSearchPaths;
+ QMap<QString, Item *> moduleItemCache;
+ };
+
+ class ProjectContext : public ContextBase
+ {
+ public:
+ ModuleLoaderResult *result;
+ QString localModuleSearchPath;
+ };
+
+ class ProductContext : public ContextBase
+ {
+ public:
+ ProjectContext *project;
+ ModuleLoaderResult::ProductInfo info;
+ QSet<FileContextConstPtr> filesWithExportItem;
+ QList<Item *> exportItems;
+ };
+
+ class DependsContext
+ {
+ public:
+ ProductContext *product;
+ QList<ModuleLoaderResult::ProductInfo::Dependency> *productDependencies;
+ };
+
+ typedef QPair<Item *, ModuleLoaderResult::ProductInfo::Dependency> ProductDependencyResult;
+ typedef QList<ProductDependencyResult> ProductDependencyResults;
+
+ void handleProject(ModuleLoaderResult *loadResult, Item *item,
+ const QSet<QString> &referencedFilePaths);
+ void handleProduct(ProjectContext *projectContext, Item *item);
+ void handleSubProject(ProjectContext *projectContext, Item *item,
+ const QSet<QString> &referencedFilePaths);
+ void createAdditionalModuleInstancesInProduct(ProductContext *productContext);
+ void handleGroup(ProductContext *productContext, Item *group);
+ void handleArtifact(ProductContext *productContext, Item *item);
+ void deferExportItem(ProductContext *productContext, Item *item);
+ void mergeExportItems(ProductContext *productContext);
+ void propagateModulesFromProduct(ProductContext *productContext, Item *item);
+ void resolveDependencies(DependsContext *productContext, Item *item);
+ class ItemModuleList;
+ void resolveDependsItem(DependsContext *dependsContext, Item *item, Item *dependsItem, ItemModuleList *moduleResults, ProductDependencyResults *productResults);
+ Item *moduleInstanceItem(Item *item, const QStringList &moduleName);
+ Item *loadModule(ProductContext *productContext, Item *item,
+ const CodeLocation &dependsItemLocation, const QString &moduleId,
+ const QStringList &moduleName, bool isBaseModule, bool isRequired);
+ Item *searchAndLoadModuleFile(ProductContext *productContext,
+ const CodeLocation &dependsItemLocation, const QStringList &moduleName,
+ const QStringList &extraSearchPaths, bool isRequired, bool *cacheHit);
+ Item *loadModuleFile(ProductContext *productContext, const QString &fullModuleName,
+ bool isBaseModule, const QString &filePath, bool *cacheHit);
+ void loadBaseModule(ProductContext *productContext, Item *item);
+ void setupBaseModulePrototype(Item *prototype);
+ void instantiateModule(ProductContext *productContext, Item *instanceScope, Item *moduleInstance, Item *modulePrototype, const QStringList &moduleName);
+ void createChildInstances(ProductContext *productContext, Item *instance,
+ Item *prototype, QHash<Item *, Item *> *prototypeInstanceMap) const;
+ void resolveProbes(Item *item);
+ void resolveProbe(Item *parent, Item *probe);
+ void checkCancelation() const;
+ bool checkItemCondition(Item *item);
+ void checkItemTypes(Item *item);
+ void callValidateScript(Item *module);
+ QStringList readExtraSearchPaths(Item *item, bool *wasSet = 0);
+ void copyProperties(const Item *sourceProject, Item *targetProject);
+ Item *wrapWithProject(Item *item);
+ static QString findExistingModulePath(const QString &searchPath,
+ const QStringList &moduleName);
+ static void copyProperty(const QString &propertyName, const Item *source, Item *destination);
+ static void setScopeForDescendants(Item *item, Item *scope);
+
+ ScriptEngine *m_engine;
+ ItemPool *m_pool;
+ Logger m_logger;
+ ProgressObserver *m_progressObserver;
+ ItemReader *m_reader;
+ Evaluator *m_evaluator;
+ QStringList m_moduleSearchPaths;
+ QMap<QString, QStringList> m_moduleDirListCache;
+ QHash<Item *, QSet<QString> > m_validItemPropertyNamesPerItem;
+ QSet<Item *> m_disabledItems;
+ QVariantMap m_overriddenProperties;
+ QVariantMap m_buildConfigProperties;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(qbs::Internal::ModuleLoaderResult::ProductInfo, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(qbs::Internal::ModuleLoaderResult::ProductInfo::Dependency, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+#endif // QBS_MODULELOADER_H
diff --git a/src/lib/corelib/language/preparescriptobserver.cpp b/src/lib/corelib/language/preparescriptobserver.cpp
new file mode 100644
index 000000000..c4f61fd29
--- /dev/null
+++ b/src/lib/corelib/language/preparescriptobserver.cpp
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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 "preparescriptobserver.h"
+
+#include "property.h"
+#include "scriptengine.h"
+
+#include <QScriptValue>
+
+namespace qbs {
+namespace Internal {
+
+PrepareScriptObserver::PrepareScriptObserver(ScriptEngine *engine)
+ : m_engine(engine)
+ , m_productObjectId(-1)
+ , m_projectObjectId(-1)
+{
+}
+
+void PrepareScriptObserver::onPropertyRead(const QScriptValue &object, const QString &name,
+ const QScriptValue &value)
+{
+ if (object.objectId() == m_productObjectId) {
+ m_engine->addPropertyRequestedInScript(
+ Property(QString(), name, value.toVariant(), Property::PropertyInProduct));
+ } else if (object.objectId() == m_projectObjectId) {
+ m_engine->addPropertyRequestedInScript(
+ Property(QString(), name, value.toVariant(), Property::PropertyInProject));
+ }
+
+}
+
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/preparescriptobserver.h b/src/lib/corelib/language/preparescriptobserver.h
new file mode 100644
index 000000000..21b101a2f
--- /dev/null
+++ b/src/lib/corelib/language/preparescriptobserver.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PREPARESCRIPTOBSERVER_H
+#define QBS_PREPARESCRIPTOBSERVER_H
+
+#include "scriptpropertyobserver.h"
+
+namespace qbs {
+namespace Internal {
+class ScriptEngine;
+
+class PrepareScriptObserver : public ScriptPropertyObserver
+{
+public:
+ PrepareScriptObserver(ScriptEngine *engine);
+
+ void setProductObjectId(qint64 productId) { m_productObjectId = productId; }
+ void setProjectObjectId(qint64 projectId) { m_projectObjectId = projectId; }
+
+private:
+ void onPropertyRead(const QScriptValue &object, const QString &name, const QScriptValue &value);
+
+ ScriptEngine * const m_engine;
+ qint64 m_productObjectId;
+ qint64 m_projectObjectId;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp
new file mode 100644
index 000000000..06205c7fe
--- /dev/null
+++ b/src/lib/corelib/language/projectresolver.cpp
@@ -0,0 +1,999 @@
+/****************************************************************************
+**
+** 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 "projectresolver.h"
+
+#include "artifactproperties.h"
+#include "builtindeclarations.h"
+#include "evaluator.h"
+#include "filecontext.h"
+#include "item.h"
+#include "moduleloader.h"
+#include "propertymapinternal.h"
+#include "scriptengine.h"
+#include <jsextensions/moduleproperties.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/progressobserver.h>
+#include <tools/scripttools.h>
+#include <tools/qbsassert.h>
+#include <tools/qttools.h>
+
+#include <QFileInfo>
+#include <QDir>
+#include <QSet>
+#include <set>
+
+namespace qbs {
+namespace Internal {
+
+extern bool debugProperties;
+
+static const FileTag unknownFileTag()
+{
+ static const FileTag tag("unknown-file-tag");
+ return tag;
+}
+
+ProjectResolver::ProjectResolver(ModuleLoader *ldr, const BuiltinDeclarations *builtins,
+ const Logger &logger)
+ : m_evaluator(ldr->evaluator())
+ , m_builtins(builtins)
+ , m_logger(logger)
+ , m_engine(m_evaluator->engine())
+ , m_progressObserver(0)
+{
+}
+
+ProjectResolver::~ProjectResolver()
+{
+}
+
+void ProjectResolver::setProgressObserver(ProgressObserver *observer)
+{
+ m_progressObserver = observer;
+}
+
+static void checkForDuplicateProductNames(const TopLevelProjectConstPtr &project)
+{
+ const QList<ResolvedProductPtr> allProducts = project->allProducts();
+ for (int i = 0; i < allProducts.count(); ++i) {
+ const ResolvedProductConstPtr product1 = allProducts.at(i);
+ const QString productName = product1->name;
+ for (int j = i + 1; j < allProducts.count(); ++j) {
+ const ResolvedProductConstPtr product2 = allProducts.at(j);
+ if (product2->name == productName) {
+ ErrorInfo error;
+ error.append(Tr::tr("Duplicate product name '%1'.").arg(productName));
+ error.append(Tr::tr("First product defined here."), product1->location);
+ error.append(Tr::tr("Second product defined here."), product2->location);
+ throw error;
+ }
+ }
+ }
+}
+
+TopLevelProjectPtr ProjectResolver::resolve(ModuleLoaderResult &loadResult,
+ const SetupProjectParameters &setupParameters)
+{
+ QBS_CHECK(FileInfo::isAbsolute(setupParameters.buildRoot()));
+ if (m_logger.traceEnabled())
+ m_logger.qbsTrace() << "[PR] resolving " << loadResult.root->file()->filePath();
+
+ ProjectContext projectContext;
+ projectContext.loadResult = &loadResult;
+ m_setupParams = setupParameters;
+ m_productContext = 0;
+ m_moduleContext = 0;
+ resolveTopLevelProject(loadResult.root, &projectContext);
+ TopLevelProjectPtr top = projectContext.project.staticCast<TopLevelProject>();
+ checkForDuplicateProductNames(top);
+ top->buildSystemFiles.unite(loadResult.qbsFiles);
+ return top;
+}
+
+void ProjectResolver::checkCancelation() const
+{
+ if (m_progressObserver && m_progressObserver->canceled()) {
+ throw ErrorInfo(Tr::tr("Project resolving canceled for configuration %1.")
+ .arg(TopLevelProject::deriveId(m_setupParams.finalBuildConfigurationTree())));
+ }
+}
+
+QString ProjectResolver::verbatimValue(const ValueConstPtr &value) const
+{
+ QString result;
+ if (value && value->type() == Value::JSSourceValueType) {
+ const JSSourceValueConstPtr sourceValue = value.staticCast<const JSSourceValue>();
+ result = sourceValue->sourceCode();
+ }
+ return result;
+}
+
+QString ProjectResolver::verbatimValue(Item *item, const QString &name) const
+{
+ return verbatimValue(item->property(name));
+}
+
+void ProjectResolver::ignoreItem(Item *item, ProjectContext *projectContext)
+{
+ Q_UNUSED(item);
+ Q_UNUSED(projectContext);
+}
+
+static void makeSubProjectNamesUniqe(const ResolvedProjectPtr &parentProject)
+{
+ QSet<QString> subProjectNames;
+ QSet<ResolvedProjectPtr> projectsInNeedOfNameChange;
+ foreach (const ResolvedProjectPtr &p, parentProject->subProjects) {
+ if (subProjectNames.contains(p->name))
+ projectsInNeedOfNameChange << p;
+ else
+ subProjectNames << p->name;
+ makeSubProjectNamesUniqe(p);
+ }
+ while (!projectsInNeedOfNameChange.isEmpty()) {
+ QSet<ResolvedProjectPtr>::Iterator it = projectsInNeedOfNameChange.begin();
+ while (it != projectsInNeedOfNameChange.end()) {
+ const ResolvedProjectPtr p = *it;
+ p->name += QLatin1Char('_');
+ if (!subProjectNames.contains(p->name)) {
+ subProjectNames << p->name;
+ it = projectsInNeedOfNameChange.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+}
+
+void ProjectResolver::resolveTopLevelProject(Item *item, ProjectContext *projectContext)
+{
+ if (m_progressObserver)
+ m_progressObserver->setMaximum(projectContext->loadResult->productInfos.count());
+ const TopLevelProjectPtr project = TopLevelProject::create();
+ project->setBuildConfiguration(m_setupParams.finalBuildConfigurationTree());
+ project->buildDirectory
+ = TopLevelProject::deriveBuildDirectory(m_setupParams.buildRoot(), project->id());
+ projectContext->project = project;
+ resolveProject(item, projectContext);
+ project->usedEnvironment = m_engine->usedEnvironment();
+ project->fileExistsResults = m_engine->fileExistsResults();
+ project->fileLastModifiedResults = m_engine->fileLastModifiedResults();
+ project->environment = m_engine->environment();
+ project->buildSystemFiles = m_engine->imports();
+ makeSubProjectNamesUniqe(project);
+ resolveProductDependencies(projectContext);
+}
+
+void ProjectResolver::resolveProject(Item *item, ProjectContext *projectContext)
+{
+ checkCancelation();
+
+ projectContext->project->name = m_evaluator->stringValue(item, QLatin1String("name"));
+ projectContext->project->location = item->location();
+ if (projectContext->project->name.isEmpty())
+ projectContext->project->name = FileInfo::baseName(item->location().fileName()); // FIXME: Must also be changed in item?
+ projectContext->project->enabled
+ = m_evaluator->boolValue(item, QLatin1String("condition"));
+ if (!projectContext->project->enabled)
+ return;
+
+ projectContext->dummyModule = ResolvedModule::create();
+
+ QVariantMap projectProperties;
+ for (QMap<QString, PropertyDeclaration>::const_iterator it
+ = item->propertyDeclarations().constBegin();
+ it != item->propertyDeclarations().constEnd(); ++it) {
+ if (it.value().flags.testFlag(PropertyDeclaration::PropertyNotAvailableInConfig))
+ continue;
+ const ValueConstPtr v = item->property(it.key());
+ QBS_ASSERT(v && v->type() != Value::ItemValueType, continue);
+ projectProperties.insert(it.key(), m_evaluator->property(item, it.key()).toVariant());
+ }
+ projectContext->project->setProjectProperties(projectProperties);
+
+ ItemFuncMap mapping;
+ mapping["Project"] = &ProjectResolver::resolveProject;
+ mapping["SubProject"] = &ProjectResolver::resolveSubProject;
+ mapping["Product"] = &ProjectResolver::resolveProduct;
+ mapping["FileTagger"] = &ProjectResolver::resolveFileTagger;
+ mapping["Rule"] = &ProjectResolver::resolveRule;
+
+ foreach (Item *child, item->children())
+ callItemFunction(mapping, child, projectContext);
+
+ foreach (const ResolvedProductPtr &product, projectContext->project->products)
+ postProcess(product, projectContext);
+}
+
+void ProjectResolver::resolveSubProject(Item *item, ProjectResolver::ProjectContext *projectContext)
+{
+ ProjectContext subProjectContext = createProjectContext(projectContext);
+
+ Item * const projectItem = item->child(QLatin1String("Project"));
+ if (projectItem) {
+ resolveProject(projectItem, &subProjectContext);
+ return;
+ }
+
+ // No project item was found, which means the project was disabled.
+ subProjectContext.project->enabled = false;
+ Item * const propertiesItem = item->child(QLatin1String("Properties"));
+ if (propertiesItem) {
+ subProjectContext.project->name
+ = m_evaluator->stringValue(propertiesItem, QLatin1String("name"));
+ }
+}
+
+class ModuleNameEquals
+{
+ QString m_str;
+public:
+ ModuleNameEquals(const QString &str)
+ : m_str(str)
+ {}
+
+ bool operator()(const Item::Module &module)
+ {
+ return module.name.count() == 1 && module.name.first() == m_str;
+ }
+};
+
+void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext)
+{
+ checkCancelation();
+ 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("name", VariantValue::create(product->name));
+ }
+ m_logger.qbsTrace() << "[PR] resolveProduct " << product->name;
+
+ if (std::find_if(item->modules().begin(), item->modules().end(),
+ ModuleNameEquals(product->name)) != item->modules().end()) {
+ throw ErrorInfo(
+ Tr::tr("The product name '%1' collides with a module name.").arg(product->name),
+ item->location());
+ }
+
+ ModuleLoader::overrideItemProperties(item, product->name, m_setupParams.overriddenValuesTree());
+ m_productsByName.insert(product->name, product);
+ product->enabled = m_evaluator->boolValue(item, QLatin1String("condition"));
+ product->additionalFileTags
+ = m_evaluator->fileTagsValue(item, QLatin1String("additionalFileTags"));
+ 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->location = item->location();
+ product->properties = PropertyMapInternal::create();
+ product->properties->setValue(createProductConfig());
+ ModuleProperties::init(m_evaluator->scriptValue(item), product);
+
+ QList<Item *> subItems = item->children();
+ const ValuePtr filesProperty = item->property(QLatin1String("files"));
+ if (filesProperty) {
+ Item *fakeGroup = Item::create(item->pool());
+ fakeGroup->setFile(item->file());
+ fakeGroup->setLocation(item->location());
+ fakeGroup->setScope(item);
+ fakeGroup->setTypeName(QLatin1String("Group"));
+ fakeGroup->setProperty(QLatin1String("name"), VariantValue::create(product->name));
+ fakeGroup->setProperty(QLatin1String("files"), filesProperty);
+ fakeGroup->setProperty(QLatin1String("excludeFiles"),
+ item->property(QLatin1String("excludeFiles")));
+ fakeGroup->setProperty(QLatin1String("overrideTags"), VariantValue::create(false));
+ m_builtins->setupItemForBuiltinType(fakeGroup);
+ subItems.prepend(fakeGroup);
+ }
+
+ ItemFuncMap mapping;
+ mapping["Depends"] = &ProjectResolver::ignoreItem;
+ mapping["Rule"] = &ProjectResolver::resolveRule;
+ mapping["FileTagger"] = &ProjectResolver::resolveFileTagger;
+ mapping["Transformer"] = &ProjectResolver::resolveTransformer;
+ mapping["Group"] = &ProjectResolver::resolveGroup;
+ mapping["Export"] = &ProjectResolver::resolveExport;
+ mapping["Probe"] = &ProjectResolver::ignoreItem;
+
+ foreach (Item *child, subItems)
+ callItemFunction(mapping, child, projectContext);
+
+ foreach (const Item::Module &module, item->modules())
+ resolveModule(module.name, module.item, projectContext);
+
+ m_productContext = 0;
+ if (m_progressObserver)
+ m_progressObserver->incrementProgressValue();
+}
+
+void ProjectResolver::resolveModule(const QStringList &moduleName, Item *item,
+ ProjectContext *projectContext)
+{
+ checkCancelation();
+ if (!m_evaluator->boolValue(item, QLatin1String("present")))
+ return;
+ ModuleContext moduleContext;
+ moduleContext.module = ResolvedModule::create();
+ m_moduleContext = &moduleContext;
+
+ const ResolvedModulePtr &module = moduleContext.module;
+ module->name = ModuleLoader::fullModuleName(moduleName);
+ module->setupBuildEnvironmentScript = scriptFunctionValue(item, "setupBuildEnvironment");
+ module->setupRunEnvironmentScript = scriptFunctionValue(item, "setupRunEnvironment");
+
+ m_productContext->product->additionalFileTags
+ += m_evaluator->fileTagsValue(item, "additionalProductFileTags");
+
+ foreach (const Item::Module &m, item->modules())
+ module->moduleDependencies += ModuleLoader::fullModuleName(m.name);
+
+ m_productContext->product->modules += module;
+
+ ItemFuncMap mapping;
+ mapping["Rule"] = &ProjectResolver::resolveRule;
+ mapping["FileTagger"] = &ProjectResolver::resolveFileTagger;
+ mapping["Transformer"] = &ProjectResolver::resolveTransformer;
+ mapping["PropertyOptions"] = &ProjectResolver::ignoreItem;
+ mapping["Depends"] = &ProjectResolver::ignoreItem;
+ mapping["Probe"] = &ProjectResolver::ignoreItem;
+ foreach (Item *child, item->children())
+ callItemFunction(mapping, child, projectContext);
+
+ m_moduleContext = 0;
+}
+
+static void createSourceArtifact(const ResolvedProductConstPtr &rproduct,
+ const PropertyMapPtr &properties,
+ const QString &fileName,
+ const FileTags &fileTags,
+ bool overrideTags,
+ QList<SourceArtifactPtr> &artifactList)
+{
+ SourceArtifactPtr artifact = SourceArtifact::create();
+ artifact->absoluteFilePath = FileInfo::resolvePath(rproduct->sourceDirectory, fileName);
+ artifact->fileTags = fileTags;
+ artifact->overrideFileTags = overrideTags;
+ artifact->properties = properties;
+ artifactList += artifact;
+}
+
+static bool isSomeModulePropertySet(Item *group)
+{
+ for (QMap<QString, ValuePtr>::const_iterator it = group->properties().constBegin();
+ it != group->properties().constEnd(); ++it)
+ {
+ if (it.value()->type() == Value::ItemValueType) {
+ // An item value is a module value in this case.
+ ItemValuePtr iv = it.value().staticCast<ItemValue>();
+ foreach (ValuePtr ivv, iv->item()->properties()) {
+ if (ivv->type() == Value::JSSourceValueType)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void ProjectResolver::resolveGroup(Item *item, ProjectContext *projectContext)
+{
+ Q_UNUSED(projectContext);
+ checkCancelation();
+ PropertyMapPtr properties = m_productContext->product->properties;
+ if (isSomeModulePropertySet(item)) {
+ properties = PropertyMapInternal::create();
+ properties->setValue(evaluateModuleValues(item));
+ }
+
+ const bool isEnabled = m_evaluator->boolValue(item, QLatin1String("condition"));
+ QStringList files = m_evaluator->stringListValue(item, QLatin1String("files"));
+ const QStringList fileTagsFilter
+ = m_evaluator->stringListValue(item, QLatin1String("fileTagsFilter"));
+ if (!fileTagsFilter.isEmpty()) {
+ if (Q_UNLIKELY(!files.isEmpty()))
+ throw ErrorInfo(Tr::tr("Group.files and Group.fileTagsFilters are exclusive."),
+ item->location());
+ if (!isEnabled)
+ return;
+ ArtifactPropertiesPtr aprops = ArtifactProperties::create();
+ aprops->setFileTagsFilter(FileTags::fromStringList(fileTagsFilter));
+ PropertyMapPtr cfg = PropertyMapInternal::create();
+ cfg->setValue(evaluateModuleValues(item));
+ aprops->setPropertyMapInternal(cfg);
+ m_productContext->product->artifactProperties += aprops;
+ return;
+ }
+ if (Q_UNLIKELY(files.isEmpty() && !item->hasProperty(QLatin1String("files")))) {
+ // Yield an error if Group without files binding is encountered.
+ // An empty files value is OK but a binding must exist.
+ throw ErrorInfo(Tr::tr("Group without files is not allowed."),
+ item->location());
+ }
+ QStringList patterns;
+ for (int i = files.count(); --i >= 0;) {
+ if (FileInfo::isPattern(files[i]))
+ patterns.append(files.takeAt(i));
+ }
+ GroupPtr group = ResolvedGroup::create();
+ group->prefix = m_evaluator->stringValue(item, QLatin1String("prefix"));
+ if (!group->prefix.isEmpty()) {
+ for (int i = files.count(); --i >= 0;)
+ files[i].prepend(group->prefix);
+ }
+ group->location = item->location();
+ group->enabled = isEnabled;
+ group->fileTags = m_evaluator->fileTagsValue(item, QLatin1String("fileTags"));
+ group->overrideTags = m_evaluator->boolValue(item, QLatin1String("overrideTags"));
+
+ if (!patterns.isEmpty()) {
+ SourceWildCards::Ptr wildcards = SourceWildCards::create();
+ wildcards->excludePatterns = m_evaluator->stringListValue(item, "excludeFiles");
+ wildcards->prefix = group->prefix;
+ wildcards->patterns = patterns;
+ QSet<QString> files = wildcards->expandPatterns(group, m_productContext->product->sourceDirectory);
+ foreach (const QString &fileName, files)
+ createSourceArtifact(m_productContext->product, properties, fileName,
+ group->fileTags, group->overrideTags, wildcards->files);
+ group->wildcards = wildcards;
+ }
+
+ foreach (const QString &fileName, files)
+ createSourceArtifact(m_productContext->product, properties, fileName,
+ group->fileTags, group->overrideTags, group->files);
+ ErrorInfo fileError;
+ foreach (const SourceArtifactConstPtr &a, group->files) {
+ if (!FileInfo(a->absoluteFilePath).exists()) {
+ fileError.append(Tr::tr("File '%1' does not exist.")
+ .arg(a->absoluteFilePath), item->property("files")->location());
+ }
+ }
+ if (fileError.hasError())
+ throw ErrorInfo(fileError);
+
+ group->name = m_evaluator->stringValue(item, "name");
+ if (group->name.isEmpty())
+ group->name = Tr::tr("Group %1").arg(m_productContext->product->groups.count());
+ group->properties = properties;
+ m_productContext->product->groups += group;
+}
+
+static QString sourceCodeAsFunction(const JSSourceValueConstPtr &value,
+ const PropertyDeclaration &decl)
+{
+ const QString args = decl.functionArgumentNames.join(QLatin1String(","));
+ if (value->hasFunctionForm()) {
+ // Insert the argument list.
+ QString code = value->sourceCode();
+ code.insert(10, args);
+ // Remove the function application "()" that has been
+ // added in ItemReaderASTVisitor::visitStatement.
+ return code.left(code.length() - 2);
+ } else {
+ return QLatin1String("(function(") + args + QLatin1String("){return ")
+ + value->sourceCode() + QLatin1String(";})");
+ }
+}
+
+ScriptFunctionPtr ProjectResolver::scriptFunctionValue(Item *item, const QString &name) const
+{
+ ScriptFunctionPtr script = ScriptFunction::create();
+ JSSourceValuePtr value = item->sourceProperty(name);
+ if (value) {
+ const PropertyDeclaration decl = item->propertyDeclaration(name);
+ script->sourceCode = sourceCodeAsFunction(value, decl);
+ script->argumentNames = decl.functionArgumentNames;
+ script->location = value->location();
+ script->fileContext = resolvedFileContext(value->file());
+ }
+ return script;
+}
+
+ResolvedFileContextPtr ProjectResolver::resolvedFileContext(const FileContextConstPtr &ctx) const
+{
+ ResolvedFileContextPtr &result = m_fileContextMap[ctx];
+ if (!result) {
+ result = ResolvedFileContext::create();
+ result->filePath = ctx->filePath();
+ result->jsExtensions = ctx->jsExtensions();
+ result->jsImports = ctx->jsImports();
+ }
+ return result;
+}
+
+void ProjectResolver::resolveRule(Item *item, ProjectContext *projectContext)
+{
+ checkCancelation();
+
+ if (!m_evaluator->boolValue(item, QLatin1String("condition")))
+ return;
+
+ RulePtr rule = Rule::create();
+
+ // read artifacts
+ bool hasAlwaysUpdatedArtifact = false;
+ foreach (Item *child, item->children()) {
+ if (Q_UNLIKELY(child->typeName() != QLatin1String("Artifact")))
+ throw ErrorInfo(Tr::tr("'Rule' can only have children of type 'Artifact'."),
+ child->location());
+
+ resolveRuleArtifact(rule, child, &hasAlwaysUpdatedArtifact);
+ }
+
+ if (Q_UNLIKELY(!hasAlwaysUpdatedArtifact))
+ throw ErrorInfo(Tr::tr("At least one output artifact of a rule "
+ "must have alwaysUpdated set to true."),
+ item->location());
+
+ rule->script = scriptFunctionValue(item, QLatin1String("prepare"));
+ rule->multiplex = m_evaluator->boolValue(item, QLatin1String("multiplex"));
+ rule->inputs = m_evaluator->fileTagsValue(item, "inputs");
+ rule->usings = m_evaluator->fileTagsValue(item, "usings");
+ rule->auxiliaryInputs
+ = m_evaluator->fileTagsValue(item, QLatin1String("auxiliaryInputs"));
+ rule->explicitlyDependsOn = m_evaluator->fileTagsValue(item, "explicitlyDependsOn");
+ rule->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
+ if (m_productContext)
+ m_productContext->product->rules += rule;
+ else
+ projectContext->rules += rule;
+}
+
+class StringListLess
+{
+public:
+ bool operator()(const QStringList &lhs, const QStringList &rhs)
+ {
+ const int c = qMin(lhs.count(), rhs.count());
+ for (int i = 0; i < c; ++i) {
+ int n = lhs.at(i).compare(rhs.at(i));
+ if (n < 0)
+ return true;
+ if (n > 0)
+ return false;
+ }
+ return lhs.count() < rhs.count();
+ }
+};
+
+class StringListSet : public std::set<QStringList, StringListLess>
+{
+public:
+ typedef std::pair<iterator, bool> InsertResult;
+};
+
+void ProjectResolver::resolveRuleArtifact(const RulePtr &rule, Item *item,
+ bool *hasAlwaysUpdatedArtifact)
+{
+ if (!m_evaluator->boolValue(item, QLatin1String("condition")))
+ return;
+ RuleArtifactPtr artifact = RuleArtifact::create();
+ rule->artifacts += artifact;
+ artifact->fileName = verbatimValue(item, "fileName");
+ artifact->fileTags = m_evaluator->fileTagsValue(item, "fileTags");
+ artifact->alwaysUpdated = m_evaluator->boolValue(item, "alwaysUpdated");
+ if (artifact->alwaysUpdated)
+ *hasAlwaysUpdatedArtifact = true;
+
+ StringListSet seenBindings;
+ for (Item *obj = item; obj; obj = obj->prototype()) {
+ for (QMap<QString, ValuePtr>::const_iterator it = obj->properties().constBegin();
+ it != obj->properties().constEnd(); ++it)
+ {
+ if (it.value()->type() != Value::ItemValueType)
+ continue;
+ resolveRuleArtifactBinding(artifact, it.value().staticCast<ItemValue>()->item(),
+ QStringList(it.key()), &seenBindings);
+ }
+ }
+}
+
+void ProjectResolver::resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArtifact,
+ Item *item,
+ const QStringList &namePrefix,
+ StringListSet *seenBindings)
+{
+ for (QMap<QString, ValuePtr>::const_iterator it = item->properties().constBegin();
+ it != item->properties().constEnd(); ++it)
+ {
+ const QStringList name = QStringList(namePrefix) << it.key();
+ if (it.value()->type() == Value::ItemValueType) {
+ resolveRuleArtifactBinding(ruleArtifact,
+ it.value().staticCast<ItemValue>()->item(), name,
+ seenBindings);
+ } else if (it.value()->type() == Value::JSSourceValueType) {
+ const StringListSet::InsertResult insertResult = seenBindings->insert(name);
+ if (!insertResult.second)
+ continue;
+ JSSourceValuePtr sourceValue = it.value().staticCast<JSSourceValue>();
+ RuleArtifact::Binding rab;
+ rab.name = name;
+ rab.code = sourceValue->sourceCode();
+ rab.location = sourceValue->location();
+ ruleArtifact->bindings += rab;
+ } else {
+ QBS_ASSERT(!"unexpected value type", continue);
+ }
+ }
+}
+
+void ProjectResolver::resolveFileTagger(Item *item, ProjectContext *projectContext)
+{
+ checkCancelation();
+ QList<FileTaggerConstPtr> &fileTaggers = m_productContext
+ ? m_productContext->product->fileTaggers : projectContext->fileTaggers;
+ QStringList patterns = m_evaluator->stringListValue(item, QLatin1String("patterns"));
+ const FileTags fileTags = m_evaluator->fileTagsValue(item, "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());
+ }
+ fileTaggers += FileTagger::create(patterns, fileTags);
+}
+
+void ProjectResolver::resolveTransformer(Item *item, ProjectContext *projectContext)
+{
+ checkCancelation();
+ if (!m_evaluator->boolValue(item, "condition")) {
+ m_logger.qbsTrace() << "[PR] transformer condition is false";
+ return;
+ }
+
+ ResolvedTransformerPtr rtrafo = ResolvedTransformer::create();
+ rtrafo->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
+ rtrafo->inputs = m_evaluator->stringListValue(item, "inputs");
+ for (int i = 0; i < rtrafo->inputs.count(); ++i)
+ rtrafo->inputs[i] = FileInfo::resolvePath(m_productContext->product->sourceDirectory, rtrafo->inputs.at(i));
+ rtrafo->transform = scriptFunctionValue(item, QLatin1String("prepare"));
+ rtrafo->explicitlyDependsOn = m_evaluator->fileTagsValue(item, "explicitlyDependsOn");
+
+ foreach (const Item *child, item->children()) {
+ if (Q_UNLIKELY(child->typeName() != QLatin1String("Artifact")))
+ throw ErrorInfo(Tr::tr("Transformer: wrong child type '%0'.").arg(child->typeName()));
+ SourceArtifactPtr artifact = SourceArtifact::create();
+ artifact->properties = m_productContext->product->properties;
+ QString fileName = m_evaluator->stringValue(child, "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->fileTags = m_evaluator->fileTagsValue(child, "fileTags");
+ if (artifact->fileTags.isEmpty())
+ artifact->fileTags.insert(unknownFileTag());
+ rtrafo->outputs += artifact;
+ }
+
+ m_productContext->product->transformers += rtrafo;
+}
+
+void ProjectResolver::resolveExport(Item *item, ProjectContext *projectContext)
+{
+ Q_UNUSED(projectContext);
+ checkCancelation();
+ const QString &productName = m_productContext->product->name;
+ m_exports[productName] = evaluateModuleValues(item);
+}
+
+static void insertExportedConfig(const QString &usedProductName,
+ const QVariantMap &exportedConfig,
+ const PropertyMapPtr &propertyMap)
+{
+ QVariantMap properties = propertyMap->value();
+ QVariant &modulesEntry = properties[QLatin1String("modules")];
+ QVariantMap modules = modulesEntry.toMap();
+ modules.insert(usedProductName, exportedConfig);
+ modulesEntry = modules;
+ propertyMap->setValue(properties);
+}
+
+static void addUsedProducts(ModuleLoaderResult::ProductInfo *productInfo,
+ const ModuleLoaderResult::ProductInfo &usedProductInfo,
+ bool *productsAdded)
+{
+ int oldCount = productInfo->usedProducts.count();
+ QSet<QString> usedProductNames;
+ foreach (const ModuleLoaderResult::ProductInfo::Dependency &usedProduct,
+ productInfo->usedProducts)
+ usedProductNames += usedProduct.name;
+ foreach (const ModuleLoaderResult::ProductInfo::Dependency &usedProduct,
+ usedProductInfo.usedProductsFromExportItem) {
+ if (!usedProductNames.contains(usedProduct.name))
+ productInfo->usedProducts += usedProduct;
+ }
+ *productsAdded = (oldCount != productInfo->usedProducts.count());
+}
+
+void ProjectResolver::resolveProductDependencies(ProjectContext *projectContext)
+{
+ // Collect product dependencies from Export items.
+ bool productDependenciesAdded;
+ QList<ResolvedProductPtr> allProducts = projectContext->project->allProducts();
+ do {
+ productDependenciesAdded = false;
+ foreach (const ResolvedProductPtr &rproduct, allProducts) {
+ if (!rproduct->enabled)
+ continue;
+ Item *productItem = m_productItemMap.value(rproduct);
+ ModuleLoaderResult::ProductInfo &productInfo
+ = projectContext->loadResult->productInfos[productItem];
+ foreach (const ModuleLoaderResult::ProductInfo::Dependency &dependency,
+ productInfo.usedProducts) {
+ ResolvedProductPtr usedProduct
+ = m_productsByName.value(dependency.name);
+ if (Q_UNLIKELY(!usedProduct))
+ throw ErrorInfo(Tr::tr("Product dependency '%1' not found.").arg(dependency.name),
+ productItem->location());
+ Item *usedProductItem = m_productItemMap.value(usedProduct);
+ const ModuleLoaderResult::ProductInfo usedProductInfo
+ = projectContext->loadResult->productInfos.value(usedProductItem);
+ bool added;
+ addUsedProducts(&productInfo, usedProductInfo, &added);
+ if (added)
+ productDependenciesAdded = true;
+ }
+ }
+ } while (productDependenciesAdded);
+
+ // Resolve all inter-product dependencies.
+ foreach (const ResolvedProductPtr &rproduct, allProducts) {
+ if (!rproduct->enabled)
+ continue;
+ Item *productItem = m_productItemMap.value(rproduct);
+ foreach (const ModuleLoaderResult::ProductInfo::Dependency &dependency,
+ projectContext->loadResult->productInfos.value(productItem).usedProducts) {
+ const QString &usedProductName = dependency.name;
+ ResolvedProductPtr usedProduct = m_productsByName.value(usedProductName);
+ if (Q_UNLIKELY(!usedProduct))
+ throw ErrorInfo(Tr::tr("Product dependency '%1' not found.").arg(usedProductName),
+ productItem->location());
+ rproduct->dependencies.insert(usedProduct);
+
+ // insert the configuration of the Export item into the product's configuration
+ const QVariantMap exportedConfig = m_exports.value(usedProductName);
+ if (exportedConfig.isEmpty())
+ continue;
+
+ insertExportedConfig(usedProductName, exportedConfig, rproduct->properties);
+
+ // insert the configuration of the Export item into the artifact configurations
+ foreach (SourceArtifactPtr artifact, rproduct->allEnabledFiles()) {
+ if (artifact->properties != rproduct->properties)
+ insertExportedConfig(usedProductName, exportedConfig,
+ artifact->properties);
+ }
+ }
+ }
+}
+
+void ProjectResolver::postProcess(const ResolvedProductPtr &product,
+ ProjectContext *projectContext) const
+{
+ product->fileTaggers += projectContext->fileTaggers;
+ foreach (const RulePtr &rule, projectContext->rules)
+ product->rules += rule;
+ applyFileTaggers(product);
+}
+
+void ProjectResolver::applyFileTaggers(const ResolvedProductPtr &product) const
+{
+ foreach (const SourceArtifactPtr &artifact, product->allEnabledFiles())
+ applyFileTaggers(artifact, product, m_logger);
+}
+
+void ProjectResolver::applyFileTaggers(const SourceArtifactPtr &artifact,
+ const ResolvedProductConstPtr &product, const Logger &logger)
+{
+ if (!artifact->overrideFileTags || artifact->fileTags.isEmpty()) {
+ const QString fileName = FileInfo::fileName(artifact->absoluteFilePath);
+ const FileTags fileTags = product->fileTagsForFileName(fileName);
+ artifact->fileTags.unite(fileTags);
+ if (artifact->fileTags.isEmpty())
+ artifact->fileTags.insert(unknownFileTag());
+ if (logger.traceEnabled())
+ logger.qbsTrace() << "[PR] adding file tags " << artifact->fileTags
+ << " to " << fileName;
+ }
+}
+
+QVariantMap ProjectResolver::evaluateModuleValues(Item *item) const
+{
+ QVariantMap modules;
+ evaluateModuleValues(item, &modules);
+ QVariantMap result;
+ result[QLatin1String("modules")] = modules;
+ return result;
+}
+
+void ProjectResolver::evaluateModuleValues(Item *item, QVariantMap *modulesMap) const
+{
+ checkCancelation();
+ for (Item::Modules::const_iterator it = item->modules().constBegin();
+ it != item->modules().constEnd(); ++it)
+ {
+ QVariantMap depmods;
+ const Item::Module &module = *it;
+ evaluateModuleValues(module.item, &depmods);
+ QVariantMap dep = evaluateProperties(module.item);
+ dep.insert("modules", depmods);
+ modulesMap->insert(ModuleLoader::fullModuleName(module.name), dep);
+ }
+}
+
+QVariantMap ProjectResolver::evaluateProperties(Item *item) const
+{
+ const QVariantMap tmplt;
+ return evaluateProperties(item, item, tmplt);
+}
+
+QVariantMap ProjectResolver::evaluateProperties(Item *item,
+ Item *propertiesContainer,
+ const QVariantMap &tmplt) const
+{
+ QVariantMap result = tmplt;
+ for (QMap<QString, ValuePtr>::const_iterator it = propertiesContainer->properties().begin();
+ it != propertiesContainer->properties().end(); ++it)
+ {
+ checkCancelation();
+ switch (it.value()->type()) {
+ case Value::ItemValueType:
+ {
+ // Ignore items. Those point to module instances
+ // and are handled in evaluateModuleValues().
+ break;
+ }
+ case Value::JSSourceValueType:
+ {
+ if (result.contains(it.key()))
+ break;
+ PropertyDeclaration pd;
+ for (Item *obj = item; obj; obj = obj->prototype()) {
+ pd = obj->propertyDeclarations().value(it.key());
+ if (pd.isValid())
+ break;
+ }
+ if (pd.type == PropertyDeclaration::Verbatim
+ || pd.flags.testFlag(PropertyDeclaration::PropertyNotAvailableInConfig))
+ {
+ break;
+ }
+ const QScriptValue scriptValue = m_evaluator->property(item, it.key());
+ if (Q_UNLIKELY(m_evaluator->engine()->hasErrorOrException(scriptValue)))
+ throw ErrorInfo(scriptValue.toString(), it.value()->location());
+
+ // NOTE: Loses type information if scriptValue.isUndefined == true,
+ // as such QScriptValues become invalid QVariants.
+ QVariant v = scriptValue.toVariant();
+
+ if (pd.type == PropertyDeclaration::Path)
+ v = convertPathProperty(v.toString(),
+ m_productContext->product->sourceDirectory);
+ else if (pd.type == PropertyDeclaration::PathList)
+ v = convertPathListProperty(v.toStringList(),
+ m_productContext->product->sourceDirectory);
+ else if (pd.type == PropertyDeclaration::StringList)
+ v = v.toStringList();
+ result[it.key()] = v;
+ break;
+ }
+ case Value::VariantValueType:
+ {
+ if (result.contains(it.key()))
+ break;
+ VariantValuePtr vvp = it.value().staticCast<VariantValue>();
+ result[it.key()] = vvp->value();
+ break;
+ }
+ case Value::BuiltinValueType:
+ // ignore
+ break;
+ }
+ }
+ return propertiesContainer->prototype()
+ ? evaluateProperties(item, propertiesContainer->prototype(), result)
+ : result;
+}
+
+QVariantMap ProjectResolver::createProductConfig() const
+{
+ QVariantMap cfg = evaluateModuleValues(m_productContext->item);
+ cfg = evaluateProperties(m_productContext->item, m_productContext->item, cfg);
+ return cfg;
+}
+
+QString ProjectResolver::convertPathProperty(const QString &path, const QString &dirPath) const
+{
+ return path.isEmpty() ? path : QDir::cleanPath(FileInfo::resolvePath(dirPath, path));
+}
+
+QStringList ProjectResolver::convertPathListProperty(const QStringList &paths,
+ const QString &dirPath) const
+{
+ QStringList result;
+ foreach (const QString &path, paths)
+ result += convertPathProperty(path, dirPath);
+ return result;
+}
+
+void ProjectResolver::callItemFunction(const ItemFuncMap &mappings, Item *item,
+ ProjectContext *projectContext)
+{
+ const QByteArray typeName = item->typeName().toLocal8Bit();
+ ItemFuncPtr f = mappings.value(typeName);
+ QBS_CHECK(f);
+ if (typeName == "Project") {
+ ProjectContext subProjectContext = createProjectContext(projectContext);
+ (this->*f)(item, &subProjectContext);
+ } else {
+ (this->*f)(item, projectContext);
+ }
+}
+
+ProjectResolver::ProjectContext ProjectResolver::createProjectContext(ProjectContext *parentProjectContext) const
+{
+ ProjectContext subProjectContext;
+ subProjectContext.project = ResolvedProject::create();
+ parentProjectContext->project->subProjects += subProjectContext.project;
+ subProjectContext.project->parentProject = parentProjectContext->project;
+ subProjectContext.loadResult = parentProjectContext->loadResult;
+ return subProjectContext;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/projectresolver.h b/src/lib/corelib/language/projectresolver.h
new file mode 100644
index 000000000..d468c8d5e
--- /dev/null
+++ b/src/lib/corelib/language/projectresolver.h
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef PROJECTRESOLVER_H
+#define PROJECTRESOLVER_H
+
+#include "evaluator.h"
+#include "filetags.h"
+#include "language.h"
+#include <logging/logger.h>
+#include <tools/setupprojectparameters.h>
+
+#include <QMap>
+#include <QSet>
+
+namespace qbs {
+namespace Internal {
+
+class BuiltinDeclarations;
+class Evaluator;
+class Item;
+class ModuleLoader;
+class ProgressObserver;
+class ScriptEngine;
+class StringListSet;
+struct ModuleLoaderResult;
+
+class ProjectResolver
+{
+public:
+ ProjectResolver(ModuleLoader *ldr, const BuiltinDeclarations *builtins, const Logger &logger);
+ ~ProjectResolver();
+
+ void setProgressObserver(ProgressObserver *observer);
+ TopLevelProjectPtr resolve(ModuleLoaderResult &loadResult,
+ const SetupProjectParameters &setupParameters);
+
+ static void applyFileTaggers(const SourceArtifactPtr &artifact,
+ const ResolvedProductConstPtr &product, const Logger &logger);
+
+private:
+ struct ProjectContext
+ {
+ ResolvedProjectPtr project;
+ QList<FileTaggerConstPtr> fileTaggers;
+ ModuleLoaderResult *loadResult;
+ QList<RulePtr> rules;
+ ResolvedModulePtr dummyModule;
+ };
+
+ struct ProductContext
+ {
+ ResolvedProductPtr product;
+ Item *item;
+ };
+
+ struct ModuleContext
+ {
+ ResolvedModulePtr module;
+ };
+
+ void checkCancelation() const;
+ QString verbatimValue(const ValueConstPtr &value) const;
+ QString verbatimValue(Item *item, const QString &name) const;
+ ScriptFunctionPtr scriptFunctionValue(Item *item, const QString &name) const;
+ ResolvedFileContextPtr resolvedFileContext(const FileContextConstPtr &ctx) const;
+ void ignoreItem(Item *item, ProjectContext *projectContext);
+ void resolveTopLevelProject(Item *item, ProjectContext *projectContext);
+ void resolveProject(Item *item, ProjectContext *projectContext);
+ void resolveSubProject(Item *item, ProjectContext *projectContext);
+ void resolveProduct(Item *item, ProjectContext *projectContext);
+ void resolveModule(const QStringList &moduleName, Item *item, ProjectContext *projectContext);
+ void resolveGroup(Item *item, ProjectContext *projectContext);
+ void resolveRule(Item *item, ProjectContext *projectContext);
+ void resolveRuleArtifact(const RulePtr &rule, Item *item, bool *hasAlwaysUpdatedArtifact);
+ static void resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArtifact, Item *item,
+ const QStringList &namePrefix,
+ StringListSet *seenBindings);
+ void resolveFileTagger(Item *item, ProjectContext *projectContext);
+ void resolveTransformer(Item *item, ProjectContext *projectContext);
+ void resolveExport(Item *item, ProjectContext *projectContext);
+ void resolveProductDependencies(ProjectContext *projectContext);
+ void postProcess(const ResolvedProductPtr &product, ProjectContext *projectContext) const;
+ void applyFileTaggers(const ResolvedProductPtr &product) const;
+ QVariantMap evaluateModuleValues(Item *item) const;
+ void evaluateModuleValues(Item *item, QVariantMap *modulesMap) const;
+ QVariantMap evaluateProperties(Item *item) const;
+ QVariantMap evaluateProperties(Item *item, Item *propertiesContainer,
+ const QVariantMap &tmplt) const;
+ QVariantMap createProductConfig() const;
+ QString convertPathProperty(const QString &path, const QString &dirPath) const;
+ QStringList convertPathListProperty(const QStringList &paths, const QString &dirPath) const;
+ ProjectContext createProjectContext(ProjectContext *parentProjectContext) const;
+
+ Evaluator *m_evaluator;
+ const BuiltinDeclarations *m_builtins;
+ Logger m_logger;
+ ScriptEngine *m_engine;
+ ProgressObserver *m_progressObserver;
+ ProductContext *m_productContext;
+ ModuleContext *m_moduleContext;
+ QMap<QString, ResolvedProductPtr> m_productsByName;
+ QHash<ResolvedProductPtr, Item *> m_productItemMap;
+ mutable QHash<FileContextConstPtr, ResolvedFileContextPtr> m_fileContextMap;
+ QMap<QString, QVariantMap> m_exports;
+ SetupProjectParameters m_setupParams;
+
+ typedef void (ProjectResolver::*ItemFuncPtr)(Item *item, ProjectContext *projectContext);
+ typedef QMap<QByteArray, ItemFuncPtr> ItemFuncMap;
+ void callItemFunction(const ItemFuncMap &mappings, Item *item, ProjectContext *projectContext);
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // PROJECTRESOLVER_H
diff --git a/src/lib/corelib/language/property.h b/src/lib/corelib/language/property.h
new file mode 100644
index 000000000..b989e5316
--- /dev/null
+++ b/src/lib/corelib/language/property.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROPERTY_H
+#define QBS_PROPERTY_H
+
+#include <tools/qbsassert.h>
+
+#include <QSet>
+#include <QString>
+#include <QVariant>
+
+namespace qbs {
+namespace Internal {
+
+class Property
+{
+public:
+ enum Kind
+ {
+ PropertyInModule,
+ PropertyInProduct,
+ PropertyInProject
+ };
+
+ Property()
+ : kind(PropertyInModule)
+ {
+ }
+
+ Property(const QString &m, const QString &p, const QVariant &v, Kind k = PropertyInModule)
+ : moduleName(m), propertyName(p), value(v), kind(k)
+ {
+ QBS_CHECK(!moduleName.contains(QLatin1Char('.')));
+ }
+
+ QString moduleName;
+ QString propertyName;
+ QVariant value;
+ Kind kind;
+};
+
+inline bool operator==(const Property &p1, const Property &p2)
+{
+ return p1.moduleName == p2.moduleName && p1.propertyName == p2.propertyName;
+}
+
+inline uint qHash(const Property &p)
+{
+ return QT_PREPEND_NAMESPACE(qHash)(p.moduleName + p.propertyName);
+}
+
+typedef QSet<Property> PropertyList;
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/language/propertydeclaration.cpp b/src/lib/corelib/language/propertydeclaration.cpp
new file mode 100644
index 000000000..79236e6da
--- /dev/null
+++ b/src/lib/corelib/language/propertydeclaration.cpp
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** 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 "propertydeclaration.h"
+
+namespace qbs {
+namespace Internal {
+
+PropertyDeclaration::PropertyDeclaration()
+ : type(UnknownType)
+ , flags(DefaultFlags)
+{
+}
+
+PropertyDeclaration::PropertyDeclaration(const QString &name, Type type, Flags flags)
+ : name(name)
+ , type(type)
+ , flags(flags)
+{
+}
+
+PropertyDeclaration::~PropertyDeclaration()
+{
+}
+
+bool PropertyDeclaration::isValid() const
+{
+ return type != UnknownType;
+}
+
+PropertyDeclaration::Type PropertyDeclaration::propertyTypeFromString(const QString &typeName)
+{
+ if (typeName == QLatin1String("bool"))
+ return PropertyDeclaration::Boolean;
+ if (typeName == QLatin1String("int"))
+ return PropertyDeclaration::Integer;
+ if (typeName == QLatin1String("path"))
+ return PropertyDeclaration::Path;
+ if (typeName == QLatin1String("pathList"))
+ return PropertyDeclaration::PathList;
+ if (typeName == QLatin1String("string"))
+ return PropertyDeclaration::String;
+ if (typeName == QLatin1String("stringList"))
+ return PropertyDeclaration::StringList;
+ if (typeName == QLatin1String("var") || typeName == QLatin1String("variant"))
+ return PropertyDeclaration::Variant;
+ return PropertyDeclaration::UnknownType;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/propertydeclaration.h b/src/lib/corelib/language/propertydeclaration.h
new file mode 100644
index 000000000..441e42af9
--- /dev/null
+++ b/src/lib/corelib/language/propertydeclaration.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_PROPERTYDECLARATION_H
+#define QBS_PROPERTYDECLARATION_H
+
+#include <QString>
+#include <QStringList>
+#include <QScriptValue>
+
+namespace qbs {
+namespace Internal {
+
+class PropertyDeclaration
+{
+public:
+ enum Type
+ {
+ UnknownType,
+ Boolean,
+ Integer,
+ Path,
+ PathList,
+ String,
+ StringList,
+ Variant,
+ Verbatim
+ };
+
+ enum Flag
+ {
+ DefaultFlags = 0,
+ ListProperty = 0x1,
+ PropertyNotAvailableInConfig = 0x2 // Is this property part of a project, product or file configuration?
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ PropertyDeclaration();
+ PropertyDeclaration(const QString &name, Type type, Flags flags = DefaultFlags);
+ ~PropertyDeclaration();
+
+ bool isValid() const;
+
+ static Type propertyTypeFromString(const QString &typeName);
+
+ QString name;
+ Type type;
+ Flags flags;
+ QScriptValue allowedValues;
+ QString description;
+ QString initialValueSource;
+ QStringList functionArgumentNames;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PROPERTYDECLARATION_H
diff --git a/src/lib/corelib/language/propertymapinternal.cpp b/src/lib/corelib/language/propertymapinternal.cpp
new file mode 100644
index 000000000..063f9e947
--- /dev/null
+++ b/src/lib/corelib/language/propertymapinternal.cpp
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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 "propertymapinternal.h"
+#include <tools/propertyfinder.h>
+#include <tools/scripttools.h>
+
+namespace qbs {
+namespace Internal {
+
+/*!
+ * \class PropertyMapInternal
+ * \brief The \c PropertyMapInternal class contains a set of properties and their values.
+ * An instance of this class is attached to every \c ResolvedProduct.
+ * \c ResolvedGroups inherit their properties from the respective \c ResolvedProduct, \c SourceArtifacts
+ * inherit theirs from the respective \c ResolvedGroup. \c ResolvedGroups can override the value of an
+ * inherited property, \c SourceArtifacts cannot. If a property value is overridden, a new
+ * \c PropertyMapInternal object is allocated, otherwise the pointer is shared.
+ * \sa ResolvedGroup
+ * \sa ResolvedProduct
+ * \sa SourceArtifact
+ */
+PropertyMapInternal::PropertyMapInternal()
+{
+}
+
+PropertyMapInternal::PropertyMapInternal(const PropertyMapInternal &other)
+ : PersistentObject(other), m_value(other.m_value)
+{
+}
+
+QVariant PropertyMapInternal::qbsPropertyValue(const QString &key) const
+{
+ return PropertyFinder().propertyValue(value(), QLatin1String("qbs"), key);
+}
+
+void PropertyMapInternal::setValue(const QVariantMap &map)
+{
+ m_value = map;
+}
+
+static QString toJSLiteral(const QVariantMap &vm, int level = 0)
+{
+ QString indent;
+ for (int i = 0; i < level; ++i)
+ indent += QLatin1String(" ");
+ QString str;
+ for (QVariantMap::const_iterator it = vm.begin(); it != vm.end(); ++it) {
+ if (it.value().type() == QVariant::Map) {
+ str += indent + it.key() + QLatin1String(": {\n");
+ str += toJSLiteral(it.value().toMap(), level + 1);
+ str += indent + QLatin1String("}\n");
+ } else {
+ str += indent + it.key() + QLatin1String(": ") + toJSLiteral(it.value())
+ + QLatin1Char('\n');
+ }
+ }
+ return str;
+}
+
+QString PropertyMapInternal::toJSLiteral() const
+{
+ return qbs::Internal::toJSLiteral(m_value);
+}
+
+void PropertyMapInternal::load(PersistentPool &pool)
+{
+ pool.stream() >> m_value;
+}
+
+void PropertyMapInternal::store(PersistentPool &pool) const
+{
+ pool.stream() << m_value;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/propertymapinternal.h b/src/lib/corelib/language/propertymapinternal.h
new file mode 100644
index 000000000..329f6ddb2
--- /dev/null
+++ b/src/lib/corelib/language/propertymapinternal.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_PROPERTYMAPINTERNAL_H
+#define QBS_PROPERTYMAPINTERNAL_H
+
+#include "forward_decls.h"
+#include <tools/persistence.h>
+#include <QVariantMap>
+
+namespace qbs {
+namespace Internal {
+
+class PropertyMapInternal : public PersistentObject
+{
+public:
+ static PropertyMapPtr create() { return PropertyMapPtr(new PropertyMapInternal); }
+ PropertyMapPtr clone() const { return PropertyMapPtr(new PropertyMapInternal(*this)); }
+
+ const QVariantMap &value() const { return m_value; }
+ QVariant qbsPropertyValue(const QString &key) const; // Convenience function.
+ void setValue(const QVariantMap &value);
+ QString toJSLiteral() const;
+
+private:
+ PropertyMapInternal();
+ PropertyMapInternal(const PropertyMapInternal &other);
+
+ void load(PersistentPool &);
+ void store(PersistentPool &) const;
+
+ QVariantMap m_value;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PROPERTYMAPINTERNAL_H
diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp
new file mode 100644
index 000000000..e7f475d4c
--- /dev/null
+++ b/src/lib/corelib/language/scriptengine.cpp
@@ -0,0 +1,314 @@
+/****************************************************************************
+**
+** 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 "scriptengine.h"
+
+#include "item.h"
+#include "filecontext.h"
+#include "propertymapinternal.h"
+#include "scriptpropertyobserver.h"
+#include <buildgraph/artifact.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+
+#include <QDebug>
+#include <QFile>
+#include <QScriptProgram>
+#include <QScriptValueIterator>
+#include <QSet>
+#include <QTextStream>
+
+namespace qbs {
+namespace Internal {
+
+const bool debugJSImports = false;
+
+ScriptEngine::ScriptEngine(const Logger &logger, QObject *parent)
+ : QScriptEngine(parent), m_logger(logger)
+{
+ QScriptValue objectProto = globalObject().property(QLatin1String("Object"));
+ m_definePropertyFunction = objectProto.property(QLatin1String("defineProperty"));
+ QBS_ASSERT(m_definePropertyFunction.isFunction(), /* ignore */);
+ m_emptyFunction = evaluate("(function(){})");
+ QBS_ASSERT(m_emptyFunction.isFunction(), /* ignore */);
+ // Initially push a new context to turn off scope chain insanity mode.
+ QScriptEngine::pushContext();
+ extendJavaScriptBuiltins();
+}
+
+ScriptEngine::~ScriptEngine()
+{
+}
+
+void ScriptEngine::import(const JsImports &jsImports, QScriptValue scope, QScriptValue targetObject)
+{
+ for (JsImports::const_iterator it = jsImports.begin(); it != jsImports.end(); ++it)
+ import(*it, scope, targetObject);
+}
+
+void ScriptEngine::import(const JsImport &jsImport, QScriptValue scope, QScriptValue targetObject)
+{
+ QBS_ASSERT(!scope.isValid() || scope.isObject(), return);
+ QBS_ASSERT(targetObject.isObject(), return);
+ QBS_ASSERT(targetObject.engine() == this, return);
+
+ if (debugJSImports)
+ m_logger.qbsDebug() << "[ENGINE] import into " << jsImport.scopeName;
+
+ foreach (const QString &fileName, jsImport.fileNames) {
+ QScriptValue jsImportValue;
+ jsImportValue = m_jsImportCache.value(fileName);
+ if (jsImportValue.isValid()) {
+ if (debugJSImports)
+ m_logger.qbsDebug() << "[ENGINE] " << fileName << " (cache hit)";
+ targetObject.setProperty(jsImport.scopeName, jsImportValue);
+ } else {
+ if (debugJSImports)
+ m_logger.qbsDebug() << "[ENGINE] " << fileName << " (cache miss)";
+ QFile file(fileName);
+ if (Q_UNLIKELY(!file.open(QFile::ReadOnly)))
+ throw ErrorInfo(tr("Cannot open '%1'.").arg(fileName));
+ const QString sourceCode = QTextStream(&file).readAll();
+ file.close();
+ QScriptProgram program(sourceCode, fileName);
+ importProgram(program, scope, jsImportValue);
+ targetObject.setProperty(jsImport.scopeName, jsImportValue);
+ m_jsImportCache.insert(fileName, jsImportValue);
+ }
+ }
+}
+
+void ScriptEngine::clearImportsCache()
+{
+ m_jsImportCache.clear();
+}
+
+void ScriptEngine::addPropertyRequestedFromArtifact(const Artifact *artifact,
+ const Property &property)
+{
+ m_propertiesRequestedFromArtifact[artifact->filePath()] << property;
+}
+
+void ScriptEngine::addToPropertyCache(const QString &moduleName, const QString &propertyName,
+ const PropertyMapConstPtr &propertyMap, const QVariant &value)
+{
+ m_propertyCache.insert(qMakePair(moduleName + QLatin1Char('.') + propertyName, propertyMap),
+ value);
+}
+
+QVariant ScriptEngine::retrieveFromPropertyCache(const QString &moduleName,
+ const QString &propertyName, const PropertyMapConstPtr &propertyMap)
+{
+ return m_propertyCache.value(qMakePair(moduleName + QLatin1Char('.') + propertyName,
+ propertyMap));
+}
+
+void ScriptEngine::defineProperty(QScriptValue &object, const QString &name,
+ const QScriptValue &descriptor)
+{
+ QScriptValue arguments = newArray();
+ arguments.setProperty(0, object);
+ arguments.setProperty(1, name);
+ arguments.setProperty(2, descriptor);
+ QScriptValue result = m_definePropertyFunction.call(QScriptValue(), arguments);
+ QBS_ASSERT(!hasErrorOrException(result), qDebug() << name << result.toString());
+}
+
+static QScriptValue js_observedGet(QScriptContext *context, QScriptEngine *, void *arg)
+{
+ ScriptPropertyObserver * const observer = static_cast<ScriptPropertyObserver *>(arg);
+ const QScriptValue data = context->callee().property(QLatin1String("qbsdata"));
+ const QScriptValue value = data.property(2);
+ observer->onPropertyRead(data.property(0), data.property(1).toVariant().toString(), value);
+ return value;
+}
+
+void ScriptEngine::setObservedProperty(QScriptValue &object, const QString &name,
+ const QScriptValue &value, ScriptPropertyObserver *observer)
+{
+ if (!observer) {
+ object.setProperty(name, value);
+ return;
+ }
+
+ QScriptValue data = newArray();
+ data.setProperty(0, object);
+ data.setProperty(1, name);
+ data.setProperty(2, value);
+ QScriptValue getterFunc = newFunction(js_observedGet, observer);
+ getterFunc.setProperty(QLatin1String("qbsdata"), data);
+ QScriptValue descriptor = newObject();
+ descriptor.setProperty(QLatin1String("get"), getterFunc);
+ descriptor.setProperty(QLatin1String("set"), m_emptyFunction);
+ defineProperty(object, name, descriptor);
+}
+
+QProcessEnvironment ScriptEngine::environment() const
+{
+ return m_environment;
+}
+
+void ScriptEngine::setEnvironment(const QProcessEnvironment &env)
+{
+ m_environment = env;
+}
+
+void ScriptEngine::importProgram(const QScriptProgram &program, const QScriptValue &scope,
+ QScriptValue &targetObject)
+{
+ QSet<QString> globalPropertyNames;
+ {
+ QScriptValueIterator it(globalObject());
+ while (it.hasNext()) {
+ it.next();
+ globalPropertyNames += it.name();
+ }
+ }
+
+ pushContext();
+ if (scope.isObject())
+ currentContext()->pushScope(scope);
+ QScriptValue result = evaluate(program);
+ QScriptValue activationObject = currentContext()->activationObject();
+ if (scope.isObject())
+ currentContext()->popScope();
+ popContext();
+ if (Q_UNLIKELY(hasErrorOrException(result)))
+ throw ErrorInfo(tr("Error when importing '%1': %2").arg(program.fileName(), result.toString()));
+
+ // If targetObject is already an object, it doesn't get overwritten but enhanced by the
+ // contents of the .js file.
+ // This is necessary for library imports that consist of multiple js files.
+ if (!targetObject.isObject())
+ targetObject = newObject();
+
+ // Copy every property of the activation object to the target object.
+ // We do not just save a reference to the activation object, because QScriptEngine contains
+ // special magic for activation objects that leads to unanticipated results.
+ {
+ QScriptValueIterator it(activationObject);
+ while (it.hasNext()) {
+ it.next();
+ if (debugJSImports)
+ m_logger.qbsDebug() << "[ENGINE] Copying property " << it.name();
+ targetObject.setProperty(it.name(), it.value());
+ }
+ }
+
+ // Copy new global properties to the target object and remove them from
+ // the global object. This is to support direct variable assignments
+ // without the 'var' keyword in JavaScript files.
+ QScriptValueIterator it(globalObject());
+ while (it.hasNext()) {
+ it.next();
+ if (globalPropertyNames.contains(it.name()))
+ continue;
+
+ if (debugJSImports) {
+ m_logger.qbsDebug() << "[ENGINE] inserting global property "
+ << it.name() << " " << it.value().toString();
+ }
+
+ targetObject.setProperty(it.name(), it.value());
+ it.remove();
+ }
+}
+
+void ScriptEngine::addEnvironmentVariable(const QString &name, const QString &value)
+{
+ m_usedEnvironment.insert(name, value);
+}
+
+void ScriptEngine::addFileExistsResult(const QString &filePath, bool exists)
+{
+ m_fileExistsResult.insert(filePath, exists);
+}
+
+void ScriptEngine::addFileLastModifiedResult(const QString &filePath, FileTime fileTime)
+{
+ m_fileLastModifiedResult.insert(filePath, fileTime);
+}
+
+QSet<QString> ScriptEngine::imports() const
+{
+ return QSet<QString>::fromList(m_jsImportCache.keys());
+}
+
+QScriptValueList ScriptEngine::argumentList(const QStringList &argumentNames,
+ const QScriptValue &context)
+{
+ QScriptValueList result;
+ for (int i = 0; i < argumentNames.count(); ++i)
+ result += context.property(argumentNames.at(i));
+ return result;
+}
+
+class JSTypeExtender
+{
+public:
+ JSTypeExtender(ScriptEngine *engine, const QString &typeName)
+ : m_engine(engine)
+ {
+ m_proto = engine->globalObject().property(typeName)
+ .property(QLatin1String("prototype"));
+ QBS_ASSERT(m_proto.isObject(), return);
+ m_descriptor = engine->newObject();
+ }
+
+ void addFunction(const QString &name, const QString &code)
+ {
+ QScriptValue f = m_engine->evaluate(code);
+ QBS_ASSERT(f.isFunction(), return);
+ m_descriptor.setProperty(QLatin1String("value"), f);
+ m_engine->defineProperty(m_proto, name, m_descriptor);
+ }
+
+private:
+ ScriptEngine *const m_engine;
+ QScriptValue m_proto;
+ QScriptValue m_descriptor;
+};
+
+void ScriptEngine::extendJavaScriptBuiltins()
+{
+ JSTypeExtender arrayExtender(this, QLatin1String("Array"));
+ arrayExtender.addFunction(QLatin1String("contains"),
+ QLatin1String("(function(e){return this.indexOf(e) !== -1;})"));
+
+ JSTypeExtender stringExtender(this, QLatin1String("String"));
+ stringExtender.addFunction(QLatin1String("contains"),
+ QLatin1String("(function(e){return this.indexOf(e) !== -1;})"));
+ stringExtender.addFunction(QLatin1String("startsWith"),
+ QLatin1String("(function(e){return this.slice(0, e.length) === e;})"));
+ stringExtender.addFunction(QLatin1String("endsWith"),
+ QLatin1String("(function(e){return this.slice(-e.length) === e;})"));
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/scriptengine.h b/src/lib/corelib/language/scriptengine.h
new file mode 100644
index 000000000..a2de9d9ea
--- /dev/null
+++ b/src/lib/corelib/language/scriptengine.h
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_SCRIPTENGINE_H
+#define QBS_SCRIPTENGINE_H
+
+#include "jsimports.h"
+#include "forward_decls.h"
+#include <language/property.h>
+#include <logging/logger.h>
+#include <tools/filetime.h>
+#include <tools/qbs_export.h>
+
+#include <QHash>
+#include <QPair>
+#include <QProcessEnvironment>
+#include <QScriptEngine>
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+class Artifact;
+
+class ScriptPropertyObserver;
+
+// FIXME: Exported for qbs-qmltypes
+class QBS_EXPORT ScriptEngine : public QScriptEngine
+{
+ Q_OBJECT
+public:
+ ScriptEngine(const Logger &logger, QObject *parent = 0);
+ ~ScriptEngine();
+
+ void setLogger(const Logger &logger) { m_logger = logger; }
+ void import(const JsImports &jsImports, QScriptValue scope, QScriptValue targetObject);
+ void import(const JsImport &jsImport, QScriptValue scope, QScriptValue targetObject);
+ void clearImportsCache();
+
+ void addPropertyRequestedInScript(const Property &property) {
+ m_propertiesRequestedInScript += property;
+ }
+ void addPropertyRequestedFromArtifact(const Artifact *artifact, const Property &property);
+ void clearRequestedProperties() {
+ m_propertiesRequestedInScript.clear();
+ m_propertiesRequestedFromArtifact.clear();
+ }
+ PropertyList propertiesRequestedInScript() const { return m_propertiesRequestedInScript; }
+ QHash<QString, PropertyList> propertiesRequestedFromArtifact() const {
+ return m_propertiesRequestedFromArtifact;
+ }
+
+ void addToPropertyCache(const QString &moduleName, const QString &propertyName,
+ const PropertyMapConstPtr &propertyMap, const QVariant &value);
+ QVariant retrieveFromPropertyCache(const QString &moduleName, const QString &propertyName,
+ const PropertyMapConstPtr &propertyMap);
+
+ void defineProperty(QScriptValue &object, const QString &name, const QScriptValue &descriptor);
+ void setObservedProperty(QScriptValue &object, const QString &name, const QScriptValue &value,
+ ScriptPropertyObserver *observer);
+
+ QProcessEnvironment environment() const;
+ void setEnvironment(const QProcessEnvironment &env);
+ void addEnvironmentVariable(const QString &name, const QString &value);
+ QHash<QString, QString> usedEnvironment() const { return m_usedEnvironment; }
+ void addFileExistsResult(const QString &filePath, bool exists);
+ void addFileLastModifiedResult(const QString &filePath, FileTime fileTime);
+ QHash<QString, bool> fileExistsResults() const { return m_fileExistsResult; }
+ QHash<QString, FileTime> fileLastModifiedResults() const { return m_fileLastModifiedResult; }
+ QSet<QString> imports() const;
+ static QScriptValueList argumentList(const QStringList &argumentNames,
+ const QScriptValue &context);
+
+ class ScriptValueCache
+ {
+ public:
+ ScriptValueCache() : observer(0), project(0), product(0) {}
+ const void *observer;
+ const void *project;
+ const void *product;
+ QScriptValue observerScriptValue;
+ QScriptValue projectScriptValue;
+ QScriptValue productScriptValue;
+ };
+
+ ScriptValueCache *scriptValueCache() { return &m_scriptValueCache; }
+
+ bool hasErrorOrException(const QScriptValue &v) const {
+ return v.isError() || hasUncaughtException();
+ }
+
+private:
+ void extendJavaScriptBuiltins();
+ void importProgram(const QScriptProgram &program, const QScriptValue &scope,
+ QScriptValue &targetObject);
+
+ ScriptValueCache m_scriptValueCache;
+ QHash<QString, QScriptValue> m_jsImportCache;
+ QHash<QPair<QString, PropertyMapConstPtr>, QVariant> m_propertyCache;
+ PropertyList m_propertiesRequestedInScript;
+ QHash<QString, PropertyList> m_propertiesRequestedFromArtifact;
+ Logger m_logger;
+ QScriptValue m_definePropertyFunction;
+ QScriptValue m_emptyFunction;
+ QProcessEnvironment m_environment;
+ QHash<QString, QString> m_usedEnvironment;
+ QHash<QString, bool> m_fileExistsResult;
+ QHash<QString, FileTime> m_fileLastModifiedResult;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_SCRIPTENGINE_H
diff --git a/src/lib/corelib/language/scriptpropertyobserver.h b/src/lib/corelib/language/scriptpropertyobserver.h
new file mode 100644
index 000000000..e40ccc1d2
--- /dev/null
+++ b/src/lib/corelib/language/scriptpropertyobserver.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_SCRIPTPROPERTYOBSERVER_H
+#define QBS_SCRIPTPROPERTYOBSERVER_H
+
+#include <QScriptValue>
+
+namespace qbs {
+namespace Internal {
+
+class ScriptPropertyObserver
+{
+public:
+ virtual void onPropertyRead(const QScriptValue &object, const QString &name,
+ const QScriptValue &value) = 0;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_SCRIPTPROPERTYOBSERVER_H
diff --git a/src/lib/corelib/language/testdata/Banana b/src/lib/corelib/language/testdata/Banana
new file mode 100644
index 000000000..53164be8a
--- /dev/null
+++ b/src/lib/corelib/language/testdata/Banana
@@ -0,0 +1 @@
+Peanut butter jelly time!
diff --git a/src/lib/corelib/language/testdata/aboutdialog.cpp b/src/lib/corelib/language/testdata/aboutdialog.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/lib/corelib/language/testdata/aboutdialog.cpp
diff --git a/src/lib/corelib/language/testdata/baseproperty.qbs b/src/lib/corelib/language/testdata/baseproperty.qbs
new file mode 100644
index 000000000..74024aedc
--- /dev/null
+++ b/src/lib/corelib/language/testdata/baseproperty.qbs
@@ -0,0 +1,7 @@
+import "baseproperty_base.qbs" as BaseProduct
+
+BaseProduct {
+ name: "product1"
+ narf: base.concat(["boo"])
+ zort: base.concat(["boo"])
+}
diff --git a/src/lib/corelib/language/testdata/baseproperty_base.qbs b/src/lib/corelib/language/testdata/baseproperty_base.qbs
new file mode 100644
index 000000000..85b64b76e
--- /dev/null
+++ b/src/lib/corelib/language/testdata/baseproperty_base.qbs
@@ -0,0 +1,4 @@
+Product {
+ property var narf
+ property var zort: ["bar"]
+}
diff --git a/src/lib/corelib/language/testdata/buildconfigstringlistsyntax.qbs b/src/lib/corelib/language/testdata/buildconfigstringlistsyntax.qbs
new file mode 100644
index 000000000..623919317
--- /dev/null
+++ b/src/lib/corelib/language/testdata/buildconfigstringlistsyntax.qbs
@@ -0,0 +1,3 @@
+Project {
+ property stringList someStrings
+}
diff --git a/src/lib/corelib/language/testdata/builtinFunctionInSearchPathsProperty.qbs b/src/lib/corelib/language/testdata/builtinFunctionInSearchPathsProperty.qbs
new file mode 100644
index 000000000..f8f1b4d17
--- /dev/null
+++ b/src/lib/corelib/language/testdata/builtinFunctionInSearchPathsProperty.qbs
@@ -0,0 +1,8 @@
+import qbs
+
+Project {
+ qbsSearchPaths: {
+ if (!qbs.getenv("PATH"))
+ throw "getenv doesn't seem to work";
+ }
+}
diff --git a/src/lib/corelib/language/testdata/canonicalArchitecture.qbs b/src/lib/corelib/language/testdata/canonicalArchitecture.qbs
new file mode 100644
index 000000000..94da7b276
--- /dev/null
+++ b/src/lib/corelib/language/testdata/canonicalArchitecture.qbs
@@ -0,0 +1,3 @@
+Product {
+ name: qbs.canonicalArchitecture("i386")
+}
diff --git a/src/lib/corelib/language/testdata/conditionaldepends.qbs b/src/lib/corelib/language/testdata/conditionaldepends.qbs
new file mode 100644
index 000000000..8ad3660ec
--- /dev/null
+++ b/src/lib/corelib/language/testdata/conditionaldepends.qbs
@@ -0,0 +1,67 @@
+import qbs 1.0
+import "conditionaldepends_base.qbs" as CondBase
+
+Project {
+ CondBase {
+ name: 'conditionaldepends_derived'
+ someProp: true
+ }
+
+ CondBase {
+ name: 'conditionaldepends_derived_false'
+ someProp: "knolf" === "narf"
+ }
+
+ Product {
+ name: "product_props_true"
+ property bool someTrueProp: true
+ Depends { condition: someTrueProp; name: "dummy"}
+ }
+
+ Product {
+ name: "product_props_false"
+ property bool someFalseProp: false
+ Depends { condition: someFalseProp; name: "dummy"}
+ }
+
+ property bool someTruePrjProp: true
+ Product {
+ name: "project_props_true"
+ Depends { condition: project.someTruePrjProp; name: "dummy"}
+ }
+
+ property bool someFalsePrjProp: false
+ Product {
+ name: "project_props_false"
+ Depends { condition: project.someFalsePrjProp; name: "dummy"}
+ }
+
+ Product {
+ name: "module_props_true"
+ Depends { name: "dummy2" }
+ Depends { condition: dummy2.someTrueProp; name: "dummy" }
+ }
+
+ Product {
+ name: "module_props_false"
+ Depends { name: "dummy2" }
+ Depends { condition: dummy2.someFalseProp; name: "dummy" }
+ }
+
+ Product {
+ name: "contradictory_conditions1"
+ Depends { condition: false; name: "dummy" }
+ Depends { condition: true; name: "dummy" } // this one wins
+ }
+
+ Product {
+ name: "contradictory_conditions2"
+ Depends { condition: true; name: "dummy" } // this one wins
+ Depends { condition: false; name: "dummy" }
+ }
+
+ Product {
+ name: "unknown_dependency_condition_false"
+ Depends { condition: false; name: "doesonlyexistifhellfreezesover" }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/conditionaldepends_base.qbs b/src/lib/corelib/language/testdata/conditionaldepends_base.qbs
new file mode 100644
index 000000000..81782ba44
--- /dev/null
+++ b/src/lib/corelib/language/testdata/conditionaldepends_base.qbs
@@ -0,0 +1,10 @@
+import qbs 1.0
+
+Application {
+ name: 'conditionaldepends_base'
+ property bool someProp: false
+ Depends {
+ condition: someProp
+ name: 'dummy'
+ }
+}
diff --git a/src/lib/corelib/language/testdata/drawline.asm b/src/lib/corelib/language/testdata/drawline.asm
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/lib/corelib/language/testdata/drawline.asm
diff --git a/src/lib/corelib/language/testdata/environmentvariable.qbs b/src/lib/corelib/language/testdata/environmentvariable.qbs
new file mode 100644
index 000000000..b930e8511
--- /dev/null
+++ b/src/lib/corelib/language/testdata/environmentvariable.qbs
@@ -0,0 +1,3 @@
+Product {
+ name: qbs.getEnv("PRODUCT_NAME")
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/importloop1.qbs b/src/lib/corelib/language/testdata/erroneous/importloop1.qbs
new file mode 100644
index 000000000..91e8f620f
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/importloop1.qbs
@@ -0,0 +1,5 @@
+import qbs 1.0
+import "importloop2.qbs" as X
+
+X {}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/importloop2.qbs b/src/lib/corelib/language/testdata/erroneous/importloop2.qbs
new file mode 100644
index 000000000..c41fe7e9f
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/importloop2.qbs
@@ -0,0 +1,5 @@
+import qbs 1.0
+import "importloop1.qbs" as X
+
+X {}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/invalid_child_item_type.qbs b/src/lib/corelib/language/testdata/erroneous/invalid_child_item_type.qbs
new file mode 100644
index 000000000..2341d4b9c
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/invalid_child_item_type.qbs
@@ -0,0 +1,6 @@
+import qbs 1.0
+
+Project {
+ Depends { name: "foo" }
+}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/invalid_file.qbs b/src/lib/corelib/language/testdata/erroneous/invalid_file.qbs
new file mode 100644
index 000000000..18f2b044d
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/invalid_file.qbs
@@ -0,0 +1,5 @@
+import qbs
+
+Application {
+ files: ["main.cpp", "other.h"]
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/invalid_property_type.qbs b/src/lib/corelib/language/testdata/erroneous/invalid_property_type.qbs
new file mode 100644
index 000000000..b9b392736
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/invalid_property_type.qbs
@@ -0,0 +1,5 @@
+import qbs
+
+Product {
+ property nonsense esnesnon
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/invalid_stringlist_element.qbs b/src/lib/corelib/language/testdata/erroneous/invalid_stringlist_element.qbs
new file mode 100644
index 000000000..fc30a2af6
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/invalid_stringlist_element.qbs
@@ -0,0 +1,3 @@
+Product {
+ files: ["foo", ["zoo"], "bar"]
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/main.cpp b/src/lib/corelib/language/testdata/erroneous/main.cpp
new file mode 100644
index 000000000..8b8d58de0
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/main.cpp
@@ -0,0 +1 @@
+int main() { }
diff --git a/src/lib/corelib/language/testdata/erroneous/multiple_exports.qbs b/src/lib/corelib/language/testdata/erroneous/multiple_exports.qbs
new file mode 100644
index 000000000..17c7f6a14
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/multiple_exports.qbs
@@ -0,0 +1,4 @@
+Product {
+ Export {}
+ Export {}
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/multiple_properties_in_subproject.qbs b/src/lib/corelib/language/testdata/erroneous/multiple_properties_in_subproject.qbs
new file mode 100644
index 000000000..0ecb41b34
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/multiple_properties_in_subproject.qbs
@@ -0,0 +1,8 @@
+import qbs
+
+Project {
+ SubProject {
+ Properties { condition: false }
+ Properties { name: "blubb" }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/nonexistentouter.qbs b/src/lib/corelib/language/testdata/erroneous/nonexistentouter.qbs
new file mode 100644
index 000000000..6c5899b5d
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/nonexistentouter.qbs
@@ -0,0 +1,7 @@
+import qbs 1.0
+
+Project {
+ Product {
+ name: outer
+ }
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/references_cycle.qbs b/src/lib/corelib/language/testdata/erroneous/references_cycle.qbs
new file mode 100644
index 000000000..6d0960f09
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/references_cycle.qbs
@@ -0,0 +1,6 @@
+import qbs 1.0
+
+Project {
+ references: ["references_cycle2.qbs"]
+}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/references_cycle2.qbs b/src/lib/corelib/language/testdata/erroneous/references_cycle2.qbs
new file mode 100644
index 000000000..0b0d2734d
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/references_cycle2.qbs
@@ -0,0 +1,6 @@
+import qbs 1.0
+
+Project {
+ references: ["references_cycle3.qbs"]
+}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/references_cycle3.qbs b/src/lib/corelib/language/testdata/erroneous/references_cycle3.qbs
new file mode 100644
index 000000000..2a237d154
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/references_cycle3.qbs
@@ -0,0 +1,6 @@
+import qbs 1.0
+
+Project {
+ references: ["references_cycle.qbs"]
+}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/reserved_name_in_import.qbs b/src/lib/corelib/language/testdata/erroneous/reserved_name_in_import.qbs
new file mode 100644
index 000000000..3940109d0
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/reserved_name_in_import.qbs
@@ -0,0 +1,4 @@
+import qbs
+import "../idusagebase.qbs" as TextFile
+
+Product { }
diff --git a/src/lib/corelib/language/testdata/erroneous/submodule_syntax.qbs b/src/lib/corelib/language/testdata/erroneous/submodule_syntax.qbs
new file mode 100644
index 000000000..4254bb8f6
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/submodule_syntax.qbs
@@ -0,0 +1,3 @@
+Product {
+ Depends { name: "abc.def"; submodules: ["ghi"] }
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/subproject_cycle.qbs b/src/lib/corelib/language/testdata/erroneous/subproject_cycle.qbs
new file mode 100644
index 000000000..0a9cd289f
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/subproject_cycle.qbs
@@ -0,0 +1,8 @@
+import qbs 1.0
+
+Project {
+ SubProject {
+ filePath: "subproject_cycle2.qbs"
+ }
+}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/subproject_cycle2.qbs b/src/lib/corelib/language/testdata/erroneous/subproject_cycle2.qbs
new file mode 100644
index 000000000..ab92d76dd
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/subproject_cycle2.qbs
@@ -0,0 +1,8 @@
+import qbs 1.0
+
+Project {
+ SubProject {
+ filePath: "subproject_cycle3.qbs"
+ }
+}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/subproject_cycle3.qbs b/src/lib/corelib/language/testdata/erroneous/subproject_cycle3.qbs
new file mode 100644
index 000000000..af1e50f5a
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/subproject_cycle3.qbs
@@ -0,0 +1,8 @@
+import qbs 1.0
+
+Project {
+ SubProject {
+ filePath: "subproject_cycle.qbs"
+ }
+}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/throw_in_property_binding.qbs b/src/lib/corelib/language/testdata/erroneous/throw_in_property_binding.qbs
new file mode 100644
index 000000000..fc251b1a4
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/throw_in_property_binding.qbs
@@ -0,0 +1,7 @@
+import qbs 1.0
+
+Product {
+ name: {
+ throw "something is wrong";
+ }
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/undeclared_item.qbs b/src/lib/corelib/language/testdata/erroneous/undeclared_item.qbs
new file mode 100644
index 000000000..b2edbf013
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/undeclared_item.qbs
@@ -0,0 +1,6 @@
+import qbs 1.0
+
+Product {
+ cpp.defines: ["SUPERCRAZY"]
+}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/undeclared_property.qbs b/src/lib/corelib/language/testdata/erroneous/undeclared_property.qbs
new file mode 100644
index 000000000..1dad5f747
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/undeclared_property.qbs
@@ -0,0 +1,6 @@
+import qbs 1.0
+
+Product {
+ doesntexist: 123
+}
+
diff --git a/src/lib/corelib/language/testdata/erroneous/unknown_item_type.qbs b/src/lib/corelib/language/testdata/erroneous/unknown_item_type.qbs
new file mode 100644
index 000000000..9e34e9243
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/unknown_item_type.qbs
@@ -0,0 +1,3 @@
+Narf {
+ zort: 1 // This invalid binding should not hide the "Unexpected item type" error.
+}
diff --git a/src/lib/corelib/language/testdata/erroneous/unknown_module.qbs b/src/lib/corelib/language/testdata/erroneous/unknown_module.qbs
new file mode 100644
index 000000000..dcfc79a9c
--- /dev/null
+++ b/src/lib/corelib/language/testdata/erroneous/unknown_module.qbs
@@ -0,0 +1,3 @@
+Product {
+ Depends { name: "neitherModuleNorProduct" }
+}
diff --git a/src/lib/corelib/language/testdata/exports.qbs b/src/lib/corelib/language/testdata/exports.qbs
new file mode 100644
index 000000000..b1adfc214
--- /dev/null
+++ b/src/lib/corelib/language/testdata/exports.qbs
@@ -0,0 +1,49 @@
+import qbs 1.0
+import "exports_product.qbs" as ProductWithInheritedExportItem
+
+Project {
+ Application {
+ name: "myapp"
+ Depends { name: "mylib" }
+ }
+ StaticLibrary {
+ name: "mylib"
+ Depends { name: "dummy" }
+ dummy.defines: ["BUILD_MYLIB"]
+ Export {
+ Depends { name: "dummy" }
+ dummy.defines: ["USE_MYLIB"]
+ }
+ }
+
+ Application {
+ name: "A"
+ Depends { name: "B" }
+ }
+ StaticLibrary {
+ name: "B"
+ Export {
+ Depends { name: "C" }
+ }
+ }
+ StaticLibrary {
+ name: "C"
+ Export {
+ Depends { name: "D" }
+ }
+ }
+ StaticLibrary {
+ name: "D"
+ }
+
+ Application {
+ name: "myapp2"
+ Depends { name: "productWithInheritedExportItem" }
+ }
+ ProductWithInheritedExportItem {
+ name: "productWithInheritedExportItem"
+ Export {
+ dummy.cxxFlags: ["-bar"]
+ }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/exports_product.qbs b/src/lib/corelib/language/testdata/exports_product.qbs
new file mode 100644
index 000000000..78136fc32
--- /dev/null
+++ b/src/lib/corelib/language/testdata/exports_product.qbs
@@ -0,0 +1,7 @@
+Product {
+ Export {
+ Depends { name: "dummy" }
+ dummy.cxxFlags: ["-foo"]
+ dummy.defines: ["ABC"]
+ }
+}
diff --git a/src/lib/corelib/language/testdata/filecontextproperties.qbs b/src/lib/corelib/language/testdata/filecontextproperties.qbs
new file mode 100644
index 000000000..5c435b3ba
--- /dev/null
+++ b/src/lib/corelib/language/testdata/filecontextproperties.qbs
@@ -0,0 +1,5 @@
+Product {
+ name: "product1"
+ property string narf: filePath
+ property string zort: path
+}
diff --git a/src/lib/corelib/language/testdata/filetags.qbs b/src/lib/corelib/language/testdata/filetags.qbs
new file mode 100644
index 000000000..38182e5f1
--- /dev/null
+++ b/src/lib/corelib/language/testdata/filetags.qbs
@@ -0,0 +1,73 @@
+import qbs 1.0
+
+Project {
+ FileTagger {
+ patterns: "*.cpp"
+ fileTags: ["cpp"]
+ }
+
+ Product {
+ name: "filetagger_project_scope"
+ files: ["main.cpp"]
+ }
+
+ Product {
+ name: "filetagger_product_scope"
+ files: ["drawline.asm"]
+ FileTagger {
+ patterns: "*.asm"
+ fileTags: ["asm"]
+ }
+ }
+
+ Product {
+ name: "filetagger_static_pattern"
+ files: "Banana"
+ FileTagger {
+ patterns: "Banana"
+ fileTags: ["yellow"]
+ }
+ }
+
+ Product {
+ name: "unknown_file_tag"
+ files: "narf.zort"
+ }
+
+ Product {
+ name: "set_file_tag_via_group"
+ Group {
+ files: ["main.cpp"]
+ fileTags: ["c++"]
+ }
+ }
+
+ Product {
+ name: "override_file_tag_via_group"
+ files: "main.cpp" // gets file tag "cpp" through the FileTagger
+ Group {
+ files: product.files
+ fileTags: ["c++"]
+ }
+ }
+
+ Product {
+ name: "add_file_tag_via_group"
+ files: "main.cpp"
+ Group {
+ overrideTags: false
+ files: "main.cpp"
+ fileTags: ["zzz"]
+ }
+ }
+
+ Product {
+ name: "add_file_tag_via_group_and_file_ref"
+ files: "main.cpp"
+ Group {
+ overrideTags: false
+ files: product.files
+ fileTags: ["zzz"]
+ }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/getNativeSetting.qbs b/src/lib/corelib/language/testdata/getNativeSetting.qbs
new file mode 100644
index 000000000..c414c79f9
--- /dev/null
+++ b/src/lib/corelib/language/testdata/getNativeSetting.qbs
@@ -0,0 +1,23 @@
+import qbs.FileInfo
+
+Project {
+ Product {
+ name: {
+ if (qbs.hostOS.contains("osx")) {
+ return qbs.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductName");
+ } else if (qbs.hostOS.contains("windows")) {
+ var productName = qbs.getNativeSetting("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName");
+ if (productName.contains("Windows")) {
+ return "Windows";
+ }
+ return undefined;
+ } else {
+ return qbs.getNativeSetting(FileInfo.joinPaths(path, "nativesettings.ini"), "osname");
+ }
+ }
+ }
+
+ Product {
+ name: qbs.getNativeSetting("/dev/null", undefined, "fallback");
+ }
+}
diff --git a/src/lib/corelib/language/testdata/groupconditions.qbs b/src/lib/corelib/language/testdata/groupconditions.qbs
new file mode 100644
index 000000000..244c80c2c
--- /dev/null
+++ b/src/lib/corelib/language/testdata/groupconditions.qbs
@@ -0,0 +1,53 @@
+import qbs 1.0
+
+Project {
+ property bool someTrueProperty: true
+ Product {
+ name: "no_condition_no_group"
+ files: ["main.cpp"]
+ }
+ Product {
+ name: "no_condition"
+ Group {
+ files: ["main.cpp"]
+ }
+ }
+ Product {
+ name: "true_condition"
+ Group {
+ condition: true
+ files: ["main.cpp"]
+ }
+ }
+ Product {
+ name: "false_condition"
+ Group {
+ condition: false
+ files: ["main.cpp"]
+ }
+ }
+ Product {
+ name: "true_condition_from_product"
+ property bool anotherTrueProperty: true
+ Group {
+ condition: anotherTrueProperty
+ files: ["main.cpp"]
+ }
+ }
+ Product {
+ name: "true_condition_from_project"
+ Group {
+ condition: project.someTrueProperty
+ files: ["main.cpp"]
+ }
+ }
+
+ Product {
+ name: "condition_accessing_module_property"
+ Group {
+ condition: qbs.targetOS.contains("narf")
+ files: ["main.cpp"]
+ qbs.install: false
+ }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/groupname.qbs b/src/lib/corelib/language/testdata/groupname.qbs
new file mode 100644
index 000000000..22e58765c
--- /dev/null
+++ b/src/lib/corelib/language/testdata/groupname.qbs
@@ -0,0 +1,20 @@
+Project {
+ Product {
+ name: "MyProduct"
+ Group {
+ name: product.name + ".MyGroup"
+ files: "*"
+ }
+ }
+
+ Product {
+ name: "My2ndProduct"
+ Group {
+ name: product.name + ".MyGroup"
+ files: ["narf"]
+ }
+ Group {
+ files: ["zort"]
+ }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/homeDirectory.qbs b/src/lib/corelib/language/testdata/homeDirectory.qbs
new file mode 100644
index 000000000..1ceeb5bbd
--- /dev/null
+++ b/src/lib/corelib/language/testdata/homeDirectory.qbs
@@ -0,0 +1,18 @@
+import qbs 1.0
+
+Project {
+ Product {
+ name: "home"
+
+ // These should resolve
+ property path home: "~"
+ property path homeSlash: "~/"
+ property path homeUp: "~/.."
+ property path homeFile: "~/a"
+
+ // These are sanity checks and should not
+ property path bogus1: "a~b"
+ property path bogus2: "a/~/bb"
+ property path user: "~foo/bar" // we don't resolve other-user paths
+ }
+}
diff --git a/src/lib/corelib/language/testdata/idusage.qbs b/src/lib/corelib/language/testdata/idusage.qbs
new file mode 100644
index 000000000..42dc43ad5
--- /dev/null
+++ b/src/lib/corelib/language/testdata/idusage.qbs
@@ -0,0 +1,20 @@
+import qbs 1.0
+import "idusagebase.qbs" as DerivedProduct
+
+Project {
+ id: theProject
+ property int initialNr: 0
+ DerivedProduct {
+ id: product1
+ }
+ Product {
+ id: product2
+ property int nr: theProject.initialNr + product1.nr + 1
+ name: "product2_" + nr
+ }
+ Product {
+ id: product3
+ property int nr: product2.nr + 1
+ name: "product3_" + nr
+ }
+}
diff --git a/src/lib/corelib/language/testdata/idusagebase.qbs b/src/lib/corelib/language/testdata/idusagebase.qbs
new file mode 100644
index 000000000..483a00ccf
--- /dev/null
+++ b/src/lib/corelib/language/testdata/idusagebase.qbs
@@ -0,0 +1,5 @@
+Product {
+ id: baseProduct
+ property int nr: theProject.initialNr + 1
+ name: "product1_" + nr
+}
diff --git a/src/lib/corelib/language/testdata/invalidBindingInDisabledItem.qbs b/src/lib/corelib/language/testdata/invalidBindingInDisabledItem.qbs
new file mode 100644
index 000000000..e3e03a319
--- /dev/null
+++ b/src/lib/corelib/language/testdata/invalidBindingInDisabledItem.qbs
@@ -0,0 +1,16 @@
+import qbs 1.0
+
+Project {
+ Product {
+ name: "product1"
+ condition: false
+ someNonsense: "Bitte stellen Sie die Tassen auf den Tisch."
+ }
+ Product {
+ name: "product2"
+ Group {
+ condition: false
+ moreNonsense: "Follen. Follen. Hünuntergefollen. Auf dön Töppüch."
+ }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/jsextensions.js b/src/lib/corelib/language/testdata/jsextensions.js
new file mode 100644
index 000000000..d26936911
--- /dev/null
+++ b/src/lib/corelib/language/testdata/jsextensions.js
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+(function() { // Function wrapper to keep the environment clean.
+
+/*
+ * poor man's JS test suite
+ */
+var testctx = {};
+
+function initTestContext(name)
+{
+ testctx.nr = 1;
+ testctx.name = name;
+}
+
+function verify(c)
+{
+ if (!c)
+ throw testctx.name + ": verification #" + testctx.nr + " failed.";
+ testctx.nr++;
+}
+
+
+/*
+ * Tests for extensions of JavaScript builtin types.
+ */
+
+var a = ["one", "two", "three"];
+initTestContext("Array.prototype.contains");
+for (var k in a)
+ verify(k !== "contains");
+verify(a.contains("one"));
+verify(a.contains("two"));
+verify(a.contains("three"));
+verify(!a.contains("four"));
+
+})() // END function wrapper
diff --git a/src/lib/corelib/language/testdata/jsimportsinmultiplescopes.js b/src/lib/corelib/language/testdata/jsimportsinmultiplescopes.js
new file mode 100644
index 000000000..4e939505c
--- /dev/null
+++ b/src/lib/corelib/language/testdata/jsimportsinmultiplescopes.js
@@ -0,0 +1,12 @@
+function getName(qbsModule)
+{
+ if (qbsModule.debugInformation)
+ return "MyProduct_debug";
+ else
+ return "MyProduct";
+}
+
+function getInstallDir()
+{
+ return "somewhere";
+}
diff --git a/src/lib/corelib/language/testdata/jsimportsinmultiplescopes.qbs b/src/lib/corelib/language/testdata/jsimportsinmultiplescopes.qbs
new file mode 100644
index 000000000..388cf974b
--- /dev/null
+++ b/src/lib/corelib/language/testdata/jsimportsinmultiplescopes.qbs
@@ -0,0 +1,7 @@
+import "jsimportsinmultiplescopes.js" as MyFunctions
+
+Product {
+ name: MyFunctions.getName(qbs)
+ qbs.installDir: MyFunctions.getInstallDir()
+ files: "main.cpp"
+}
diff --git a/src/lib/corelib/language/testdata/main.cpp b/src/lib/corelib/language/testdata/main.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/lib/corelib/language/testdata/main.cpp
diff --git a/src/lib/corelib/language/testdata/moduleproperties.qbs b/src/lib/corelib/language/testdata/moduleproperties.qbs
new file mode 100644
index 000000000..c466a15cf
--- /dev/null
+++ b/src/lib/corelib/language/testdata/moduleproperties.qbs
@@ -0,0 +1,21 @@
+import qbs 1.0
+
+Project {
+ Product {
+ name: "merge_lists"
+ Depends { name: "dummyqt"; submodules: ["gui", "network"] }
+ Depends { name: "dummy" }
+ dummy.defines: ["THE_PRODUCT"]
+ }
+ Product {
+ name: "merge_lists_and_values"
+ Depends { name: "dummyqt"; submodules: ["network", "gui"] }
+ Depends { name: "dummy" }
+ dummy.defines: "THE_PRODUCT"
+ }
+ Product {
+ name: "merge_lists_with_duplicates"
+ Depends { name: "dummy" }
+ dummy.cxxFlags: ["-foo", "BAR", "-foo", "BAZ"]
+ }
+}
diff --git a/src/lib/corelib/language/testdata/modules.qbs b/src/lib/corelib/language/testdata/modules.qbs
new file mode 100644
index 000000000..7841def35
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modules.qbs
@@ -0,0 +1,34 @@
+Project {
+ Product {
+ name: "no_modules"
+ property var foo
+ }
+ Product {
+ name: "qt_core"
+ dummyqt.core.version: "1.2.3"
+ property var foo: dummyqt.core.coreVersion
+ Depends {
+ name: "dummyqt.core"
+ }
+ }
+ Product {
+ name: "qt_gui"
+ property var foo: dummyqt.gui.guiProperty
+ Depends {
+ name: "dummyqt.gui"
+ }
+ }
+ Product {
+ name: "qt_gui_network"
+ property var foo: dummyqt.gui.guiProperty + ',' + dummyqt.network.networkProperty
+ Depends {
+ name: "dummyqt"
+ submodules: ["gui", "network"]
+ }
+ }
+ Product {
+ name: "dummy_twice"
+ Depends { name: "dummy" }
+ Depends { name: "dummy" }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/modules/dummy/dummy.qbs b/src/lib/corelib/language/testdata/modules/dummy/dummy.qbs
new file mode 100644
index 000000000..9484a70b8
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modules/dummy/dummy.qbs
@@ -0,0 +1,8 @@
+import "dummy_base.qbs" as DummyBase
+
+DummyBase {
+ condition: true
+ property stringList defines
+ property stringList cFlags
+ property stringList cxxFlags
+}
diff --git a/src/lib/corelib/language/testdata/modules/dummy/dummy_base.qbs b/src/lib/corelib/language/testdata/modules/dummy/dummy_base.qbs
new file mode 100644
index 000000000..0ecd8a1d8
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modules/dummy/dummy_base.qbs
@@ -0,0 +1,4 @@
+Module {
+ condition: false
+ property pathList includePaths
+}
diff --git a/src/lib/corelib/language/testdata/modules/dummy2/dummy2.qbs b/src/lib/corelib/language/testdata/modules/dummy2/dummy2.qbs
new file mode 100644
index 000000000..f60c38a71
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modules/dummy2/dummy2.qbs
@@ -0,0 +1,7 @@
+import qbs 1.0
+
+Module {
+ property var defines
+ property var someTrueProp: true
+ property var someFalseProp: false
+}
diff --git a/src/lib/corelib/language/testdata/modules/dummyqt/core/dummycore.qbs b/src/lib/corelib/language/testdata/modules/dummyqt/core/dummycore.qbs
new file mode 100644
index 000000000..13f5e6fc1
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modules/dummyqt/core/dummycore.qbs
@@ -0,0 +1,14 @@
+import qbs 1.0
+
+Module {
+ id: qtcore
+ property int versionMajor: 5
+ property int versionMinor: 0
+ property int versionPatch: 0
+ property string version: versionMajor.toString() + "." + versionMinor.toString() + "." + versionPatch.toString()
+ property string coreProperty: "coreProperty"
+ property string coreVersion: qtcore.version
+
+ Depends { name: "dummy" }
+ dummy.defines: ["QT_CORE"]
+}
diff --git a/src/lib/corelib/language/testdata/modules/dummyqt/gui/dummygui.qbs b/src/lib/corelib/language/testdata/modules/dummyqt/gui/dummygui.qbs
new file mode 100644
index 000000000..a42003c34
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modules/dummyqt/gui/dummygui.qbs
@@ -0,0 +1,9 @@
+import qbs 1.0
+
+Module {
+ Depends { name: "dummyqt.core" }
+ property string guiProperty: "guiProperty"
+
+ Depends { name: "dummy" }
+ dummy.defines: ["QT_GUI"]
+}
diff --git a/src/lib/corelib/language/testdata/modules/dummyqt/network/dummynetwork.qbs b/src/lib/corelib/language/testdata/modules/dummyqt/network/dummynetwork.qbs
new file mode 100644
index 000000000..2da3af050
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modules/dummyqt/network/dummynetwork.qbs
@@ -0,0 +1,9 @@
+import qbs 1.0
+
+Module {
+ Depends { name: "dummyqt"; submodules: ["core"] }
+ property string networkProperty: "networkProperty"
+
+ Depends { name: "dummy" }
+ dummy.defines: ["QT_NETWORK"]
+}
diff --git a/src/lib/corelib/language/testdata/modules/scopemod/scopemod.qbs b/src/lib/corelib/language/testdata/modules/scopemod/scopemod.qbs
new file mode 100644
index 000000000..ba7dbcbf0
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modules/scopemod/scopemod.qbs
@@ -0,0 +1,12 @@
+import qbs 1.0
+
+Module {
+ property int a: 1
+ property int b: 1
+ property int c: a + 1
+ property int d: b + 1
+ property int e: 1
+ property int f: 1
+ property int g: 1
+ property int h: 1
+}
diff --git a/src/lib/corelib/language/testdata/modulescope.qbs b/src/lib/corelib/language/testdata/modulescope.qbs
new file mode 100644
index 000000000..c127f0c61
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modulescope.qbs
@@ -0,0 +1,14 @@
+import qbs 1.0
+import "modulescope_base.qbs" as MyProduct
+
+Project {
+ MyProduct {
+ name: "product1"
+ property int e: 12
+ property int f: 13
+ scopemod.a: 2
+ scopemod.f: 2
+ scopemod.g: e * f
+ scopemod.h: base + 2
+ }
+}
diff --git a/src/lib/corelib/language/testdata/modulescope_base.qbs b/src/lib/corelib/language/testdata/modulescope_base.qbs
new file mode 100644
index 000000000..16a9875fa
--- /dev/null
+++ b/src/lib/corelib/language/testdata/modulescope_base.qbs
@@ -0,0 +1,6 @@
+import qbs 1.0
+
+Product {
+ Depends { name: "scopemod" }
+ scopemod.h: e * f
+}
diff --git a/src/lib/corelib/language/testdata/narf b/src/lib/corelib/language/testdata/narf
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/lib/corelib/language/testdata/narf
diff --git a/src/lib/corelib/language/testdata/narf.zort b/src/lib/corelib/language/testdata/narf.zort
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/lib/corelib/language/testdata/narf.zort
diff --git a/src/lib/corelib/language/testdata/nativesettings.ini b/src/lib/corelib/language/testdata/nativesettings.ini
new file mode 100644
index 000000000..4caf32e56
--- /dev/null
+++ b/src/lib/corelib/language/testdata/nativesettings.ini
@@ -0,0 +1 @@
+osname = Unix
diff --git a/src/lib/corelib/language/testdata/outerInGroup.qbs b/src/lib/corelib/language/testdata/outerInGroup.qbs
new file mode 100644
index 000000000..751392a4d
--- /dev/null
+++ b/src/lib/corelib/language/testdata/outerInGroup.qbs
@@ -0,0 +1,14 @@
+import qbs 1.0
+
+Project {
+ Product {
+ name: "OuterInGroup"
+ qbs.installDir: "/somewhere"
+ files: ["main.cpp"]
+ Group {
+ name: "Special Group"
+ files: ["aboutdialog.cpp"]
+ qbs.installDir: outer + "/else"
+ }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/pathproperties.qbs b/src/lib/corelib/language/testdata/pathproperties.qbs
new file mode 100644
index 000000000..f0eeabf57
--- /dev/null
+++ b/src/lib/corelib/language/testdata/pathproperties.qbs
@@ -0,0 +1,9 @@
+import "subdir/pathproperties_base.qbs" as ProductBase
+
+ProductBase {
+ name: "product1"
+ property path projectFileDir: "."
+ property pathList filesInProjectFileDir: ["./aboutdialog.h", "aboutdialog.cpp"]
+ Depends { name: "dummy" }
+ dummy.includePaths: ["."]
+}
diff --git a/src/lib/corelib/language/testdata/productconditions.qbs b/src/lib/corelib/language/testdata/productconditions.qbs
new file mode 100644
index 000000000..336c41340
--- /dev/null
+++ b/src/lib/corelib/language/testdata/productconditions.qbs
@@ -0,0 +1,19 @@
+import qbs 1.0
+
+Project {
+ Product {
+ name: "product_no_condition"
+ }
+ Product {
+ name: "product_true_condition"
+ condition: 1 === 1
+ }
+ Product {
+ name: "product_false_condition"
+ condition: 1 === 2
+ }
+ Product {
+ name: "product_condition_dependent_of_module"
+ condition: qbs.endianness !== (qbs.endianness + "foo")
+ }
+}
diff --git a/src/lib/corelib/language/testdata/productdirectories.qbs b/src/lib/corelib/language/testdata/productdirectories.qbs
new file mode 100644
index 000000000..dc4315207
--- /dev/null
+++ b/src/lib/corelib/language/testdata/productdirectories.qbs
@@ -0,0 +1,5 @@
+import qbs 1.0
+
+Product {
+ name: "MyApp"
+}
diff --git a/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs b/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs
new file mode 100644
index 000000000..cc1b7b2a2
--- /dev/null
+++ b/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs
@@ -0,0 +1,19 @@
+import qbs 1.0
+
+Project {
+ Application {
+ name: {
+ 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";
+ }
+ Depends { name: "dummy" }
+ // dummy.cxxFlags is set via profile and is not overridden
+ dummy.defines: ["IN_FILE"] // set in profile, overridden in file
+ dummy.cFlags: ["IN_FILE"] // set in profile, overridden on command line
+ }
+}
diff --git a/src/lib/corelib/language/testdata/propertiesblocks.qbs b/src/lib/corelib/language/testdata/propertiesblocks.qbs
new file mode 100644
index 000000000..d61f0dd86
--- /dev/null
+++ b/src/lib/corelib/language/testdata/propertiesblocks.qbs
@@ -0,0 +1,150 @@
+import qbs 1.0
+import "propertiesblocks_base.qbs" as ProductBase
+
+Project {
+ Product {
+ name: "property_overwrite"
+ Depends { id: cpp; name: "dummy" }
+ cpp.defines: ["SOMETHING"]
+ Properties {
+ condition: true
+ cpp.defines: ["OVERWRITTEN"]
+ }
+ }
+ Product {
+ name: "property_overwrite_no_outer"
+ Depends { id: cpp; name: "dummy" }
+ Properties {
+ condition: true
+ cpp.defines: ["OVERWRITTEN"]
+ }
+ }
+ Product {
+ name: "property_append_to_outer"
+ Depends { id: cpp; name: "dummy" }
+ cpp.defines: ["ONE"]
+ Properties {
+ condition: true
+ cpp.defines: outer.concat(["TWO"])
+ }
+ }
+ Product {
+ name: "multiple_exclusive_properties"
+ Depends { id: cpp; name: "dummy" }
+ cpp.defines: ["SOMETHING"]
+ Properties {
+ condition: true
+ cpp.defines: ["OVERWRITTEN"]
+ }
+ Properties {
+ condition: false
+ cpp.defines: ["IMPOSSIBLE"]
+ }
+ }
+ Product {
+ name: "multiple_exclusive_properties_no_outer"
+ Depends { id: cpp; name: "dummy" }
+ Properties {
+ condition: true
+ cpp.defines: ["OVERWRITTEN"]
+ }
+ Properties {
+ condition: false
+ cpp.defines: ["IMPOSSIBLE"]
+ }
+ }
+ Product {
+ name: "multiple_exclusive_properties_append_to_outer"
+ Depends { id: cpp; name: "dummy" }
+ cpp.defines: ["ONE"]
+ Properties {
+ condition: true
+ cpp.defines: outer.concat(["TWO"])
+ }
+ Properties {
+ condition: false
+ cpp.defines: ["IMPOSSIBLE"]
+ }
+ }
+ Product {
+ name: "ambiguous_properties"
+ Depends { id: cpp; name: "dummy" }
+ cpp.defines: ["ONE"]
+ Properties {
+ condition: true
+ cpp.defines: outer.concat(["TWO"])
+ }
+ Properties {
+ condition: false
+ cpp.defines: outer.concat(["IMPOSSIBLE"])
+ }
+ Properties { // will be ignored
+ condition: true
+ cpp.defines: outer.concat(["THREE"])
+ }
+ }
+ Product {
+ name: "condition_refers_to_product_property"
+ property string narf: true
+ Depends { name: "dummy" }
+ Properties {
+ condition: narf
+ dummy.defines: ["OVERWRITTEN"]
+ }
+ }
+ property string zort: true
+ Product {
+ name: "condition_refers_to_project_property"
+ Depends { name: "dummy" }
+ Properties {
+ condition: project.zort
+ dummy.defines: ["OVERWRITTEN"]
+ }
+ }
+ ProductBase {
+ name: "inheritance_overwrite_in_subitem"
+ dummy.defines: ["OVERWRITTEN_IN_SUBITEM"]
+ }
+ ProductBase {
+ name: "inheritance_retain_base1"
+ dummy.defines: base.concat("SUB")
+ }
+ ProductBase {
+ name: "inheritance_retain_base2"
+ Properties {
+ condition: true
+ dummy.defines: base.concat("SUB")
+ }
+ dummy.defines: ["GNAMPF"]
+ }
+ ProductBase {
+ name: "inheritance_retain_base3"
+ Properties {
+ condition: true
+ dummy.defines: base.concat("SUB")
+ }
+ // no dummy.defines binding
+ }
+ ProductBase {
+ name: "inheritance_condition_in_subitem1"
+ defineBase: false
+ dummy.defines: base.concat("SUB")
+ }
+ ProductBase {
+ name: "inheritance_condition_in_subitem2"
+ defineBase: false
+ // no dummy.defines binding
+ }
+ Product {
+ id: knolf
+ name: "gnampf"
+ }
+ Product {
+ name: "condition_references_id"
+ Depends { id: cpp; name: "dummy" }
+ Properties {
+ condition: knolf.name === "gnampf"
+ cpp.defines: ["OVERWRITTEN"]
+ }
+ }
+}
diff --git a/src/lib/corelib/language/testdata/propertiesblocks_base.qbs b/src/lib/corelib/language/testdata/propertiesblocks_base.qbs
new file mode 100644
index 000000000..71b09a3da
--- /dev/null
+++ b/src/lib/corelib/language/testdata/propertiesblocks_base.qbs
@@ -0,0 +1,11 @@
+import qbs 1.0
+
+Product {
+ property bool defineBase: true
+ Depends { name: "dummy" }
+ Properties {
+ condition: defineBase
+ dummy.defines: ["BASE"]
+ }
+ dummy.defines: ["SOMETHING"]
+}
diff --git a/src/lib/corelib/language/testdata/subdir/pathproperties_base.qbs b/src/lib/corelib/language/testdata/subdir/pathproperties_base.qbs
new file mode 100644
index 000000000..62427169f
--- /dev/null
+++ b/src/lib/corelib/language/testdata/subdir/pathproperties_base.qbs
@@ -0,0 +1,4 @@
+Product {
+ property path base_fileInProductDir: "foo"
+ property path base_fileInBaseProductDir: path + "/bar"
+}
diff --git a/src/lib/corelib/language/testdata/zort b/src/lib/corelib/language/testdata/zort
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/lib/corelib/language/testdata/zort
diff --git a/src/lib/corelib/language/tst_language.cpp b/src/lib/corelib/language/tst_language.cpp
new file mode 100644
index 000000000..2308a7aa2
--- /dev/null
+++ b/src/lib/corelib/language/tst_language.cpp
@@ -0,0 +1,1439 @@
+/****************************************************************************
+**
+** 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 "tst_language.h"
+
+#include <language/evaluator.h>
+#include <language/identifiersearch.h>
+#include <language/item.h>
+#include <language/itempool.h>
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <parser/qmljslexer_p.h>
+#include <parser/qmljsparser_p.h>
+#include <tools/scripttools.h>
+#include <tools/error.h>
+#include <tools/hostosinfo.h>
+#include <tools/propertyfinder.h>
+
+#include <QProcessEnvironment>
+
+Q_DECLARE_METATYPE(QList<bool>)
+
+namespace qbs {
+namespace Internal {
+static QString testDataDir() { return FileInfo::resolvePath(QLatin1String(SRCDIR),
+ QLatin1String("language/testdata")); }
+static QString testProject(const char *fileName) {
+ return testDataDir() + QLatin1Char('/') + QLatin1String(fileName);
+}
+
+TestLanguage::TestLanguage(ILogSink *logSink)
+ : m_logSink(logSink)
+ , m_wildcardsTestDirPath(QDir::tempPath() + QLatin1String("/_wildcards_test_dir_"))
+{
+ qsrand(QTime::currentTime().msec());
+ qRegisterMetaType<QList<bool> >("QList<bool>");
+ defaultParameters.setBuildRoot("/some/build/directory");
+}
+
+TestLanguage::~TestLanguage()
+{
+}
+
+QHash<QString, ResolvedProductPtr> TestLanguage::productsFromProject(ResolvedProjectPtr project)
+{
+ QHash<QString, ResolvedProductPtr> result;
+ foreach (ResolvedProductPtr product, project->products)
+ result.insert(product->name, product);
+ return result;
+}
+
+ResolvedModuleConstPtr TestLanguage::findModuleByName(ResolvedProductPtr product, const QString &name)
+{
+ foreach (const ResolvedModuleConstPtr &module, product->modules)
+ if (module->name == name)
+ return module;
+ return ResolvedModuleConstPtr();
+}
+
+QVariant TestLanguage::productPropertyValue(ResolvedProductPtr product, QString propertyName)
+{
+ QStringList propertyNameComponents = propertyName.split(QLatin1Char('.'));
+ if (propertyNameComponents.count() > 1)
+ propertyNameComponents.prepend(QLatin1String("modules"));
+ return getConfigProperty(product->properties->value(), propertyNameComponents);
+}
+
+void TestLanguage::handleInitCleanupDataTags(const char *projectFileName, bool *handled)
+{
+ const QByteArray dataTag = QTest::currentDataTag();
+ if (dataTag == "init") {
+ *handled = true;
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject(projectFileName));
+ project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+ } else if (dataTag == "cleanup") {
+ *handled = true;
+ project.clear();
+ } else {
+ *handled = false;
+ }
+}
+
+QString TestLanguage::buildDir(const SetupProjectParameters &params) const
+{
+ return FileInfo::resolvePath(params.buildRoot(),
+ getConfigProperty(params.buildConfigurationTree(),
+ QStringList() << "qbs" << "profile").toString() + QLatin1Char('-')
+ + getConfigProperty(params.buildConfigurationTree(),
+ QStringList() << "qbs" << "buildVariant").toString());
+}
+
+#define HANDLE_INIT_CLEANUP_DATATAGS(fn) {\
+ bool handled;\
+ handleInitCleanupDataTags(fn, &handled);\
+ if (handled)\
+ return;\
+ QVERIFY(project);\
+}
+
+void TestLanguage::initTestCase()
+{
+ m_logger = Logger(m_logSink);
+ m_engine = new ScriptEngine(m_logger, this);
+ loader = new Loader(m_engine, m_logger);
+ loader->setSearchPaths(QStringList()
+ << QLatin1String(SRCDIR "/../../../share/qbs"));
+ QVariantMap buildConfig = defaultParameters.buildConfigurationTree();
+ buildConfig.insert("qbs.targetOS", "linux");
+ buildConfig.insert("qbs.architecture", "x86_64");
+ buildConfig.insert("qbs.profile", "qbs_autotests");
+ defaultParameters.setBuildConfiguration(buildConfig);
+ QVERIFY(QFileInfo(m_wildcardsTestDirPath).isAbsolute());
+}
+
+void TestLanguage::cleanupTestCase()
+{
+ delete loader;
+}
+
+void TestLanguage::baseProperty()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("baseproperty.qbs"));
+ project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ ResolvedProductPtr product = products.value("product1");
+ QVERIFY(product);
+ QVariantMap cfg = product->properties->value();
+ QCOMPARE(cfg.value("narf").toStringList(), QStringList() << "boo");
+ QCOMPARE(cfg.value("zort").toStringList(), QStringList() << "bar" << "boo");
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::buildConfigStringListSyntax()
+{
+ bool exceptionCaught = false;
+ try {
+ SetupProjectParameters parameters = defaultParameters;
+ QVariantMap overriddenValues;
+ overriddenValues.insert("project.someStrings", "foo,bar,baz");
+ parameters.setOverriddenValues(overriddenValues);
+ parameters.setProjectFilePath(testProject("buildconfigstringlistsyntax.qbs"));
+ project = loader->loadProject(parameters);
+ QVERIFY(project);
+ QCOMPARE(project->projectProperties().value("someStrings").toStringList(),
+ QStringList() << "foo" << "bar" << "baz");
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::builtinFunctionInSearchPathsProperty()
+{
+ bool exceptionCaught = false;
+ try {
+ SetupProjectParameters parameters = defaultParameters;
+ parameters.setProjectFilePath(testProject("builtinFunctionInSearchPathsProperty.qbs"));
+ QVERIFY(loader->loadProject(parameters));
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::canonicalArchitecture()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("canonicalArchitecture.qbs"));
+ project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ ResolvedProductPtr product = products.value(QLatin1String("x86"));
+ QVERIFY(product);
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::conditionalDepends()
+{
+ bool exceptionCaught = false;
+ ResolvedProductPtr product;
+ ResolvedModuleConstPtr dependency;
+ try {
+ defaultParameters.setProjectFilePath(testProject("conditionaldepends.qbs"));
+ project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+
+ product = products.value("conditionaldepends_derived");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy");
+ QVERIFY(dependency);
+
+ product = products.value("conditionaldepends_derived_false");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy");
+ QCOMPARE(dependency, ResolvedModuleConstPtr());
+
+ product = products.value("product_props_true");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy");
+ QVERIFY(dependency);
+
+ product = products.value("product_props_false");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy");
+ QCOMPARE(dependency, ResolvedModuleConstPtr());
+
+ product = products.value("project_props_true");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy");
+ QVERIFY(dependency);
+
+ product = products.value("project_props_false");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy");
+ QCOMPARE(dependency, ResolvedModuleConstPtr());
+
+ product = products.value("module_props_true");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy2");
+ QVERIFY(dependency);
+ dependency = findModuleByName(product, "dummy");
+ QVERIFY(dependency);
+
+ product = products.value("module_props_false");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy2");
+ QVERIFY(dependency);
+ dependency = findModuleByName(product, "dummy");
+ QCOMPARE(dependency, ResolvedModuleConstPtr());
+
+ product = products.value("contradictory_conditions1");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy");
+ QVERIFY(dependency);
+
+ product = products.value("contradictory_conditions2");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "dummy");
+ QVERIFY(dependency);
+
+ product = products.value("unknown_dependency_condition_false");
+ QVERIFY(product);
+ dependency = findModuleByName(product, "doesonlyexistifhellfreezesover");
+ QVERIFY(!dependency);
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::environmentVariable()
+{
+ bool exceptionCaught = false;
+ try {
+ // Create new environment:
+ const QString varName = QLatin1String("PRODUCT_NAME");
+ const QString productName = QLatin1String("MyApp") + QString::number(qrand());
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ env.insert(varName, productName);
+
+ QProcessEnvironment origEnv = defaultParameters.environment(); // store orig environment
+
+ defaultParameters.setEnvironment(env);
+ defaultParameters.setProjectFilePath(testProject("environmentvariable.qbs"));
+ project = loader->loadProject(defaultParameters);
+
+ defaultParameters.setEnvironment(origEnv); // reset environment
+
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ ResolvedProductPtr product = products.value(productName);
+ QVERIFY(product);
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::erroneousFiles_data()
+{
+ QTest::addColumn<QString>("errorMessage");
+ QTest::newRow("unknown_module")
+ << "Product dependency 'neitherModuleNorProduct' not found";
+ QTest::newRow("submodule_syntax")
+ << "Depends.submodules cannot be used if name contains a dot";
+ QTest::newRow("multiple_exports")
+ << "Multiple Export items in one product are prohibited.";
+ QTest::newRow("multiple_properties_in_subproject")
+ << "Multiple instances of item 'Properties' found where at most one "
+ "is allowed.";
+ QTest::newRow("importloop1")
+ << "Loop detected when importing";
+ QTest::newRow("nonexistentouter")
+ << "Can't find variable: outer";
+ QTest::newRow("invalid_file")
+ << "does not exist";
+ QTest::newRow("invalid_property_type")
+ << "Unknown type 'nonsense' in property declaration.";
+ QTest::newRow("reserved_name_in_import")
+ << "Cannot reuse the name of built-in extension 'TextFile'.";
+ QTest::newRow("throw_in_property_binding")
+ << "something is wrong";
+ QTest::newRow("references_cycle")
+ << "Cycle detected while referencing file 'references_cycle.qbs'.";
+ QTest::newRow("subproject_cycle")
+ << "Cycle detected while loading subproject file 'subproject_cycle.qbs'.";
+ QTest::newRow("invalid_stringlist_element")
+ << "Expected array element of type String at index 1.";
+ QTest::newRow("undeclared_item")
+ << "Item 'cpp' is not declared.";
+ QTest::newRow("undeclared_property")
+ << "Property 'doesntexist' is not declared.";
+ QTest::newRow("unknown_item_type")
+ << "Unexpected item type 'Narf'";
+ QTest::newRow("invalid_child_item_type")
+ << "Items of type 'Project' cannot contain items of type 'Depends'.";
+}
+
+void TestLanguage::erroneousFiles()
+{
+ QFETCH(QString, errorMessage);
+ QString fileName = QString::fromLocal8Bit(QTest::currentDataTag()) + QLatin1String(".qbs");
+ try {
+ defaultParameters.setProjectFilePath(testProject("/erroneous/") + fileName);
+ loader->loadProject(defaultParameters);
+ } catch (const ErrorInfo &e) {
+ if (!e.toString().contains(errorMessage)) {
+ qDebug() << "Message: " << e.toString();
+ qDebug() << "Expected: " << errorMessage;
+ QFAIL("Unexpected error message.");
+ }
+ return;
+ }
+ QFAIL("No error thrown on invalid input.");
+}
+
+void TestLanguage::exports()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("exports.qbs"));
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 8);
+ ResolvedProductPtr product;
+ product = products.value("myapp");
+ QVERIFY(product);
+ QStringList propertyName = QStringList() << "modules" << "mylib"
+ << "modules" << "dummy" << "defines";
+ QVariant propertyValue = getConfigProperty(product->properties->value(), propertyName);
+ QCOMPARE(propertyValue.toStringList(), QStringList() << "USE_MYLIB");
+ product = products.value("mylib");
+
+ QVERIFY(product);
+ propertyName = QStringList() << "modules" << "dummy" << "defines";
+ propertyValue = getConfigProperty(product->properties->value(), propertyName);
+ QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYLIB");
+
+ product = products.value("A");
+ QVERIFY(product);
+ QVERIFY(product->dependencies.contains(products.value("B")));
+ QVERIFY(product->dependencies.contains(products.value("C")));
+ QVERIFY(product->dependencies.contains(products.value("D")));
+ product = products.value("B");
+ QVERIFY(product);
+ QVERIFY(product->dependencies.isEmpty());
+ product = products.value("C");
+ QVERIFY(product);
+ QVERIFY(product->dependencies.isEmpty());
+ product = products.value("D");
+ QVERIFY(product);
+ QVERIFY(product->dependencies.isEmpty());
+
+ product = products.value("myapp2");
+ QVERIFY(product);
+ propertyName = QStringList() << "modules" << "productWithInheritedExportItem"
+ << "modules" << "dummy" << "cxxFlags";
+ propertyValue = getConfigProperty(product->properties->value(), propertyName);
+ QCOMPARE(propertyValue.toStringList(), QStringList() << "-bar");
+ propertyName = QStringList() << "modules" << "productWithInheritedExportItem"
+ << "modules" << "dummy" << "defines";
+ propertyValue = getConfigProperty(product->properties->value(), propertyName);
+ QCOMPARE(propertyValue.toStringList(), QStringList() << "ABC");
+ }
+ catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::fileContextProperties()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("filecontextproperties.qbs"));
+ project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ ResolvedProductPtr product = products.value("product1");
+ QVERIFY(product);
+ QVariantMap cfg = product->properties->value();
+ QCOMPARE(cfg.value("narf").toString(), defaultParameters.projectFilePath());
+ QString dirPath = QFileInfo(defaultParameters.projectFilePath()).absolutePath();
+ QCOMPARE(cfg.value("zort").toString(), dirPath);
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::getNativeSetting()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("getNativeSetting.qbs"));
+ project = loader->loadProject(defaultParameters);
+
+ QString expectedProductName;
+ if (HostOsInfo::isOsxHost())
+ expectedProductName = QLatin1String("Mac OS X");
+ else if (HostOsInfo::isWindowsHost())
+ expectedProductName = QLatin1String("Windows");
+ else
+ expectedProductName = QLatin1String("Unix");
+
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ ResolvedProductPtr product = products.value(expectedProductName);
+ QVERIFY(product);
+ ResolvedProductPtr product2 = products.value(QLatin1String("fallback"));
+ QVERIFY(product2);
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::groupConditions_data()
+{
+ QTest::addColumn<int>("groupCount");
+ QTest::addColumn<QList<bool> >("groupEnabled");
+ QTest::newRow("init") << 0 << QList<bool>();
+ QTest::newRow("no_condition_no_group")
+ << 1 << (QList<bool>() << true);
+ QTest::newRow("no_condition")
+ << 2 << (QList<bool>() << true << true);
+ QTest::newRow("true_condition")
+ << 2 << (QList<bool>() << true << true);
+ QTest::newRow("false_condition")
+ << 2 << (QList<bool>() << true << false);
+ QTest::newRow("true_condition_from_product")
+ << 2 << (QList<bool>() << true << true);
+ QTest::newRow("true_condition_from_project")
+ << 2 << (QList<bool>() << true << true);
+ QTest::newRow("condition_accessing_module_property")
+ << 2 << (QList<bool>() << true << false);
+ QTest::newRow("cleanup") << 0 << QList<bool>();
+}
+
+void TestLanguage::groupConditions()
+{
+ HANDLE_INIT_CLEANUP_DATATAGS("groupconditions.qbs");
+ QFETCH(int, groupCount);
+ QFETCH(QList<bool>, groupEnabled);
+ QCOMPARE(groupCount, groupEnabled.count());
+ const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ const QString productName = QString::fromLocal8Bit(QTest::currentDataTag());
+ ResolvedProductPtr product = products.value(productName);
+ QVERIFY(product);
+ QCOMPARE(product->name, productName);
+ QCOMPARE(product->groups.count(), groupCount);
+ for (int i = 0; i < groupCount; ++i) {
+ if (product->groups.at(i)->enabled != groupEnabled.at(i)) {
+ QFAIL(qPrintable(
+ QString("groups.at(%1)->enabled != %2").arg(i).arg(groupEnabled.at(i))));
+ }
+ }
+}
+
+void TestLanguage::groupName()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("groupname.qbs"));
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 2);
+
+ ResolvedProductPtr product = products.value("MyProduct");
+ QVERIFY(product);
+ QCOMPARE(product->groups.count(), 2);
+ GroupConstPtr group = product->groups.at(0);
+ QVERIFY(group);
+ QCOMPARE(group->name, QString("MyProduct"));
+ group = product->groups.at(1);
+ QVERIFY(group);
+ QCOMPARE(group->name, QString("MyProduct.MyGroup"));
+
+ product = products.value("My2ndProduct");
+ QVERIFY(product);
+ QCOMPARE(product->groups.count(), 3);
+ group = product->groups.at(0);
+ QVERIFY(group);
+ QCOMPARE(group->name, QString("My2ndProduct"));
+ group = product->groups.at(1);
+ QVERIFY(group);
+ QCOMPARE(group->name, QString("My2ndProduct.MyGroup"));
+ group = product->groups.at(2);
+ QVERIFY(group);
+ QCOMPARE(group->name, QString("Group 2"));
+ }
+ catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::homeDirectory()
+{
+ try {
+ defaultParameters.setProjectFilePath(testProject("homeDirectory.qbs"));
+ ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 1);
+
+ ResolvedProductPtr product = products.value("home");
+ QVERIFY(product);
+
+ QDir dir = QDir::home();
+ QCOMPARE(product->properties->value().value("home").toString(), dir.canonicalPath());
+ QCOMPARE(product->properties->value().value("homeSlash").toString(), dir.canonicalPath());
+
+ dir.cdUp();
+ QCOMPARE(product->properties->value().value("homeUp").toString(), dir.canonicalPath());
+
+ dir = QDir::home();
+ QCOMPARE(product->properties->value().value("homeFile").toString(), dir.filePath("a"));
+
+ QCOMPARE(product->properties->value().value("bogus1").toString(),
+ FileInfo::resolvePath(product->sourceDirectory, QLatin1String("a~b")));
+ QCOMPARE(product->properties->value().value("bogus2").toString(),
+ FileInfo::resolvePath(product->sourceDirectory, QLatin1String("a/~/bb")));
+ QCOMPARE(product->properties->value().value("user").toString(),
+ FileInfo::resolvePath(product->sourceDirectory, QLatin1String("~foo/bar")));
+ }
+ catch (const ErrorInfo &e) {
+ qDebug() << e.toString();
+ }
+}
+
+void TestLanguage::identifierSearch_data()
+{
+ QTest::addColumn<bool>("expectedHasNarf");
+ QTest::addColumn<bool>("expectedHasZort");
+ QTest::addColumn<QString>("sourceCode");
+ QTest::newRow("no narf, no zort") << false << false << QString(
+ "Product {\n"
+ " name: {\n"
+ " var foo = 'bar';\n"
+ " print(foo);\n"
+ " return foo;\n"
+ " }\n"
+ "}\n");
+ QTest::newRow("narf, no zort") << true << false << QString(
+ "Product {\n"
+ " name: {\n"
+ " var foo = 'zort';\n"
+ " print(narf + foo);\n"
+ " return foo;\n"
+ " }\n"
+ "}\n");
+ QTest::newRow("no narf, zort") << false << true << QString(
+ "Product {\n"
+ " name: {\n"
+ " var foo = 'narf';\n"
+ " print(zort + foo);\n"
+ " return foo;\n"
+ " }\n"
+ "}\n");
+ QTest::newRow("narf, zort") << true << true << QString(
+ "Product {\n"
+ " name: {\n"
+ " var foo = narf;\n"
+ " foo = foo + zort;\n"
+ " return foo;\n"
+ " }\n"
+ "}\n");
+ QTest::newRow("2 narfs, 1 zort") << true << true << QString(
+ "Product {\n"
+ " name: {\n"
+ " var foo = narf;\n"
+ " foo = narf + foo + zort;\n"
+ " return foo;\n"
+ " }\n"
+ "}\n");
+}
+
+void TestLanguage::identifierSearch()
+{
+ QFETCH(bool, expectedHasNarf);
+ QFETCH(bool, expectedHasZort);
+ QFETCH(QString, sourceCode);
+
+ bool hasNarf = !expectedHasNarf;
+ bool hasZort = !expectedHasZort;
+ IdentifierSearch isearch;
+ isearch.add("narf", &hasNarf);
+ isearch.add("zort", &hasZort);
+
+ QbsQmlJS::Engine engine;
+ QbsQmlJS::Lexer lexer(&engine);
+ lexer.setCode(sourceCode, 1);
+ QbsQmlJS::Parser parser(&engine);
+ QVERIFY(parser.parse());
+ QVERIFY(parser.ast());
+ isearch.start(parser.ast());
+ QCOMPARE(hasNarf, expectedHasNarf);
+ QCOMPARE(hasZort, expectedHasZort);
+}
+
+void TestLanguage::idUsage()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("idusage.qbs"));
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 3);
+ QVERIFY(products.contains("product1_1"));
+ QVERIFY(products.contains("product2_2"));
+ QVERIFY(products.contains("product3_3"));
+ }
+ catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QVERIFY(!exceptionCaught);
+}
+
+void TestLanguage::invalidBindingInDisabledItem()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("invalidBindingInDisabledItem.qbs"));
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 2);
+ }
+ catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QVERIFY(!exceptionCaught);
+}
+
+class JSSourceValueCreator
+{
+ FileContextPtr m_fileContext;
+public:
+ JSSourceValueCreator(const FileContextPtr &fileContext)
+ : m_fileContext(fileContext)
+ {
+ }
+
+ JSSourceValuePtr create(const QString &sourceCode)
+ {
+ JSSourceValuePtr value = JSSourceValue::create();
+ value->setFile(m_fileContext);
+ value->setSourceCode(sourceCode);
+ return value;
+ }
+};
+
+void TestLanguage::itemPrototype()
+{
+ FileContextPtr fileContext = FileContext::create();
+ JSSourceValueCreator sourceValueCreator(fileContext);
+ ItemPool pool;
+ Item *proto = Item::create(&pool);
+ proto->setProperty("x", sourceValueCreator.create("1"));
+ proto->setProperty("y", sourceValueCreator.create("1"));
+ Item *item = Item::create(&pool);
+ item->setPrototype(proto);
+ item->setProperty("y", sourceValueCreator.create("x + 1"));
+ item->setProperty("z", sourceValueCreator.create("2"));
+
+ Evaluator evaluator(m_engine, m_logger);
+ QCOMPARE(evaluator.property(proto, "x").toVariant().toInt(), 1);
+ QCOMPARE(evaluator.property(proto, "y").toVariant().toInt(), 1);
+ QVERIFY(!evaluator.property(proto, "z").isValid());
+ QCOMPARE(evaluator.property(item, "x").toVariant().toInt(), 1);
+ QCOMPARE(evaluator.property(item, "y").toVariant().toInt(), 2);
+ QCOMPARE(evaluator.property(item, "z").toVariant().toInt(), 2);
+}
+
+void TestLanguage::itemScope()
+{
+ FileContextPtr fileContext = FileContext::create();
+ JSSourceValueCreator sourceValueCreator(fileContext);
+ ItemPool pool;
+ Item *scope1 = Item::create(&pool);
+ scope1->setProperty("x", sourceValueCreator.create("1"));
+ Item *scope2 = Item::create(&pool);
+ scope2->setScope(scope1);
+ scope2->setProperty("y", sourceValueCreator.create("x + 1"));
+ Item *item = Item::create(&pool);
+ item->setScope(scope2);
+ item->setProperty("z", sourceValueCreator.create("x + y"));
+
+ Evaluator evaluator(m_engine, m_logger);
+ QCOMPARE(evaluator.property(scope1, "x").toVariant().toInt(), 1);
+ QCOMPARE(evaluator.property(scope2, "y").toVariant().toInt(), 2);
+ QVERIFY(!evaluator.property(scope2, "x").isValid());
+ QCOMPARE(evaluator.property(item, "z").toVariant().toInt(), 3);
+}
+
+void TestLanguage::jsExtensions()
+{
+ QFile file(testProject("jsextensions.js"));
+ QVERIFY(file.open(QFile::ReadOnly));
+ QTextStream ts(&file);
+ QString code = ts.readAll();
+ QVERIFY(!code.isEmpty());
+ QScriptValue evaluated = m_engine->evaluate(code, file.fileName(), 1);
+ if (m_engine->hasErrorOrException(evaluated)) {
+ qDebug() << m_engine->uncaughtExceptionBacktrace();
+ QFAIL(qPrintable(evaluated.toString()));
+ }
+}
+
+void TestLanguage::jsImportUsedInMultipleScopes_data()
+{
+ QTest::addColumn<QString>("buildVariant");
+ QTest::addColumn<QString>("expectedProductName");
+ QTest::newRow("debug") << QString("debug") << QString("MyProduct_debug");
+ QTest::newRow("release") << QString("release") << QString("MyProduct");
+}
+
+void TestLanguage::jsImportUsedInMultipleScopes()
+{
+ QFETCH(QString, buildVariant);
+ QFETCH(QString, expectedProductName);
+
+ bool exceptionCaught = false;
+ try {
+ QVariantMap customBuildConfig = defaultParameters.buildConfiguration();
+ customBuildConfig.insert(QLatin1String("qbs.buildVariant"), buildVariant);
+ SetupProjectParameters params = defaultParameters;
+ params.setProjectFilePath(testProject("jsimportsinmultiplescopes.qbs"));
+ params.setBuildConfiguration(customBuildConfig);
+ TopLevelProjectPtr project = loader->loadProject(params);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 1);
+ ResolvedProductPtr product = products.values().first();
+ QVERIFY(product);
+ QCOMPARE(product->name, expectedProductName);
+ }
+ catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QVERIFY(!exceptionCaught);
+}
+
+void TestLanguage::moduleProperties_data()
+{
+ QTest::addColumn<QString>("propertyName");
+ QTest::addColumn<QStringList>("expectedValues");
+ QTest::newRow("init") << QString() << QStringList();
+ QTest::newRow("merge_lists")
+ << "defines"
+ << (QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK");
+ QTest::newRow("merge_lists_and_values")
+ << "defines"
+ << (QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK");
+ QTest::newRow("merge_lists_with_duplicates")
+ << "cxxFlags"
+ << (QStringList() << "-foo" << "BAR" << "-foo" << "BAZ");
+ QTest::newRow("cleanup") << QString() << QStringList();
+}
+
+void TestLanguage::moduleProperties()
+{
+ HANDLE_INIT_CLEANUP_DATATAGS("moduleproperties.qbs");
+ QFETCH(QString, propertyName);
+ QFETCH(QStringList, expectedValues);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ const QString productName = QString::fromLocal8Bit(QTest::currentDataTag());
+ ResolvedProductPtr product = products.value(productName);
+ QVERIFY(product);
+ QVariantList values = PropertyFinder().propertyValues(product->properties->value(),
+ "dummy", propertyName,
+ PropertyFinder::DoMergeLists);
+ QStringList valueStrings;
+ foreach (const QVariant &v, values)
+ valueStrings += v.toString();
+ QCOMPARE(valueStrings, expectedValues);
+}
+
+void TestLanguage::moduleScope()
+{
+ class IntPropertyFinder
+ {
+ const QVariantMap &m_properties;
+ public:
+ IntPropertyFinder(const QVariantMap &properties)
+ : m_properties(properties)
+ {}
+
+ int intValue(const QString &name)
+ {
+ return PropertyFinder().propertyValue(m_properties, "scopemod", name).toInt();
+ }
+ };
+
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("modulescope.qbs"));
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 1);
+ ResolvedProductPtr product = products.value("product1");
+ QVERIFY(product);
+ IntPropertyFinder ipf(product->properties->value());
+ QCOMPARE(ipf.intValue("a"), 2); // overridden in module instance
+ QCOMPARE(ipf.intValue("b"), 1); // genuine
+ QCOMPARE(ipf.intValue("c"), 3); // genuine, dependent on overridden value
+ QCOMPARE(ipf.intValue("d"), 2); // genuine, dependent on genuine value
+ QCOMPARE(ipf.intValue("e"), 1); // genuine
+ QCOMPARE(ipf.intValue("f"), 2); // overridden
+ QCOMPARE(ipf.intValue("g"), 156); // overridden, dependent on product properties
+ QCOMPARE(ipf.intValue("h"), 158); // overridden, base dependent on product properties
+ }
+ catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+
+}
+
+void TestLanguage::modules_data()
+{
+ QTest::addColumn<QStringList>("expectedModulesInProduct");
+ QTest::addColumn<QString>("expectedProductProperty");
+ QTest::newRow("init") << QStringList();
+ QTest::newRow("no_modules")
+ << (QStringList() << "qbs")
+ << QString();
+ QTest::newRow("qt_core")
+ << (QStringList() << "qbs" << "dummy" << "dummyqt/core")
+ << QString("1.2.3");
+ QTest::newRow("qt_gui")
+ << (QStringList() << "qbs" << "dummy" << "dummyqt/core" << "dummyqt/gui")
+ << QString("guiProperty");
+ QTest::newRow("qt_gui_network")
+ << (QStringList() << "qbs" << "dummy" << "dummyqt/core" << "dummyqt/gui"
+ << "dummyqt/network")
+ << QString("guiProperty,networkProperty");
+ QTest::newRow("dummy_twice")
+ << (QStringList() << "qbs" << "dummy")
+ << QString();
+ QTest::newRow("cleanup") << QStringList();
+}
+
+void TestLanguage::modules()
+{
+ HANDLE_INIT_CLEANUP_DATATAGS("modules.qbs");
+ QFETCH(QStringList, expectedModulesInProduct);
+ QFETCH(QString, expectedProductProperty);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ const QString productName = QString::fromLocal8Bit(QTest::currentDataTag());
+ ResolvedProductPtr product = products.value(productName);
+ QVERIFY(product);
+ QCOMPARE(product->name, productName);
+ QStringList modulesInProduct;
+ foreach (ResolvedModuleConstPtr m, product->modules)
+ modulesInProduct += m->name;
+ modulesInProduct.sort();
+ expectedModulesInProduct.sort();
+ QCOMPARE(modulesInProduct, expectedModulesInProduct);
+ QCOMPARE(product->properties->value().value("foo").toString(), expectedProductProperty);
+}
+
+void TestLanguage::outerInGroup()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("outerInGroup.qbs"));
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 1);
+ ResolvedProductPtr product = products.value("OuterInGroup");
+ QVERIFY(product);
+ QCOMPARE(product->groups.count(), 2);
+ GroupPtr group = product->groups.at(0);
+ QVERIFY(group);
+ QCOMPARE(group->name, product->name);
+ QCOMPARE(group->files.count(), 1);
+ SourceArtifactConstPtr artifact = group->files.first();
+ QVariant installDir = artifact->properties->qbsPropertyValue("installDir");
+ QCOMPARE(installDir.toString(), QString("/somewhere"));
+ group = product->groups.at(1);
+ QVERIFY(group);
+ QCOMPARE(group->name, QString("Special Group"));
+ QCOMPARE(group->files.count(), 1);
+ artifact = group->files.first();
+ installDir = artifact->properties->qbsPropertyValue("installDir");
+ QCOMPARE(installDir.toString(), QString("/somewhere/else"));
+ }
+ catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::pathProperties()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("pathproperties.qbs"));
+ project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ ResolvedProductPtr product = products.value("product1");
+ QVERIFY(product);
+ QVariantMap cfg = product->properties->value();
+ QString projectFileDir = QFileInfo(defaultParameters.projectFilePath()).absolutePath();
+ QCOMPARE(cfg.value("projectFileDir").toString(), projectFileDir);
+ QStringList filesInProjectFileDir = QStringList()
+ << FileInfo::resolvePath(projectFileDir, "aboutdialog.h")
+ << FileInfo::resolvePath(projectFileDir, "aboutdialog.cpp");
+ QCOMPARE(cfg.value("filesInProjectFileDir").toStringList(), filesInProjectFileDir);
+ QStringList includePaths = getConfigProperty(cfg, QStringList() << "modules" << "dummy"
+ << "includePaths").toStringList();
+ QCOMPARE(includePaths, QStringList() << projectFileDir);
+ QCOMPARE(cfg.value("base_fileInProductDir").toString(),
+ FileInfo::resolvePath(projectFileDir, QLatin1String("foo")));
+ QCOMPARE(cfg.value("base_fileInBaseProductDir").toString(),
+ FileInfo::resolvePath(projectFileDir, QLatin1String("subdir/bar")));
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::profileValuesAndOverriddenValues()
+{
+ bool exceptionCaught = false;
+ try {
+ SetupProjectParameters parameters = defaultParameters;
+ QVariantMap buildConfig = parameters.buildConfiguration();
+ buildConfig.insert("dummy.defines", "IN_PROFILE");
+ buildConfig.insert("dummy.cFlags", "IN_PROFILE");
+ buildConfig.insert("dummy.cxxFlags", "IN_PROFILE");
+ parameters.setBuildConfiguration(buildConfig);
+ QVariantMap overriddenValues;
+ overriddenValues.insert("dummy.cFlags", "OVERRIDDEN");
+ parameters.setOverriddenValues(overriddenValues);
+ parameters.setProjectFilePath(testProject("profilevaluesandoverriddenvalues.qbs"));
+ project = loader->loadProject(parameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ ResolvedProductPtr product = products.value("product1");
+ QVERIFY(product);
+ PropertyFinder pf;
+ QVariantList values;
+ values = pf.propertyValues(product->properties->value(),
+ "dummy", "cxxFlags", PropertyFinder::DoMergeLists);
+ QCOMPARE(values.length(), 1);
+ QCOMPARE(values.first().toString(), QString("IN_PROFILE"));
+ values = pf.propertyValues(product->properties->value(),
+ "dummy", "defines", PropertyFinder::DoMergeLists);
+ QCOMPARE(values.length(), 1);
+ QCOMPARE(values.first().toString(), QString("IN_FILE"));
+ values = pf.propertyValues(product->properties->value(),
+ "dummy", "cFlags", PropertyFinder::DoMergeLists);
+ QCOMPARE(values.length(), 1);
+ QCOMPARE(values.first().toString(), QString("OVERRIDDEN"));
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::productConditions()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("productconditions.qbs"));
+ TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 4);
+ ResolvedProductPtr product;
+ product = products.value("product_no_condition");
+ QVERIFY(product);
+ QVERIFY(product->enabled);
+
+ product = products.value("product_true_condition");
+ QVERIFY(product);
+ QVERIFY(product->enabled);
+
+ product = products.value("product_condition_dependent_of_module");
+ QVERIFY(product);
+ QVERIFY(product->enabled);
+
+ product = products.value("product_false_condition");
+ QVERIFY(product);
+ QVERIFY(!product->enabled);
+ }
+ catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::productDirectories()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("productdirectories.qbs"));
+ ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.count(), 1);
+ ResolvedProductPtr product;
+ product = products.value("MyApp");
+ QVERIFY(product);
+ const QVariantMap config = product->properties->value();
+ QCOMPARE(config.value(QLatin1String("buildDirectory")).toString(),
+ buildDir(defaultParameters));
+ QCOMPARE(config.value(QLatin1String("sourceDirectory")).toString(), testDataDir());
+ }
+ catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+void TestLanguage::propertiesBlocks_data()
+{
+ QTest::addColumn<QString>("propertyName");
+ QTest::addColumn<QStringList>("expectedValues");
+
+ QTest::newRow("init") << QString() << QStringList();
+ QTest::newRow("property_overwrite") << QString("dummy.defines") << QStringList("OVERWRITTEN");
+ QTest::newRow("property_overwrite_no_outer") << QString("dummy.defines") << QStringList("OVERWRITTEN");
+ QTest::newRow("property_append_to_outer") << QString("dummy.defines") << (QStringList() << QString("ONE") << QString("TWO"));
+
+ QTest::newRow("multiple_exclusive_properties") << QString("dummy.defines") << QStringList("OVERWRITTEN");
+ QTest::newRow("multiple_exclusive_properties_no_outer") << QString("dummy.defines") << QStringList("OVERWRITTEN");
+ QTest::newRow("multiple_exclusive_properties_append_to_outer") << QString("dummy.defines") << (QStringList() << QString("ONE") << QString("TWO"));
+ QTest::newRow("condition_refers_to_product_property")
+ << QString("dummy.defines") << QStringList("OVERWRITTEN");
+ QTest::newRow("condition_refers_to_project_property")
+ << QString("dummy.defines") << QStringList("OVERWRITTEN");
+
+ QTest::newRow("ambiguous_properties")
+ << QString("dummy.defines")
+ << (QStringList() << QString("ONE") << QString("TWO"));
+ QTest::newRow("inheritance_overwrite_in_subitem")
+ << QString("dummy.defines")
+ << (QStringList() << QString("OVERWRITTEN_IN_SUBITEM"));
+ QTest::newRow("inheritance_retain_base1")
+ << QString("dummy.defines")
+ << (QStringList() << QString("BASE") << QString("SUB"));
+ QTest::newRow("inheritance_retain_base2")
+ << QString("dummy.defines")
+ << (QStringList() << QString("BASE") << QString("SUB"));
+ QTest::newRow("inheritance_retain_base3")
+ << QString("dummy.defines")
+ << (QStringList() << QString("BASE") << QString("SUB"));
+ QTest::newRow("inheritance_condition_in_subitem1")
+ << QString("dummy.defines")
+ << (QStringList() << QString("SOMETHING") << QString("SUB"));
+ QTest::newRow("inheritance_condition_in_subitem2")
+ << QString("dummy.defines")
+ << (QStringList() << QString("SOMETHING"));
+ QTest::newRow("condition_references_id")
+ << QString("dummy.defines")
+ << (QStringList() << QString("OVERWRITTEN"));
+ QTest::newRow("cleanup") << QString() << QStringList();
+}
+
+void TestLanguage::propertiesBlocks()
+{
+ HANDLE_INIT_CLEANUP_DATATAGS("propertiesblocks.qbs");
+ QFETCH(QString, propertyName);
+ QFETCH(QStringList, expectedValues);
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ const QString productName = QString::fromLocal8Bit(QTest::currentDataTag());
+ ResolvedProductPtr product = products.value(productName);
+ QVERIFY(product);
+ QCOMPARE(product->name, productName);
+ QVariant v = productPropertyValue(product, propertyName);
+ QCOMPARE(v.toStringList(), expectedValues);
+}
+
+void TestLanguage::fileTags_data()
+{
+ QTest::addColumn<int>("numberOfGroups");
+ QTest::addColumn<QStringList>("expectedFileTags");
+
+ QTest::newRow("init") << 0 << QStringList();
+ QTest::newRow("filetagger_project_scope") << 1 << (QStringList() << "cpp");
+ QTest::newRow("filetagger_product_scope") << 1 << (QStringList() << "asm");
+ QTest::newRow("filetagger_static_pattern") << 1 << (QStringList() << "yellow");
+ QTest::newRow("unknown_file_tag") << 1 << (QStringList() << "unknown-file-tag");
+ QTest::newRow("set_file_tag_via_group") << 2 << (QStringList() << "c++");
+ QTest::newRow("override_file_tag_via_group") << 2 << (QStringList() << "c++");
+ QTest::newRow("add_file_tag_via_group") << 2 << (QStringList() << "cpp" << "zzz");
+ QTest::newRow("add_file_tag_via_group_and_file_ref") << 2 << (QStringList() << "cpp" << "zzz");
+ QTest::newRow("cleanup") << 0 << QStringList();
+}
+
+void TestLanguage::fileTags()
+{
+ HANDLE_INIT_CLEANUP_DATATAGS("filetags.qbs");
+ QFETCH(int, numberOfGroups);
+ QFETCH(QStringList, expectedFileTags);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ ResolvedProductPtr product;
+ const QString productName = QString::fromLocal8Bit(QTest::currentDataTag());
+ QVERIFY(product = products.value(productName));
+ QCOMPARE(product->groups.count(), numberOfGroups);
+ GroupPtr group = product->groups.last();
+ QVERIFY(group);
+ QCOMPARE(group->files.count(), 1);
+ SourceArtifactConstPtr sourceFile = group->files.first();
+ QStringList fileTags = sourceFile->fileTags.toStringList();
+ fileTags.sort();
+ QCOMPARE(fileTags, expectedFileTags);
+}
+
+void TestLanguage::wildcards_data()
+{
+ QTest::addColumn<bool>("useGroup");
+ QTest::addColumn<QStringList>("filesToCreate");
+ QTest::addColumn<QString>("projectFileSubDir");
+ QTest::addColumn<QString>("prefix");
+ QTest::addColumn<QStringList>("patterns");
+ QTest::addColumn<QStringList>("excludePatterns");
+ QTest::addColumn<QStringList>("expected");
+
+ const bool useGroup = true;
+ for (int i = 0; i <= 1; ++i) {
+ const bool useGroup = i;
+ const QByteArray dataTagSuffix = useGroup ? " group" : " nogroup";
+ QTest::newRow(QByteArray("simple 1") + dataTagSuffix)
+ << useGroup
+ << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << "*.h")
+ << QStringList()
+ << (QStringList() << "foo.h" << "bar.h");
+ QTest::newRow(QByteArray("simple 2") + dataTagSuffix)
+ << useGroup
+ << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << "foo.*")
+ << QStringList()
+ << (QStringList() << "foo.h" << "foo.cpp");
+ QTest::newRow(QByteArray("simple 3") + dataTagSuffix)
+ << useGroup
+ << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << "*.h" << "*.cpp")
+ << QStringList()
+ << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp");
+ QTest::newRow(QByteArray("exclude 1") + dataTagSuffix)
+ << useGroup
+ << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << "*.h" << "*.cpp")
+ << (QStringList() << "bar*")
+ << (QStringList() << "foo.h" << "foo.cpp");
+ QTest::newRow(QByteArray("exclude 2") + dataTagSuffix)
+ << useGroup
+ << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << "*")
+ << (QStringList() << "*.qbs")
+ << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp");
+ QTest::newRow(QByteArray("non-recursive") + dataTagSuffix)
+ << useGroup
+ << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << "a/*")
+ << QStringList()
+ << (QStringList() << "a/foo.h" << "a/foo.cpp");
+ QTest::newRow(QByteArray("absolute paths") + dataTagSuffix)
+ << useGroup
+ << (QStringList() << "foo.h" << "foo.cpp" << "bar.h" << "bar.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << m_wildcardsTestDirPath + "/?oo.*")
+ << QStringList()
+ << (QStringList() << "foo.h" << "foo.cpp");
+ QTest::newRow(QByteArray("relative paths with dotdot") + dataTagSuffix)
+ << useGroup
+ << (QStringList() << "bar.h" << "bar.cpp")
+ << QString("TheLaughingLlama")
+ << QString()
+ << (QStringList() << "../bar.*")
+ << QStringList()
+ << (QStringList() << "bar.h" << "bar.cpp");
+ }
+ QTest::newRow(QByteArray("recursive 1"))
+ << useGroup
+ << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << "a/**")
+ << QStringList()
+ << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp");
+ QTest::newRow(QByteArray("recursive 2"))
+ << useGroup
+ << (QStringList()
+ << "d/1.h" << "b/d/1.h" << "b/c/d/1.h"
+ << "d/e/1.h" << "b/d/e/1.h" << "b/c/d/e/1.h"
+ << "a/d/1.h" << "a/b/d/1.h" << "a/b/c/d/1.h"
+ << "a/d/e/1.h" << "a/b/d/e/1.h" << "a/b/c/d/e/1.h"
+ << "a/d/1.cpp" << "a/b/d/1.cpp" << "a/b/c/d/1.h"
+ << "a/d/e/1.cpp" << "a/b/d/e/1.cpp" << "a/b/c/d/e/1.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << "a/**/d/*.h")
+ << QStringList()
+ << (QStringList() << "a/d/1.h" << "a/b/d/1.h" << "a/b/c/d/1.h");
+ QTest::newRow(QByteArray("recursive 3"))
+ << useGroup
+ << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp")
+ << QString()
+ << QString()
+ << (QStringList() << "a/**/**/**")
+ << QStringList()
+ << (QStringList() << "a/foo.h" << "a/foo.cpp" << "a/b/bar.h" << "a/b/bar.cpp");
+ QTest::newRow(QByteArray("prefix"))
+ << useGroup
+ << (QStringList() << "subdir/foo.h" << "subdir/foo.cpp" << "subdir/bar.h"
+ << "subdir/bar.cpp")
+ << QString()
+ << QString("subdir/")
+ << (QStringList() << "*.h")
+ << QStringList()
+ << (QStringList() << "subdir/foo.h" << "subdir/bar.h");
+}
+
+void TestLanguage::wildcards()
+{
+ QFETCH(bool, useGroup);
+ QFETCH(QStringList, filesToCreate);
+ QFETCH(QString, projectFileSubDir);
+ QFETCH(QString, prefix);
+ QFETCH(QStringList, patterns);
+ QFETCH(QStringList, excludePatterns);
+ QFETCH(QStringList, expected);
+
+ // create test directory
+ QDir::setCurrent(QDir::tempPath());
+ {
+ QString errorMessage;
+ if (QFile::exists(m_wildcardsTestDirPath)) {
+ if (!removeDirectoryWithContents(m_wildcardsTestDirPath, &errorMessage)) {
+ qDebug() << errorMessage;
+ QVERIFY2(false, "removeDirectoryWithContents failed");
+ }
+ }
+ QVERIFY(QDir().mkdir(m_wildcardsTestDirPath));
+ }
+
+ // create project file
+ const QString groupName = "Keks";
+ QString dataTag = QString::fromLocal8Bit(QTest::currentDataTag());
+ dataTag.replace(' ', '_');
+ if (!projectFileSubDir.isEmpty()) {
+ if (!projectFileSubDir.startsWith('/'))
+ projectFileSubDir.prepend('/');
+ if (projectFileSubDir.endsWith('/'))
+ projectFileSubDir.chop(1);
+ QVERIFY(QDir().mkpath(m_wildcardsTestDirPath + projectFileSubDir));
+ }
+ const QString projectFilePath = m_wildcardsTestDirPath + projectFileSubDir + "/test_" + dataTag
+ + ".qbs";
+ {
+ QFile projectFile(projectFilePath);
+ QVERIFY(projectFile.open(QIODevice::WriteOnly));
+ QTextStream s(&projectFile);
+ s << "import qbs.base 1.0" << endl << endl
+ << "Application {" << endl
+ << " name: \"MyProduct\"" << endl;
+ if (useGroup) {
+ s << " Group {" << endl
+ << " name: " << toJSLiteral(groupName) << endl;
+ }
+ if (!prefix.isEmpty())
+ s << " prefix: " << toJSLiteral(prefix) << endl;
+ if (!patterns.isEmpty())
+ s << " files: " << toJSLiteral(patterns) << endl;
+ if (!excludePatterns.isEmpty())
+ s << " excludeFiles: " << toJSLiteral(excludePatterns) << endl;
+ if (useGroup)
+ s << " }" << endl;
+ s << "}" << endl << endl;
+ }
+
+ // create files
+ foreach (QString filePath, filesToCreate) {
+ filePath.prepend(m_wildcardsTestDirPath + '/');
+ QFileInfo fi(filePath);
+ if (!QDir(fi.path()).exists())
+ QVERIFY(QDir().mkpath(fi.path()));
+ QFile file(filePath);
+ QVERIFY(file.open(QIODevice::WriteOnly));
+ }
+
+ // read the project
+ bool exceptionCaught = false;
+ ResolvedProductPtr product;
+ try {
+ defaultParameters.setProjectFilePath(projectFilePath);
+ project = loader->loadProject(defaultParameters);
+ QVERIFY(project);
+ const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ product = products.value("MyProduct");
+ QVERIFY(product);
+ GroupPtr group;
+ if (useGroup) {
+ QCOMPARE(product->groups.count(), 2);
+ foreach (const GroupPtr &rg, product->groups) {
+ if (rg->name == groupName) {
+ group = rg;
+ break;
+ }
+ }
+ } else {
+ QCOMPARE(product->groups.count(), 1);
+ group = product->groups.first();
+ }
+ QVERIFY(group);
+ QCOMPARE(group->files.count(), 0);
+ SourceWildCards::Ptr wildcards = group->wildcards;
+ QVERIFY(wildcards);
+ QStringList actualFilePaths;
+ foreach (const SourceArtifactConstPtr &artifact, wildcards->files) {
+ QString str = artifact->absoluteFilePath;
+ int idx = str.indexOf(m_wildcardsTestDirPath);
+ if (idx != -1)
+ str.remove(0, idx + m_wildcardsTestDirPath.count() + 1);
+ actualFilePaths << str;
+ }
+ actualFilePaths.sort();
+ expected.sort();
+ QCOMPARE(actualFilePaths, expected);
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/tst_language.h b/src/lib/corelib/language/tst_language.h
new file mode 100644
index 000000000..7e3ce5df5
--- /dev/null
+++ b/src/lib/corelib/language/tst_language.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef TST_LANGUAGE_H
+#define TST_LANGUAGE_H
+
+#include <language/forward_decls.h>
+#include <language/loader.h>
+#include <logging/ilogsink.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/qbs_export.h>
+#include <QtTest>
+
+namespace qbs {
+namespace Internal {
+
+class QBS_EXPORT TestLanguage : public QObject
+{
+ Q_OBJECT
+public:
+ TestLanguage(ILogSink *logSink);
+ ~TestLanguage();
+
+private:
+ ILogSink *m_logSink;
+ Logger m_logger;
+ ScriptEngine *m_engine;
+ Loader *loader;
+ TopLevelProjectPtr project;
+ SetupProjectParameters defaultParameters;
+ const QString m_wildcardsTestDirPath;
+
+ QHash<QString, ResolvedProductPtr> productsFromProject(ResolvedProjectPtr project);
+ ResolvedModuleConstPtr findModuleByName(ResolvedProductPtr product, const QString &name);
+ QVariant productPropertyValue(ResolvedProductPtr product, QString propertyName);
+ void handleInitCleanupDataTags(const char *projectFileName, bool *handled);
+ QString buildDir(const SetupProjectParameters &params) const;
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void baseProperty();
+ void buildConfigStringListSyntax();
+ void builtinFunctionInSearchPathsProperty();
+ void canonicalArchitecture();
+ void conditionalDepends();
+ void environmentVariable();
+ void erroneousFiles_data();
+ void erroneousFiles();
+ void exports();
+ void fileContextProperties();
+ void getNativeSetting();
+ void groupConditions_data();
+ void groupConditions();
+ void groupName();
+ void homeDirectory();
+ void identifierSearch_data();
+ void identifierSearch();
+ void idUsage();
+ void invalidBindingInDisabledItem();
+ void itemPrototype();
+ void itemScope();
+ void jsExtensions();
+ void jsImportUsedInMultipleScopes_data();
+ void jsImportUsedInMultipleScopes();
+ void moduleProperties_data();
+ void moduleProperties();
+ void moduleScope();
+ void modules_data();
+ void modules();
+ void outerInGroup();
+ void pathProperties();
+ void profileValuesAndOverriddenValues();
+ void productConditions();
+ void productDirectories();
+ void propertiesBlocks_data();
+ void propertiesBlocks();
+ void fileTags_data();
+ void fileTags();
+ void wildcards_data();
+ void wildcards();
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // TST_LANGUAGE_H
diff --git a/src/lib/corelib/language/value.cpp b/src/lib/corelib/language/value.cpp
new file mode 100644
index 000000000..e9b755f2e
--- /dev/null
+++ b/src/lib/corelib/language/value.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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 "value.h"
+#include "item.h"
+
+namespace qbs {
+namespace Internal {
+
+Value::Value(Type t)
+ : m_type(t)
+{
+}
+
+Value::~Value()
+{
+}
+
+
+JSSourceValue::JSSourceValue()
+ : Value(JSSourceValueType)
+ , m_sourceUsesBase(false)
+ , m_sourceUsesOuter(false)
+ , m_hasFunctionForm(false)
+{
+}
+
+JSSourceValuePtr JSSourceValue::create()
+{
+ return JSSourceValuePtr(new JSSourceValue);
+}
+
+JSSourceValue::~JSSourceValue()
+{
+}
+
+
+ItemValue::ItemValue(Item *item)
+ : Value(ItemValueType)
+ , m_item(item)
+{
+}
+
+ItemValuePtr ItemValue::create(Item *item)
+{
+ return ItemValuePtr(new ItemValue(item));
+}
+
+ItemValue::~ItemValue()
+{
+}
+
+VariantValue::VariantValue(const QVariant &v)
+ : Value(VariantValueType)
+ , m_value(v)
+{
+}
+
+VariantValuePtr VariantValue::create(const QVariant &v)
+{
+ return VariantValuePtr(new VariantValue(v));
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/language/value.h b/src/lib/corelib/language/value.h
new file mode 100644
index 000000000..92313a98b
--- /dev/null
+++ b/src/lib/corelib/language/value.h
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_VALUE_H
+#define QBS_VALUE_H
+
+#include "filecontext.h"
+#include "item.h"
+#include <tools/codelocation.h>
+#include <QVariant>
+
+namespace qbs {
+namespace Internal {
+
+class ValueHandler;
+
+class Value
+{
+public:
+ enum Type
+ {
+ JSSourceValueType,
+ ItemValueType,
+ VariantValueType,
+ BuiltinValueType
+ };
+
+ Value(Type t);
+ virtual ~Value();
+
+ Type type() const { return m_type; }
+ virtual void apply(ValueHandler *) = 0;
+ virtual CodeLocation location() const { return CodeLocation(); }
+
+private:
+ Type m_type;
+};
+
+class ValueHandler
+{
+public:
+ virtual void handle(JSSourceValue *value) = 0;
+ virtual void handle(ItemValue *value) = 0;
+ virtual void handle(VariantValue *value) = 0;
+ virtual void handle(BuiltinValue *value) = 0;
+};
+
+class JSSourceValue : public Value
+{
+ friend class ItemReaderASTVisitor;
+ JSSourceValue();
+public:
+ static JSSourceValuePtr create();
+ ~JSSourceValue();
+
+ void apply(ValueHandler *handler) { handler->handle(this); }
+
+ void setSourceCode(const QString &sourceCode) { m_sourceCode = sourceCode; }
+ const QString &sourceCode() const { return m_sourceCode; }
+
+ void setLocation(const CodeLocation &location) { m_location = location; }
+ CodeLocation location() const { return m_location; }
+
+ void setFile(const FileContextPtr &file) { m_file = file; }
+ const FileContextPtr &file() const { return m_file; }
+
+ bool sourceUsesBase() const { return m_sourceUsesBase; }
+ bool sourceUsesOuter() const { return m_sourceUsesOuter; }
+ bool hasFunctionForm() const { return m_hasFunctionForm; }
+
+ const JSSourceValuePtr &baseValue() const { return m_baseValue; }
+ void setBaseValue(const JSSourceValuePtr &v) { m_baseValue = v; }
+
+ struct Alternative
+ {
+ QString condition;
+ const Item *conditionScopeItem;
+ JSSourceValuePtr value;
+ };
+
+ const QList<Alternative> &alternatives() const { return m_alternatives; }
+ void setAlternatives(const QList<Alternative> &alternatives) { m_alternatives = alternatives; }
+ void addAlternative(const Alternative &alternative) { m_alternatives.append(alternative); }
+
+private:
+ QString m_sourceCode;
+ CodeLocation m_location;
+ FileContextPtr m_file;
+ bool m_sourceUsesBase;
+ bool m_sourceUsesOuter;
+ bool m_hasFunctionForm;
+ JSSourceValuePtr m_baseValue;
+ QList<Alternative> m_alternatives;
+};
+
+class Item;
+
+class ItemValue : public Value
+{
+ ItemValue(Item *item);
+public:
+ static ItemValuePtr create(Item *item = 0);
+ ~ItemValue();
+
+ void apply(ValueHandler *handler) { handler->handle(this); }
+ Item *item() const;
+ void setItem(Item *ptr);
+
+private:
+ Item *m_item;
+};
+
+inline Item *ItemValue::item() const
+{
+ return m_item;
+}
+
+inline void ItemValue::setItem(Item *ptr)
+{
+ m_item = ptr;
+}
+
+
+class VariantValue : public Value
+{
+ VariantValue(const QVariant &v);
+public:
+ static VariantValuePtr create(const QVariant &v = QVariant());
+
+ void apply(ValueHandler *handler) { handler->handle(this); }
+
+ void setValue(const QVariant &v) { m_value = v; }
+ const QVariant &value() const { return m_value; }
+
+private:
+ QVariant m_value;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_VALUE_H
diff --git a/src/lib/corelib/logging/ilogsink.cpp b/src/lib/corelib/logging/ilogsink.cpp
new file mode 100644
index 000000000..118fdf106
--- /dev/null
+++ b/src/lib/corelib/logging/ilogsink.cpp
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** 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 "ilogsink.h"
+
+#include <tools/error.h>
+
+#include <QByteArray>
+#include <QMutex>
+
+namespace qbs {
+
+QString logLevelTag(LoggerLevel level)
+{
+ if (level == LoggerInfo)
+ return QByteArray();
+ QString str = logLevelName(level).toUpper();
+ if (!str.isEmpty())
+ str.append(QLatin1String(": "));
+ return str;
+}
+
+QString logLevelName(LoggerLevel level)
+{
+ switch (level) {
+ case qbs::LoggerError:
+ return QLatin1String("error");
+ case qbs::LoggerWarning:
+ return QLatin1String("warning");
+ case qbs::LoggerInfo:
+ return QLatin1String("info");
+ case qbs::LoggerDebug:
+ return QLatin1String("debug");
+ case qbs::LoggerTrace:
+ return QLatin1String("trace");
+ default:
+ break;
+ }
+ return QString();
+}
+
+class ILogSink::ILogSinkPrivate
+{
+public:
+ LoggerLevel logLevel;
+ QMutex mutex;
+};
+
+ILogSink::ILogSink() : d(new ILogSinkPrivate)
+{
+ d->logLevel = defaultLogLevel();
+}
+
+ILogSink::~ILogSink()
+{
+ delete d;
+}
+
+void ILogSink::setLogLevel(LoggerLevel level)
+{
+ d->logLevel = level;
+}
+
+LoggerLevel ILogSink::logLevel() const
+{
+ return d->logLevel;
+}
+
+void ILogSink::printWarning(const ErrorInfo &warning)
+{
+ if (willPrint(LoggerWarning)) {
+ d->mutex.lock();
+ doPrintWarning(warning);
+ d->mutex.unlock();
+ }
+}
+
+void ILogSink::printMessage(LoggerLevel level, const QString &message, const QString &tag,
+ bool force)
+{
+ if (force || willPrint(level)) {
+ d->mutex.lock();
+ doPrintMessage(level, message, tag);
+ d->mutex.unlock();
+ }
+}
+
+void ILogSink::doPrintWarning(const ErrorInfo &warning)
+{
+ doPrintMessage(LoggerWarning, warning.toString(), QString());
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/logging/ilogsink.h b/src/lib/corelib/logging/ilogsink.h
new file mode 100644
index 000000000..9c263b52d
--- /dev/null
+++ b/src/lib/corelib/logging/ilogsink.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_ILOGSINK_H
+#define QBS_ILOGSINK_H
+
+#include "../tools/qbs_export.h"
+
+#include <QString>
+
+namespace qbs {
+class ErrorInfo;
+
+enum LoggerLevel
+{
+ LoggerMinLevel,
+ LoggerError = LoggerMinLevel,
+ LoggerWarning,
+ LoggerInfo,
+ LoggerDebug,
+ LoggerTrace,
+ LoggerMaxLevel = LoggerTrace
+};
+
+inline LoggerLevel defaultLogLevel() { return LoggerInfo; }
+QBS_EXPORT QString logLevelTag(LoggerLevel level);
+QBS_EXPORT QString logLevelName(LoggerLevel level);
+
+class QBS_EXPORT ILogSink
+{
+ Q_DISABLE_COPY(ILogSink)
+public:
+ ILogSink();
+ virtual ~ILogSink();
+
+ void setLogLevel(LoggerLevel level);
+ LoggerLevel logLevel() const;
+
+ bool willPrint(LoggerLevel level) const { return level <= logLevel(); }
+
+ void printWarning(const ErrorInfo &warning);
+ void printMessage(LoggerLevel level, const QString &message,
+ const QString &tag = QString(), bool force = false);
+
+private:
+ virtual void doPrintWarning(const ErrorInfo &warning);
+ virtual void doPrintMessage(LoggerLevel level, const QString &message,
+ const QString &tag) = 0;
+
+ class ILogSinkPrivate;
+ ILogSinkPrivate * const d;
+};
+
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/logging/logger.cpp b/src/lib/corelib/logging/logger.cpp
new file mode 100644
index 000000000..9f7a19c01
--- /dev/null
+++ b/src/lib/corelib/logging/logger.cpp
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#if defined(_MSC_VER) && _MSC_VER > 0
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include "logger.h"
+
+#include <QByteArray>
+#include <QElapsedTimer>
+#include <QMutex>
+#include <QSet>
+#include <QVariant>
+
+#include <cstdarg>
+#include <stdio.h>
+
+namespace qbs {
+namespace Internal {
+
+LogWriter::LogWriter(ILogSink *logSink, LoggerLevel level, bool force)
+ : m_logSink(logSink), m_level(level), m_force(force)
+{}
+
+LogWriter::LogWriter(const LogWriter &other)
+ : m_logSink(other.m_logSink)
+ , m_level(other.m_level)
+ , m_message(other.m_message)
+ , m_tag(other.m_tag)
+ , m_force(other.m_force)
+{
+ other.m_message.clear();
+}
+
+LogWriter::~LogWriter()
+{
+ if (!m_message.isEmpty())
+ m_logSink->printMessage(m_level, m_message, m_tag, m_force);
+}
+
+const LogWriter &LogWriter::operator=(const LogWriter &other)
+{
+ m_logSink = other.m_logSink;
+ m_level = other.m_level;
+ m_message = other.m_message;
+ m_tag = other.m_tag;
+ m_force = other.m_force;
+ other.m_message.clear();
+ return *this;
+}
+
+void LogWriter::write(char c)
+{
+ write(QLatin1Char(c));
+}
+
+void LogWriter::write(const char *str)
+{
+ write(QLatin1String(str));
+}
+
+void LogWriter::write(const QChar &c)
+{
+ if (m_force || m_logSink->logLevel() >= m_level)
+ m_message.append(c);
+}
+
+void LogWriter::write(const QString &message)
+{
+ if (m_force || m_logSink->logLevel() >= m_level)
+ m_message += message;
+}
+
+void LogWriter::setMessageTag(const QString &tag)
+{
+ m_tag = tag;
+}
+
+LogWriter operator<<(LogWriter w, const char *str)
+{
+ w.write(str);
+ return w;
+}
+
+LogWriter operator<<(LogWriter w, const QByteArray &byteArray)
+{
+ w.write(byteArray.data());
+ return w;
+}
+
+LogWriter operator<<(LogWriter w, const QString &str)
+{
+ w.write(str);
+ return w;
+}
+
+LogWriter operator<<(LogWriter w, const QStringList &strList)
+{
+ w.write('[');
+ for (int i = 0; i < strList.size(); ++i) {
+ w.write(strList.at(i));
+ if (i != strList.size() - 1)
+ w.write(QLatin1String(", "));
+ }
+ w.write(']');
+ return w;
+}
+
+LogWriter operator<<(LogWriter w, const QSet<QString> &strSet)
+{
+ bool firstLoop = true;
+ w.write('(');
+ foreach (const QString &str, strSet) {
+ if (firstLoop)
+ firstLoop = false;
+ else
+ w.write(QLatin1String(", "));
+ w.write(str);
+ }
+ w.write(')');
+ return w;
+}
+
+LogWriter operator<<(LogWriter w, const QVariant &variant)
+{
+ QString str = variant.typeName() + QLatin1Char('(');
+ if (variant.type() == QVariant::List) {
+ bool firstLoop = true;
+ foreach (const QVariant &item, variant.toList()) {
+ str += item.toString();
+ if (firstLoop)
+ firstLoop = false;
+ else
+ str += QLatin1String(", ");
+ }
+ } else {
+ str += variant.toString();
+ }
+ str += QLatin1Char(')');
+ w.write(str);
+ return w;
+}
+
+LogWriter operator<<(LogWriter w, int n)
+{
+ return w << QString::number(n);
+}
+
+LogWriter operator<<(LogWriter w, qint64 n)
+{
+ return w << QString::number(n);
+}
+
+LogWriter operator<<(LogWriter w, bool b)
+{
+ return w << QString(QLatin1String(b ? "true" : "false"));
+}
+
+LogWriter operator<<(LogWriter w, const MessageTag &tag)
+{
+ w.setMessageTag(tag.tag());
+ return w;
+}
+
+Logger::Logger(ILogSink *logger) : m_logSink(logger)
+{
+}
+
+bool Logger::debugEnabled() const
+{
+ return m_logSink->willPrint(LoggerDebug);
+}
+
+bool Logger::traceEnabled() const
+{
+ return m_logSink->willPrint(LoggerTrace);
+}
+
+LogWriter Logger::qbsLog(LoggerLevel level, bool force) const
+{
+ return LogWriter(m_logSink, level, force);
+}
+
+
+class TimedActivityLogger::TimedActivityLoggerPrivate
+{
+public:
+ Logger logger;
+ QString prefix;
+ QString activity;
+ LoggerLevel logLevel;
+ QElapsedTimer timer;
+ bool alwaysLog;
+};
+
+TimedActivityLogger::TimedActivityLogger(const Logger &logger, const QString &activity,
+ const QString &prefix, LoggerLevel logLevel, bool alwaysLog)
+ : d(0)
+{
+ if (!alwaysLog && !logger.logSink()->willPrint(logLevel))
+ return;
+ d = new TimedActivityLoggerPrivate;
+ d->logger = logger;
+ d->prefix = prefix;
+ d->activity = activity;
+ d->logLevel = logLevel;
+ d->alwaysLog = alwaysLog;
+ d->logger.qbsLog(logLevel, alwaysLog) << QString::fromLocal8Bit("%1Starting activity '%2'.")
+ .arg(prefix, activity);
+ d->timer.start();
+}
+
+void TimedActivityLogger::finishActivity()
+{
+ if (!d)
+ return;
+ qint64 ms = d->timer.elapsed();
+ qint64 s = ms/1000;
+ ms -= s*1000;
+ qint64 m = s/60;
+ s -= m*60;
+ const qint64 h = m/60;
+ m -= h*60;
+ QString timeString = QString::fromLocal8Bit("%1ms").arg(ms);
+ if (h || m || s)
+ timeString.prepend(QString::fromLocal8Bit("%1s, ").arg(s));
+ if (h || m)
+ timeString.prepend(QString::fromLocal8Bit("%1m, ").arg(m));
+ if (h)
+ timeString.prepend(QString::fromLocal8Bit("%1h, ").arg(h));
+ d->logger.qbsLog(d->logLevel, d->alwaysLog)
+ << QString::fromLocal8Bit("%1Activity '%2' took %3.")
+ .arg(d->prefix, d->activity, timeString);
+ delete d;
+ d = 0;
+}
+
+TimedActivityLogger::~TimedActivityLogger()
+{
+ finishActivity();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/logging/logger.h b/src/lib/corelib/logging/logger.h
new file mode 100644
index 000000000..1ab11144d
--- /dev/null
+++ b/src/lib/corelib/logging/logger.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_LOGGER_H
+#define QBS_LOGGER_H
+
+#include "ilogsink.h"
+
+#include <QByteArray>
+#include <QString>
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+class QVariant;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+// Note that while these classes are not part of the API, we export some stuff for use by
+// our command line tools for the sake of a uniform logging approach.
+
+class QBS_EXPORT LogWriter
+{
+public:
+ LogWriter(ILogSink *logSink, LoggerLevel level, bool force = false);
+
+ // log writer has move semantics and the last instance of
+ // a << chain prints the accumulated data
+ LogWriter(const LogWriter &other);
+ ~LogWriter();
+ const LogWriter &operator=(const LogWriter &other);
+
+ void write(char c);
+ void write(const char *str);
+ void write(const QChar &c);
+ void write(const QString &message);
+
+ void setMessageTag(const QString &tag);
+
+private:
+ ILogSink *m_logSink;
+ LoggerLevel m_level;
+ mutable QString m_message;
+ QString m_tag;
+ bool m_force;
+};
+
+class QBS_EXPORT MessageTag
+{
+public:
+ explicit MessageTag(const QString &tag) : m_tag(tag) {}
+
+ const QString &tag() const { return m_tag; }
+
+private:
+ QString m_tag;
+};
+
+QBS_EXPORT LogWriter operator<<(LogWriter w, const char *str);
+QBS_EXPORT LogWriter operator<<(LogWriter w, const QByteArray &byteArray);
+QBS_EXPORT LogWriter operator<<(LogWriter w, const QString &str);
+QBS_EXPORT LogWriter operator<<(LogWriter w, const QStringList &strList);
+QBS_EXPORT LogWriter operator<<(LogWriter w, const QSet<QString> &strSet);
+QBS_EXPORT LogWriter operator<<(LogWriter w, const QVariant &variant);
+QBS_EXPORT LogWriter operator<<(LogWriter w, int n);
+QBS_EXPORT LogWriter operator<<(LogWriter w, qint64 n);
+QBS_EXPORT LogWriter operator<<(LogWriter w, bool b);
+QBS_EXPORT LogWriter operator<<(LogWriter w, const MessageTag &tag);
+
+class QBS_EXPORT Logger
+{
+public:
+ Logger(ILogSink *logSink = 0);
+
+ ILogSink *logSink() const { return m_logSink; }
+
+ bool debugEnabled() const;
+ bool traceEnabled() const;
+
+ void printWarning(const ErrorInfo &warning) { logSink()->printWarning(warning); }
+
+ LogWriter qbsLog(LoggerLevel level, bool force = false) const;
+ LogWriter qbsWarning() const { return qbsLog(LoggerWarning); }
+ LogWriter qbsInfo() const { return qbsLog(LoggerInfo); }
+ LogWriter qbsDebug() const { return qbsLog(LoggerDebug); }
+ LogWriter qbsTrace() const { return qbsLog(LoggerTrace); }
+
+private:
+ ILogSink *m_logSink;
+};
+
+
+class TimedActivityLogger
+{
+public:
+ TimedActivityLogger(const Logger &logger, const QString &activity,
+ const QString &prefix = QString(), LoggerLevel logLevel = LoggerDebug,
+ bool alwaysLog = false);
+ void finishActivity();
+ ~TimedActivityLogger();
+
+private:
+ class TimedActivityLoggerPrivate;
+ TimedActivityLoggerPrivate *d;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_LOGGER_H
diff --git a/src/lib/corelib/logging/logging.pri b/src/lib/corelib/logging/logging.pri
new file mode 100644
index 000000000..8c0367779
--- /dev/null
+++ b/src/lib/corelib/logging/logging.pri
@@ -0,0 +1,14 @@
+HEADERS += \
+ $$PWD/logger.h \
+ $$PWD/translator.h \
+ $$PWD/ilogsink.h
+
+SOURCES += \
+ $$PWD/logger.cpp \
+ $$PWD/ilogsink.cpp
+
+!qbs_no_dev_install {
+ logging_headers.files = $$PWD/ilogsink.h
+ logging_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/logging
+ INSTALLS += logging_headers
+}
diff --git a/src/lib/corelib/logging/translator.h b/src/lib/corelib/logging/translator.h
new file mode 100644
index 000000000..5c1f5304b
--- /dev/null
+++ b/src/lib/corelib/logging/translator.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_TRANSLATOR_H
+#define QBS_TRANSLATOR_H
+
+#include <tools/qbs_export.h>
+
+#include <QCoreApplication>
+
+namespace qbs {
+namespace Internal {
+
+class QBS_EXPORT Tr // Name intended to be short. Exported for use by command line tools.
+{
+ Q_DECLARE_TR_FUNCTIONS(Qbs)
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_TRANSLATOR_H
diff --git a/src/lib/corelib/parser/parser.pri b/src/lib/corelib/parser/parser.pri
new file mode 100644
index 000000000..e6a8a5345
--- /dev/null
+++ b/src/lib/corelib/parser/parser.pri
@@ -0,0 +1,21 @@
+HEADERS += \
+ $$PWD/qmljsast_p.h \
+ $$PWD/qmljsastfwd_p.h \
+ $$PWD/qmljsastvisitor_p.h \
+ $$PWD/qmljsengine_p.h \
+ $$PWD/qmljsgrammar_p.h \
+ $$PWD/qmljslexer_p.h \
+ $$PWD/qmljsmemorypool_p.h \
+ $$PWD/qmljsparser_p.h \
+ $$PWD/qmljsglobal_p.h \
+ $$PWD/qmlerror.h \
+ $$PWD/qmljskeywords_p.h \
+
+SOURCES += \
+ $$PWD/qmljsast.cpp \
+ $$PWD/qmljsastvisitor.cpp \
+ $$PWD/qmljsengine_p.cpp \
+ $$PWD/qmljsgrammar.cpp \
+ $$PWD/qmljslexer.cpp \
+ $$PWD/qmljsparser.cpp \
+ $$PWD/qmlerror.cpp \
diff --git a/src/lib/corelib/parser/qmlerror.cpp b/src/lib/corelib/parser/qmlerror.cpp
new file mode 100644
index 000000000..b94fd1556
--- /dev/null
+++ b/src/lib/corelib/parser/qmlerror.cpp
@@ -0,0 +1,282 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 "qmlerror.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qstringlist.h>
+
+namespace QbsQmlJS {
+
+/*!
+ \class QmlError
+ \since 5.0
+ \inmodule QtQml
+ \brief The QmlError class encapsulates a QML error.
+
+ QmlError includes a textual description of the error, as well
+ as location information (the file, line, and column). The toString()
+ method creates a single-line, human-readable string containing all of
+ this information, for example:
+ \code
+ file:///home/user/test.qml:7:8: Invalid property assignment: double expected
+ \endcode
+
+ You can use qDebug() or qWarning() to output errors to the console. This method
+ will attempt to open the file indicated by the error
+ and include additional contextual information.
+ \code
+ file:///home/user/test.qml:7:8: Invalid property assignment: double expected
+ y: "hello"
+ ^
+ \endcode
+
+ Note that the QtQuick 1 version is named QDeclarativeError
+
+ \sa QQuickView::errors(), QmlComponent::errors()
+*/
+class QmlErrorPrivate
+{
+public:
+ QmlErrorPrivate();
+
+ QUrl url;
+ QString description;
+ int line;
+ int column;
+};
+
+QmlErrorPrivate::QmlErrorPrivate()
+: line(-1), column(-1)
+{
+}
+
+/*!
+ Creates an empty error object.
+*/
+QmlError::QmlError()
+: d(0)
+{
+}
+
+/*!
+ Creates a copy of \a other.
+*/
+QmlError::QmlError(const QmlError &other)
+: d(0)
+{
+ *this = other;
+}
+
+/*!
+ Assigns \a other to this error object.
+*/
+QmlError &QmlError::operator=(const QmlError &other)
+{
+ if (!other.d) {
+ delete d;
+ d = 0;
+ } else {
+ if (!d) d = new QmlErrorPrivate;
+ d->url = other.d->url;
+ d->description = other.d->description;
+ d->line = other.d->line;
+ d->column = other.d->column;
+ }
+ return *this;
+}
+
+/*!
+ \internal
+*/
+QmlError::~QmlError()
+{
+ delete d; d = 0;
+}
+
+/*!
+ Returns true if this error is valid, otherwise false.
+*/
+bool QmlError::isValid() const
+{
+ return d != 0;
+}
+
+/*!
+ Returns the url for the file that caused this error.
+*/
+QUrl QmlError::url() const
+{
+ if (d) return d->url;
+ else return QUrl();
+}
+
+/*!
+ Sets the \a url for the file that caused this error.
+*/
+void QmlError::setUrl(const QUrl &url)
+{
+ if (!d) d = new QmlErrorPrivate;
+ d->url = url;
+}
+
+/*!
+ Returns the error description.
+*/
+QString QmlError::description() const
+{
+ if (d) return d->description;
+ else return QString();
+}
+
+/*!
+ Sets the error \a description.
+*/
+void QmlError::setDescription(const QString &description)
+{
+ if (!d) d = new QmlErrorPrivate;
+ d->description = description;
+}
+
+/*!
+ Returns the error line number.
+*/
+int QmlError::line() const
+{
+ if (d) return d->line;
+ else return -1;
+}
+
+/*!
+ Sets the error \a line number.
+*/
+void QmlError::setLine(int line)
+{
+ if (!d) d = new QmlErrorPrivate;
+ d->line = line;
+}
+
+/*!
+ Returns the error column number.
+*/
+int QmlError::column() const
+{
+ if (d) return d->column;
+ else return -1;
+}
+
+/*!
+ Sets the error \a column number.
+*/
+void QmlError::setColumn(int column)
+{
+ if (!d) d = new QmlErrorPrivate;
+ d->column = column;
+}
+
+/*!
+ Returns the error as a human readable string.
+*/
+QString QmlError::toString() const
+{
+ QString rv;
+ if (url().isEmpty()) {
+ rv = QLatin1String("<Unknown File>");
+ } else if (line() != -1) {
+ rv = url().toString() + QLatin1Char(':') + QString::number(line());
+ if(column() != -1)
+ rv += QLatin1Char(':') + QString::number(column());
+ } else {
+ rv = url().toString();
+ }
+
+ rv += QLatin1String(": ") + description();
+
+ return rv;
+}
+
+} // namespace QbsQmlJS
+
+QT_BEGIN_NAMESPACE
+
+using namespace QbsQmlJS;
+
+/*!
+ \relates QmlError
+ \fn QDebug operator<<(QDebug debug, const QmlError &error)
+
+ Outputs a human readable version of \a error to \a debug.
+*/
+
+QDebug operator<<(QDebug debug, const QmlError &error)
+{
+ debug << qPrintable(error.toString());
+
+ QUrl url = error.url();
+
+ if (error.line() > 0 && url.scheme() == QLatin1String("file")) {
+ QString file = url.toLocalFile();
+ QFile f(file);
+ if (f.open(QIODevice::ReadOnly)) {
+ QByteArray data = f.readAll();
+ QTextStream stream(data, QIODevice::ReadOnly);
+#ifndef QT_NO_TEXTCODEC
+ stream.setCodec("UTF-8");
+#endif
+ const QString code = stream.readAll();
+ const QStringList lines = code.split(QLatin1Char('\n'));
+
+ if (lines.count() >= error.line()) {
+ const QString &line = lines.at(error.line() - 1);
+ debug << "\n " << qPrintable(line);
+
+ if(error.column() > 0) {
+ int column = qMax(0, error.column() - 1);
+ column = qMin(column, line.length());
+
+ QByteArray ind;
+ ind.reserve(column);
+ for (int i = 0; i < column; ++i) {
+ const QChar ch = line.at(i);
+ if (ch.isSpace())
+ ind.append(ch.unicode());
+ else
+ ind.append(' ');
+ }
+ ind.append('^');
+ debug << "\n " << ind.constData();
+ }
+ }
+ }
+ }
+ return debug;
+}
+
+QT_END_NAMESPACE
diff --git a/src/lib/corelib/parser/qmlerror.h b/src/lib/corelib/parser/qmlerror.h
new file mode 100644
index 000000000..bcc82b483
--- /dev/null
+++ b/src/lib/corelib/parser/qmlerror.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QQMLERROR_H
+#define QQMLERROR_H
+
+
+
+#include <QtCore/qurl.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+QT_END_NAMESPACE
+
+namespace QbsQmlJS {
+
+class QmlErrorPrivate;
+class QmlError
+{
+public:
+ QmlError();
+ QmlError(const QmlError &);
+ QmlError &operator=(const QmlError &);
+ ~QmlError();
+
+ bool isValid() const;
+
+ QUrl url() const;
+ void setUrl(const QUrl &);
+ QString description() const;
+ void setDescription(const QString &);
+ int line() const;
+ void setLine(int);
+ int column() const;
+ void setColumn(int);
+
+ QString toString() const;
+private:
+ QmlErrorPrivate *d;
+};
+
+} // namespace QbsQmlJS
+
+QT_BEGIN_NAMESPACE
+QDebug operator<<(QDebug debug, const QbsQmlJS::QmlError &error);
+Q_DECLARE_TYPEINFO(QbsQmlJS::QmlError, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+#endif // QQMLERROR_H
diff --git a/src/lib/corelib/parser/qmljs.g b/src/lib/corelib/parser/qmljs.g
new file mode 100644
index 000000000..a15002857
--- /dev/null
+++ b/src/lib/corelib/parser/qmljs.g
@@ -0,0 +1,3002 @@
+-----------------------------------------------------------------------------
+--
+-- Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+-- Contact: http://www.qt-project.org/legal
+--
+-- This file is part of Qt Creator.
+--
+-- 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.
+--
+-----------------------------------------------------------------------------
+
+
+
+%parser QmlJSGrammar
+%decl qmljsparser_p.h
+%impl qdeclarativejsparser.cpp
+%expect 2
+%expect-rr 2
+
+%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&="
+%token T_BREAK "break" T_CASE "case" T_CATCH "catch"
+%token T_COLON ":" T_COMMA "," T_CONTINUE "continue"
+%token T_DEFAULT "default" T_DELETE "delete" T_DIVIDE_ "/"
+%token T_DIVIDE_EQ "/=" T_DO "do" T_DOT "."
+%token T_ELSE "else" T_EQ "=" T_EQ_EQ "=="
+%token T_EQ_EQ_EQ "===" T_FINALLY "finally" T_FOR "for"
+%token T_FUNCTION "function" T_GE ">=" T_GT ">"
+%token T_GT_GT ">>" T_GT_GT_EQ ">>=" T_GT_GT_GT ">>>"
+%token T_GT_GT_GT_EQ ">>>=" T_IDENTIFIER "identifier" T_IF "if"
+%token T_IN "in" T_INSTANCEOF "instanceof" T_LBRACE "{"
+%token T_LBRACKET "[" T_LE "<=" T_LPAREN "("
+%token T_LT "<" T_LT_LT "<<" T_LT_LT_EQ "<<="
+%token T_MINUS "-" T_MINUS_EQ "-=" T_MINUS_MINUS "--"
+%token T_NEW "new" T_NOT "!" T_NOT_EQ "!="
+%token T_NOT_EQ_EQ "!==" T_NUMERIC_LITERAL "numeric literal" T_OR "|"
+%token T_OR_EQ "|=" T_OR_OR "||" T_PLUS "+"
+%token T_PLUS_EQ "+=" T_PLUS_PLUS "++" T_QUESTION "?"
+%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%"
+%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")"
+%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*"
+%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal"
+%token T_PROPERTY "property" T_SIGNAL "signal" T_READONLY "readonly"
+%token T_SWITCH "switch" T_THIS "this" T_THROW "throw"
+%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof"
+%token T_VAR "var" T_VOID "void" T_WHILE "while"
+%token T_WITH "with" T_XOR "^" T_XOR_EQ "^="
+%token T_NULL "null" T_TRUE "true" T_FALSE "false"
+%token T_CONST "const"
+%token T_DEBUGGER "debugger"
+%token T_RESERVED_WORD "reserved word"
+%token T_MULTILINE_STRING_LITERAL "multiline string literal"
+%token T_COMMENT "comment"
+
+--- context keywords.
+%token T_PUBLIC "public"
+%token T_IMPORT "import"
+%token T_AS "as"
+%token T_ON "on"
+
+%token T_ERROR
+
+--- feed tokens
+%token T_FEED_UI_PROGRAM
+%token T_FEED_UI_OBJECT_MEMBER
+%token T_FEED_JS_STATEMENT
+%token T_FEED_JS_EXPRESSION
+%token T_FEED_JS_SOURCE_ELEMENT
+%token T_FEED_JS_PROGRAM
+
+%nonassoc SHIFT_THERE
+%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY
+%nonassoc REDUCE_HERE
+
+%start TopLevel
+
+/./****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 <QtCore/QDebug>
+#include <QtCore/QCoreApplication>
+
+#include <string.h>
+
+#include "qmljsengine_p.h"
+#include "qmljslexer_p.h"
+#include "qmljsast_p.h"
+#include "qmljsmemorypool_p.h"
+
+./
+
+/:/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+//
+// This file is automatically generated from qmljs.g.
+// Changes will be lost.
+//
+
+#ifndef QMLJSPARSER_P_H
+#define QMLJSPARSER_P_H
+
+#include "qmljsglobal_p.h"
+#include "qmljsgrammar_p.h"
+#include "qmljsast_p.h"
+#include "qmljsengine_p.h"
+
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+QT_QML_BEGIN_NAMESPACE
+
+namespace QmlJS {
+
+class Engine;
+
+class QML_PARSER_EXPORT Parser: protected $table
+{
+public:
+ union Value {
+ int ival;
+ double dval;
+ AST::ArgumentList *ArgumentList;
+ AST::CaseBlock *CaseBlock;
+ AST::CaseClause *CaseClause;
+ AST::CaseClauses *CaseClauses;
+ AST::Catch *Catch;
+ AST::DefaultClause *DefaultClause;
+ AST::ElementList *ElementList;
+ AST::Elision *Elision;
+ AST::ExpressionNode *Expression;
+ AST::Finally *Finally;
+ AST::FormalParameterList *FormalParameterList;
+ AST::FunctionBody *FunctionBody;
+ AST::FunctionDeclaration *FunctionDeclaration;
+ AST::Node *Node;
+ AST::PropertyName *PropertyName;
+ AST::PropertyNameAndValueList *PropertyNameAndValueList;
+ AST::SourceElement *SourceElement;
+ AST::SourceElements *SourceElements;
+ AST::Statement *Statement;
+ AST::StatementList *StatementList;
+ AST::Block *Block;
+ AST::VariableDeclaration *VariableDeclaration;
+ AST::VariableDeclarationList *VariableDeclarationList;
+
+ AST::UiProgram *UiProgram;
+ AST::UiImportList *UiImportList;
+ AST::UiImport *UiImport;
+ AST::UiParameterList *UiParameterList;
+ AST::UiPublicMember *UiPublicMember;
+ AST::UiObjectDefinition *UiObjectDefinition;
+ AST::UiObjectInitializer *UiObjectInitializer;
+ AST::UiObjectBinding *UiObjectBinding;
+ AST::UiScriptBinding *UiScriptBinding;
+ AST::UiArrayBinding *UiArrayBinding;
+ AST::UiObjectMember *UiObjectMember;
+ AST::UiObjectMemberList *UiObjectMemberList;
+ AST::UiArrayMemberList *UiArrayMemberList;
+ AST::UiQualifiedId *UiQualifiedId;
+ };
+
+public:
+ Parser(Engine *engine);
+ ~Parser();
+
+ // parse a UI program
+ bool parse() { return parse(T_FEED_UI_PROGRAM); }
+ bool parseStatement() { return parse(T_FEED_JS_STATEMENT); }
+ bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); }
+ bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); }
+ bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); }
+ bool parseProgram() { return parse(T_FEED_JS_PROGRAM); }
+
+ AST::UiProgram *ast() const
+ { return AST::cast<AST::UiProgram *>(program); }
+
+ AST::Statement *statement() const
+ {
+ if (! program)
+ return 0;
+
+ return program->statementCast();
+ }
+
+ AST::ExpressionNode *expression() const
+ {
+ if (! program)
+ return 0;
+
+ return program->expressionCast();
+ }
+
+ AST::UiObjectMember *uiObjectMember() const
+ {
+ if (! program)
+ return 0;
+
+ return program->uiObjectMemberCast();
+ }
+
+ AST::Node *rootNode() const
+ { return program; }
+
+ QList<DiagnosticMessage> diagnosticMessages() const
+ { return diagnostic_messages; }
+
+ inline DiagnosticMessage diagnosticMessage() const
+ {
+ foreach (const DiagnosticMessage &d, diagnostic_messages) {
+ if (d.kind != DiagnosticMessage::Warning)
+ return d;
+ }
+
+ return DiagnosticMessage();
+ }
+
+ inline QString errorMessage() const
+ { return diagnosticMessage().message; }
+
+ inline int errorLineNumber() const
+ { return diagnosticMessage().loc.startLine; }
+
+ inline int errorColumnNumber() const
+ { return diagnosticMessage().loc.startColumn; }
+
+protected:
+ bool parse(int startToken);
+
+ void reallocateStack();
+
+ inline Value &sym(int index)
+ { return sym_stack [tos + index - 1]; }
+
+ inline QStringRef &stringRef(int index)
+ { return string_stack [tos + index - 1]; }
+
+ inline AST::SourceLocation &loc(int index)
+ { return location_stack [tos + index - 1]; }
+
+ AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr);
+
+protected:
+ Engine *driver;
+ MemoryPool *pool;
+ int tos;
+ int stack_size;
+ Value *sym_stack;
+ int *state_stack;
+ AST::SourceLocation *location_stack;
+ QStringRef *string_stack;
+
+ AST::Node *program;
+
+ // error recovery
+ enum { TOKEN_BUFFER_SIZE = 3 };
+
+ struct SavedToken {
+ int token;
+ double dval;
+ AST::SourceLocation loc;
+ QStringRef spell;
+ };
+
+ double yylval;
+ QStringRef yytokenspell;
+ AST::SourceLocation yylloc;
+ AST::SourceLocation yyprevlloc;
+
+ SavedToken token_buffer[TOKEN_BUFFER_SIZE];
+ SavedToken *first_token;
+ SavedToken *last_token;
+
+ QList<DiagnosticMessage> diagnostic_messages;
+};
+
+} // end of namespace QmlJS
+
+
+:/
+
+
+/.
+
+#include "qmljsparser_p.h"
+#include <QVarLengthArray>
+
+//
+// This file is automatically generated from qmljs.g.
+// Changes will be lost.
+//
+
+using namespace QmlJS;
+
+QT_QML_BEGIN_NAMESPACE
+
+void Parser::reallocateStack()
+{
+ if (! stack_size)
+ stack_size = 128;
+ else
+ stack_size <<= 1;
+
+ sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value)));
+ state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int)));
+ location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation)));
+ string_stack = reinterpret_cast<QStringRef*> (realloc(string_stack, stack_size * sizeof(QStringRef)));
+}
+
+Parser::Parser(Engine *engine):
+ driver(engine),
+ pool(engine->pool()),
+ tos(0),
+ stack_size(0),
+ sym_stack(0),
+ state_stack(0),
+ location_stack(0),
+ string_stack(0),
+ first_token(0),
+ last_token(0)
+{
+}
+
+Parser::~Parser()
+{
+ if (stack_size) {
+ free(sym_stack);
+ free(state_stack);
+ free(location_stack);
+ free(string_stack);
+ }
+}
+
+static inline AST::SourceLocation location(Lexer *lexer)
+{
+ AST::SourceLocation loc;
+ loc.offset = lexer->tokenOffset();
+ loc.length = lexer->tokenLength();
+ loc.startLine = lexer->tokenStartLine();
+ loc.startColumn = lexer->tokenStartColumn();
+ return loc;
+}
+
+AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr)
+{
+ QVarLengthArray<QStringRef, 4> nameIds;
+ QVarLengthArray<AST::SourceLocation, 4> locations;
+
+ AST::ExpressionNode *it = expr;
+ while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) {
+ nameIds.append(m->name);
+ locations.append(m->identifierToken);
+ it = m->base;
+ }
+
+ if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) {
+ AST::UiQualifiedId *q = new (pool) AST::UiQualifiedId(idExpr->name);
+ q->identifierToken = idExpr->identifierToken;
+
+ AST::UiQualifiedId *currentId = q;
+ for (int i = nameIds.size() - 1; i != -1; --i) {
+ currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]);
+ currentId->identifierToken = locations[i];
+ }
+
+ return currentId->finish();
+ }
+
+ return 0;
+}
+
+bool Parser::parse(int startToken)
+{
+ Lexer *lexer = driver->lexer();
+ bool hadErrors = false;
+ int yytoken = -1;
+ int action = 0;
+
+ token_buffer[0].token = startToken;
+ first_token = &token_buffer[0];
+ last_token = &token_buffer[1];
+
+ tos = -1;
+ program = 0;
+
+ do {
+ if (++tos == stack_size)
+ reallocateStack();
+
+ state_stack[tos] = action;
+
+ _Lcheck_token:
+ if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) {
+ yyprevlloc = yylloc;
+
+ if (first_token == last_token) {
+ yytoken = lexer->lex();
+ yylval = lexer->tokenValue();
+ yytokenspell = lexer->tokenSpell();
+ yylloc = location(lexer);
+ } else {
+ yytoken = first_token->token;
+ yylval = first_token->dval;
+ yytokenspell = first_token->spell;
+ yylloc = first_token->loc;
+ ++first_token;
+ }
+ }
+
+ action = t_action(action, yytoken);
+ if (action > 0) {
+ if (action != ACCEPT_STATE) {
+ yytoken = -1;
+ sym(1).dval = yylval;
+ stringRef(1) = yytokenspell;
+ loc(1) = yylloc;
+ } else {
+ --tos;
+ return ! hadErrors;
+ }
+ } else if (action < 0) {
+ const int r = -action - 1;
+ tos -= rhs[r];
+
+ switch (r) {
+./
+
+--------------------------------------------------------------------------------------------------------
+-- Declarative UI
+--------------------------------------------------------------------------------------------------------
+
+TopLevel: T_FEED_UI_PROGRAM UiProgram ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+./
+
+TopLevel: T_FEED_JS_STATEMENT Statement ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+./
+
+TopLevel: T_FEED_JS_EXPRESSION Expression ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+./
+
+TopLevel: T_FEED_JS_SOURCE_ELEMENT SourceElement ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+./
+
+TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+./
+
+TopLevel: T_FEED_JS_PROGRAM Program ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+./
+
+UiProgram: UiImportListOpt UiRootMember ;
+/.
+case $rule_number: {
+ sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiImportList,
+ sym(2).UiObjectMemberList->finish());
+} break;
+./
+
+UiImportListOpt: Empty ;
+UiImportListOpt: UiImportList ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(1).UiImportList->finish();
+} break;
+./
+
+UiImportList: UiImport ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImport);
+} break;
+./
+
+UiImportList: UiImportList UiImport ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImportList, sym(2).UiImport);
+} break;
+./
+
+ImportId: MemberExpression ;
+
+UiImport: UiImportHead T_AUTOMATIC_SEMICOLON ;
+UiImport: UiImportHead T_SEMICOLON ;
+/.
+case $rule_number: {
+ sym(1).UiImport->semicolonToken = loc(2);
+} break;
+./
+
+UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON ;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON ;
+/.
+case $rule_number: {
+ sym(1).UiImport->versionToken = loc(2);
+ sym(1).UiImport->semicolonToken = loc(3);
+} break;
+./
+
+UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_SEMICOLON ;
+/.
+case $rule_number: {
+ sym(1).UiImport->versionToken = loc(2);
+ sym(1).UiImport->asToken = loc(3);
+ sym(1).UiImport->importIdToken = loc(4);
+ sym(1).UiImport->importId = stringRef(4);
+ sym(1).UiImport->semicolonToken = loc(5);
+} break;
+./
+
+UiImport: UiImportHead T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ;
+UiImport: UiImportHead T_AS JsIdentifier T_SEMICOLON ;
+/.
+case $rule_number: {
+ sym(1).UiImport->asToken = loc(2);
+ sym(1).UiImport->importIdToken = loc(3);
+ sym(1).UiImport->importId = stringRef(3);
+ sym(1).UiImport->semicolonToken = loc(4);
+} break;
+./
+
+
+UiImportHead: T_IMPORT ImportId ;
+/.
+case $rule_number: {
+ AST::UiImport *node = 0;
+
+ if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) {
+ node = new (pool) AST::UiImport(importIdLiteral->value);
+ node->fileNameToken = loc(2);
+ } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) {
+ node = new (pool) AST::UiImport(qualifiedId);
+ node->fileNameToken = loc(2);
+ }
+
+ sym(1).Node = node;
+
+ if (node) {
+ node->importToken = loc(1);
+ } else {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ QLatin1String("Expected a qualified name id or a string literal")));
+
+ return false; // ### remove me
+ }
+} break;
+./
+
+Empty: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+UiRootMember: UiObjectDefinition ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
+} break;
+./
+
+UiObjectMemberList: UiObjectMember ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
+} break;
+./
+
+UiObjectMemberList: UiObjectMemberList UiObjectMember ;
+/.
+case $rule_number: {
+ AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList(
+ sym(1).UiObjectMemberList, sym(2).UiObjectMember);
+ sym(1).Node = node;
+} break;
+./
+
+UiArrayMemberList: UiObjectDefinition ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember);
+} break;
+./
+
+UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition ;
+/.
+case $rule_number: {
+ AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList(
+ sym(1).UiArrayMemberList, sym(3).UiObjectMember);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectInitializer: T_LBRACE T_RBRACE ;
+/.
+case $rule_number: {
+ AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE ;
+/.
+case $rule_number: {
+ AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectDefinition: UiQualifiedId UiObjectInitializer ;
+/.
+case $rule_number: {
+ AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId,
+ sym(2).UiObjectInitializer);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: UiObjectDefinition ;
+
+UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
+/.
+case $rule_number: {
+ AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding(
+ sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish());
+ node->colonToken = loc(2);
+ node->lbracketToken = loc(3);
+ node->rbracketToken = loc(5);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
+/.
+case $rule_number: {
+ AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
+ sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer ;
+/.
+case $rule_number: {
+ AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
+ sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer);
+ node->colonToken = loc(2);
+ node->hasOnToken = true;
+ sym(1).Node = node;
+} break;
+./
+
+UiScriptStatement: Block ;
+UiScriptStatement: EmptyStatement ;
+UiScriptStatement: ExpressionStatement ;
+UiScriptStatement: IfStatement ;
+UiScriptStatement: WithStatement ;
+UiScriptStatement: SwitchStatement ;
+UiScriptStatement: TryStatement ;
+
+UiObjectMember: UiQualifiedId T_COLON UiScriptStatement ;
+/.
+case $rule_number:
+{
+ AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(
+ sym(1).UiQualifiedId, sym(3).Statement);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+UiPropertyType: T_VAR ;
+UiPropertyType: T_RESERVED_WORD ;
+UiPropertyType: T_IDENTIFIER ;
+
+UiParameterListOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+UiParameterListOpt: UiParameterList ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(1).UiParameterList->finish ();
+} break;
+./
+
+UiParameterList: UiPropertyType JsIdentifier ;
+/.
+case $rule_number: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(stringRef(1), stringRef(2));
+ node->propertyTypeToken = loc(1);
+ node->identifierToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+UiParameterList: UiParameterList T_COMMA UiPropertyType JsIdentifier ;
+/.
+case $rule_number: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, stringRef(3), stringRef(4));
+ node->commaToken = loc(2);
+ node->identifierToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON ;
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2));
+ node->type = AST::UiPublicMember::Signal;
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(2);
+ node->parameters = sym(4).UiParameterList;
+ node->semicolonToken = loc(6);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON ;
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2));
+ node->type = AST::UiPublicMember::Signal;
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ;
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6));
+ node->typeModifier = stringRef(2);
+ node->propertyToken = loc(1);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(6);
+ node->semicolonToken = loc(7);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ;
+UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3));
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->semicolonToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ;
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4));
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->semicolonToken = loc(5);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3),
+ sym(5).Statement);
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4),
+ sym(6).Statement);
+ node->isReadonlyMember = true;
+ node->readonlyToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->colonToken = loc(5);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4),
+ sym(6).Statement);
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->colonToken = loc(5);
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6));
+ node->typeModifier = stringRef(2);
+ node->propertyToken = loc(1);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(6);
+ node->semicolonToken = loc(7); // insert a fake ';' before ':'
+
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
+ propertyName->identifierToken = loc(6);
+ propertyName->next = 0;
+
+ AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(
+ propertyName, sym(9).UiArrayMemberList->finish());
+ binding->colonToken = loc(7);
+ binding->lbracketToken = loc(8);
+ binding->rbracketToken = loc(10);
+
+ node->binding = binding;
+
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ;
+/.
+case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3));
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->semicolonToken = loc(4); // insert a fake ';' before ':'
+
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
+ propertyName->identifierToken = loc(3);
+ propertyName->next = 0;
+
+ AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
+ propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer);
+ binding->colonToken = loc(4);
+
+ node->binding = binding;
+
+ sym(1).Node = node;
+} break;
+./
+
+UiObjectMember: FunctionDeclaration ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
+} break;
+./
+
+UiObjectMember: VariableStatement ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
+} break;
+./
+
+JsIdentifier: T_IDENTIFIER;
+
+JsIdentifier: T_PROPERTY ;
+JsIdentifier: T_SIGNAL ;
+JsIdentifier: T_READONLY ;
+JsIdentifier: T_ON ;
+
+--------------------------------------------------------------------------------------------------------
+-- Expressions
+--------------------------------------------------------------------------------------------------------
+
+PrimaryExpression: T_THIS ;
+/.
+case $rule_number: {
+ AST::ThisExpression *node = new (pool) AST::ThisExpression();
+ node->thisToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: JsIdentifier ;
+/.
+case $rule_number: {
+ AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_NULL ;
+/.
+case $rule_number: {
+ AST::NullExpression *node = new (pool) AST::NullExpression();
+ node->nullToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_TRUE ;
+/.
+case $rule_number: {
+ AST::TrueLiteral *node = new (pool) AST::TrueLiteral();
+ node->trueToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_FALSE ;
+/.
+case $rule_number: {
+ AST::FalseLiteral *node = new (pool) AST::FalseLiteral();
+ node->falseToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_NUMERIC_LITERAL ;
+/.
+case $rule_number: {
+ AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval);
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_MULTILINE_STRING_LITERAL ;
+/.case $rule_number:./
+
+PrimaryExpression: T_STRING_LITERAL ;
+/.
+case $rule_number: {
+ AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1));
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_DIVIDE_ ;
+/:
+#define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number
+:/
+/.
+case $rule_number: {
+ bool rx = lexer->scanRegExp(Lexer::NoPrefix);
+ if (!rx) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
+ return false; // ### remove me
+ }
+
+ loc(1).length = lexer->tokenLength();
+ yylloc = loc(1); // adjust the location of the current token
+
+ AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
+ driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_DIVIDE_EQ ;
+/:
+#define J_SCRIPT_REGEXPLITERAL_RULE2 $rule_number
+:/
+/.
+case $rule_number: {
+ bool rx = lexer->scanRegExp(Lexer::EqualPrefix);
+ if (!rx) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
+ return false;
+ }
+
+ loc(1).length = lexer->tokenLength();
+ yylloc = loc(1); // adjust the location of the current token
+
+ AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
+ driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_LBRACKET T_RBRACKET ;
+/.
+case $rule_number: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0);
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_LBRACKET Elision T_RBRACKET ;
+/.
+case $rule_number: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish());
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ;
+/.
+case $rule_number: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ());
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_LBRACKET ElementList T_COMMA T_RBRACKET ;
+/.
+case $rule_number: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
+ (AST::Elision *) 0);
+ node->lbracketToken = loc(1);
+ node->commaToken = loc(3);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_LBRACKET ElementList T_COMMA Elision T_RBRACKET ;
+/.
+case $rule_number: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
+ sym(4).Elision->finish());
+ node->lbracketToken = loc(1);
+ node->commaToken = loc(3);
+ node->rbracketToken = loc(5);
+ sym(1).Node = node;
+} break;
+./
+
+-- PrimaryExpression: T_LBRACE T_RBRACE ;
+-- /.
+-- case $rule_number: {
+-- sym(1).Node = new (pool) AST::ObjectLiteral();
+-- } break;
+-- ./
+
+PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ;
+/.
+case $rule_number: {
+ AST::ObjectLiteral *node = 0;
+ if (sym(2).Node)
+ node = new (pool) AST::ObjectLiteral(
+ sym(2).PropertyNameAndValueList->finish ());
+ else
+ node = new (pool) AST::ObjectLiteral();
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_LBRACE PropertyNameAndValueList T_COMMA T_RBRACE ;
+/.
+case $rule_number: {
+ AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral(
+ sym(2).PropertyNameAndValueList->finish ());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+PrimaryExpression: T_LPAREN Expression T_RPAREN ;
+/.
+case $rule_number: {
+ AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression);
+ node->lparenToken = loc(1);
+ node->rparenToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+UiQualifiedId: MemberExpression ;
+/.
+case $rule_number: {
+ if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken,
+ QLatin1String("Ignored annotation")));
+
+ sym(1).Expression = mem->base;
+ }
+
+ if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) {
+ sym(1).UiQualifiedId = qualifiedId;
+ } else {
+ sym(1).UiQualifiedId = 0;
+
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ QLatin1String("Expected a qualified name id")));
+
+ return false; // ### recover
+ }
+} break;
+./
+
+ElementList: AssignmentExpression ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression);
+} break;
+./
+
+ElementList: Elision AssignmentExpression ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression);
+} break;
+./
+
+ElementList: ElementList T_COMMA AssignmentExpression ;
+/.
+case $rule_number: {
+ AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList,
+ (AST::Elision *) 0, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+ElementList: ElementList T_COMMA Elision AssignmentExpression ;
+/.
+case $rule_number: {
+ AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(),
+ sym(4).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+Elision: T_COMMA ;
+/.
+case $rule_number: {
+ AST::Elision *node = new (pool) AST::Elision();
+ node->commaToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+Elision: Elision T_COMMA ;
+/.
+case $rule_number: {
+ AST::Elision *node = new (pool) AST::Elision(sym(1).Elision);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+PropertyNameAndValueList: PropertyName T_COLON AssignmentExpression ;
+/.
+case $rule_number: {
+ AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList(
+ sym(1).PropertyName, sym(3).Expression);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+PropertyNameAndValueList: PropertyNameAndValueList T_COMMA PropertyName T_COLON AssignmentExpression ;
+/.
+case $rule_number: {
+ AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList(
+ sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression);
+ node->commaToken = loc(2);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+PropertyName: T_IDENTIFIER %prec SHIFT_THERE ;
+/.
+case $rule_number: {
+ AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PropertyName: T_SIGNAL ;
+/.case $rule_number:./
+
+PropertyName: T_PROPERTY ;
+/.
+case $rule_number: {
+ AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PropertyName: T_STRING_LITERAL ;
+/.
+case $rule_number: {
+ AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PropertyName: T_NUMERIC_LITERAL ;
+/.
+case $rule_number: {
+ AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval);
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+PropertyName: ReservedIdentifier ;
+/.
+case $rule_number: {
+ AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+ReservedIdentifier: T_BREAK ;
+ReservedIdentifier: T_CASE ;
+ReservedIdentifier: T_CATCH ;
+ReservedIdentifier: T_CONTINUE ;
+ReservedIdentifier: T_DEFAULT ;
+ReservedIdentifier: T_DELETE ;
+ReservedIdentifier: T_DO ;
+ReservedIdentifier: T_ELSE ;
+ReservedIdentifier: T_FALSE ;
+ReservedIdentifier: T_FINALLY ;
+ReservedIdentifier: T_FOR ;
+ReservedIdentifier: T_FUNCTION ;
+ReservedIdentifier: T_IF ;
+ReservedIdentifier: T_IN ;
+ReservedIdentifier: T_INSTANCEOF ;
+ReservedIdentifier: T_NEW ;
+ReservedIdentifier: T_NULL ;
+ReservedIdentifier: T_RETURN ;
+ReservedIdentifier: T_SWITCH ;
+ReservedIdentifier: T_THIS ;
+ReservedIdentifier: T_THROW ;
+ReservedIdentifier: T_TRUE ;
+ReservedIdentifier: T_TRY ;
+ReservedIdentifier: T_TYPEOF ;
+ReservedIdentifier: T_VAR ;
+ReservedIdentifier: T_VOID ;
+ReservedIdentifier: T_WHILE ;
+ReservedIdentifier: T_CONST ;
+ReservedIdentifier: T_DEBUGGER ;
+ReservedIdentifier: T_RESERVED_WORD ;
+ReservedIdentifier: T_WITH ;
+
+PropertyIdentifier: JsIdentifier ;
+PropertyIdentifier: ReservedIdentifier ;
+
+MemberExpression: PrimaryExpression ;
+MemberExpression: FunctionExpression ;
+
+MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ;
+/.
+case $rule_number: {
+ AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
+ node->lbracketToken = loc(2);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+MemberExpression: MemberExpression T_DOT PropertyIdentifier ;
+/.
+case $rule_number: {
+ AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
+ node->dotToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+MemberExpression: T_NEW MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ;
+/.
+case $rule_number: {
+ AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList);
+ node->newToken = loc(1);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ sym(1).Node = node;
+} break;
+./
+
+NewExpression: MemberExpression ;
+
+NewExpression: T_NEW NewExpression ;
+/.
+case $rule_number: {
+ AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression);
+ node->newToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+CallExpression: MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ;
+/.
+case $rule_number: {
+ AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+CallExpression: CallExpression T_LPAREN ArgumentListOpt T_RPAREN ;
+/.
+case $rule_number: {
+ AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ;
+/.
+case $rule_number: {
+ AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
+ node->lbracketToken = loc(2);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+CallExpression: CallExpression T_DOT PropertyIdentifier ;
+/.
+case $rule_number: {
+ AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
+ node->dotToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+ArgumentListOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+ArgumentListOpt: ArgumentList ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(1).ArgumentList->finish();
+} break;
+./
+
+ArgumentList: AssignmentExpression ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression);
+} break;
+./
+
+ArgumentList: ArgumentList T_COMMA AssignmentExpression ;
+/.
+case $rule_number: {
+ AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+LeftHandSideExpression: NewExpression ;
+LeftHandSideExpression: CallExpression ;
+PostfixExpression: LeftHandSideExpression ;
+
+PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ;
+/.
+case $rule_number: {
+ AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression);
+ node->incrementToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ;
+/.
+case $rule_number: {
+ AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression);
+ node->decrementToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+UnaryExpression: PostfixExpression ;
+
+UnaryExpression: T_DELETE UnaryExpression ;
+/.
+case $rule_number: {
+ AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression);
+ node->deleteToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+UnaryExpression: T_VOID UnaryExpression ;
+/.
+case $rule_number: {
+ AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression);
+ node->voidToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+UnaryExpression: T_TYPEOF UnaryExpression ;
+/.
+case $rule_number: {
+ AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression);
+ node->typeofToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+UnaryExpression: T_PLUS_PLUS UnaryExpression ;
+/.
+case $rule_number: {
+ AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression);
+ node->incrementToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+UnaryExpression: T_MINUS_MINUS UnaryExpression ;
+/.
+case $rule_number: {
+ AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression);
+ node->decrementToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+UnaryExpression: T_PLUS UnaryExpression ;
+/.
+case $rule_number: {
+ AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression);
+ node->plusToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+UnaryExpression: T_MINUS UnaryExpression ;
+/.
+case $rule_number: {
+ AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression);
+ node->minusToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+UnaryExpression: T_TILDE UnaryExpression ;
+/.
+case $rule_number: {
+ AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression);
+ node->tildeToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+UnaryExpression: T_NOT UnaryExpression ;
+/.
+case $rule_number: {
+ AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression);
+ node->notToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+MultiplicativeExpression: UnaryExpression ;
+
+MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Mul, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Div, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Mod, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+AdditiveExpression: MultiplicativeExpression ;
+
+AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Add, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Sub, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+ShiftExpression: AdditiveExpression ;
+
+ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::LShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::RShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::URShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpression: ShiftExpression ;
+
+RelationalExpression: RelationalExpression T_LT ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Lt, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpression: RelationalExpression T_GT ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Gt, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpression: RelationalExpression T_LE ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Le, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpression: RelationalExpression T_GE ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Ge, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::InstanceOf, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpression: RelationalExpression T_IN ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::In, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpressionNotIn: ShiftExpression ;
+
+RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Lt, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Gt, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Le, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Ge, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::InstanceOf, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+EqualityExpression: RelationalExpression ;
+
+EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Equal, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::NotEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::StrictEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::StrictNotEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+EqualityExpressionNotIn: RelationalExpressionNotIn ;
+
+EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Equal, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::NotEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::StrictEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::StrictNotEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+BitwiseANDExpression: EqualityExpression ;
+
+BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitAnd, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+BitwiseANDExpressionNotIn: EqualityExpressionNotIn ;
+
+BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitAnd, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+BitwiseXORExpression: BitwiseANDExpression ;
+
+BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitXor, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ;
+
+BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitXor, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+BitwiseORExpression: BitwiseXORExpression ;
+
+BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitOr, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ;
+
+BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitOr, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+LogicalANDExpression: BitwiseORExpression ;
+
+LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::And, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ;
+
+LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::And, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+LogicalORExpression: LogicalANDExpression ;
+
+LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Or, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+LogicalORExpressionNotIn: LogicalANDExpressionNotIn ;
+
+LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Or, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+ConditionalExpression: LogicalORExpression ;
+
+ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ;
+/.
+case $rule_number: {
+ AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
+ sym(3).Expression, sym(5).Expression);
+ node->questionToken = loc(2);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+ConditionalExpressionNotIn: LogicalORExpressionNotIn ;
+
+ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
+ sym(3).Expression, sym(5).Expression);
+ node->questionToken = loc(2);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+AssignmentExpression: ConditionalExpression ;
+
+AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+AssignmentExpressionNotIn: ConditionalExpressionNotIn ;
+
+AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+AssignmentOperator: T_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::Assign;
+} break;
+./
+
+AssignmentOperator: T_STAR_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceMul;
+} break;
+./
+
+AssignmentOperator: T_DIVIDE_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceDiv;
+} break;
+./
+
+AssignmentOperator: T_REMAINDER_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceMod;
+} break;
+./
+
+AssignmentOperator: T_PLUS_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceAdd;
+} break;
+./
+
+AssignmentOperator: T_MINUS_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceSub;
+} break;
+./
+
+AssignmentOperator: T_LT_LT_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceLeftShift;
+} break;
+./
+
+AssignmentOperator: T_GT_GT_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceRightShift;
+} break;
+./
+
+AssignmentOperator: T_GT_GT_GT_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceURightShift;
+} break;
+./
+
+AssignmentOperator: T_AND_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceAnd;
+} break;
+./
+
+AssignmentOperator: T_XOR_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceXor;
+} break;
+./
+
+AssignmentOperator: T_OR_EQ ;
+/.
+case $rule_number: {
+ sym(1).ival = QSOperator::InplaceOr;
+} break;
+./
+
+Expression: AssignmentExpression ;
+
+Expression: Expression T_COMMA AssignmentExpression ;
+/.
+case $rule_number: {
+ AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+ExpressionOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+ExpressionOpt: Expression ;
+
+ExpressionNotIn: AssignmentExpressionNotIn ;
+
+ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ;
+/.
+case $rule_number: {
+ AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+ExpressionNotInOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+ExpressionNotInOpt: ExpressionNotIn ;
+
+Statement: Block ;
+Statement: VariableStatement ;
+Statement: EmptyStatement ;
+Statement: ExpressionStatement ;
+Statement: IfStatement ;
+Statement: IterationStatement ;
+Statement: ContinueStatement ;
+Statement: BreakStatement ;
+Statement: ReturnStatement ;
+Statement: WithStatement ;
+Statement: LabelledStatement ;
+Statement: SwitchStatement ;
+Statement: ThrowStatement ;
+Statement: TryStatement ;
+Statement: DebuggerStatement ;
+
+
+Block: T_LBRACE StatementListOpt T_RBRACE ;
+/.
+case $rule_number: {
+ AST::Block *node = new (pool) AST::Block(sym(2).StatementList);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+StatementList: Statement ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::StatementList(sym(1).Statement);
+} break;
+./
+
+StatementList: StatementList Statement ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement);
+} break;
+./
+
+StatementListOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+StatementListOpt: StatementList ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(1).StatementList->finish ();
+} break;
+./
+
+VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::VariableStatement *node = new (pool) AST::VariableStatement(
+ sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST));
+ node->declarationKindToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+VariableDeclarationKind: T_CONST ;
+/.
+case $rule_number: {
+ sym(1).ival = T_CONST;
+} break;
+./
+
+VariableDeclarationKind: T_VAR ;
+/.
+case $rule_number: {
+ sym(1).ival = T_VAR;
+} break;
+./
+
+VariableDeclarationList: VariableDeclaration ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
+} break;
+./
+
+VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ;
+/.
+case $rule_number: {
+ AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList(
+ sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+VariableDeclarationListNotIn: VariableDeclarationNotIn ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
+} break;
+./
+
+VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
+} break;
+./
+
+VariableDeclaration: JsIdentifier InitialiserOpt ;
+/.
+case $rule_number: {
+ AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression);
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ;
+/.
+case $rule_number: {
+ AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression);
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+Initialiser: T_EQ AssignmentExpression ;
+/.
+case $rule_number: {
+ // ### TODO: AST for initializer
+ sym(1) = sym(2);
+} break;
+./
+
+InitialiserOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+InitialiserOpt: Initialiser ;
+
+InitialiserNotIn: T_EQ AssignmentExpressionNotIn ;
+/.
+case $rule_number: {
+ // ### TODO: AST for initializer
+ sym(1) = sym(2);
+} break;
+./
+
+InitialiserNotInOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+InitialiserNotInOpt: InitialiserNotIn ;
+
+EmptyStatement: T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::EmptyStatement *node = new (pool) AST::EmptyStatement();
+ node->semicolonToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ExpressionStatement: Expression T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ;
+/.
+case $rule_number: {
+ AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement);
+ node->ifToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ node->elseToken = loc(6);
+ sym(1).Node = node;
+} break;
+./
+
+IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ;
+/.
+case $rule_number: {
+ AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement);
+ node->ifToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression);
+ node->doToken = loc(1);
+ node->whileToken = loc(3);
+ node->lparenToken = loc(4);
+ node->rparenToken = loc(6);
+ node->semicolonToken = loc(7);
+ sym(1).Node = node;
+} break;
+./
+
+IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ;
+/.
+case $rule_number: {
+ AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement);
+ node->whileToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+/.
+case $rule_number: {
+ AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression,
+ sym(5).Expression, sym(7).Expression, sym(9).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->firstSemicolonToken = loc(4);
+ node->secondSemicolonToken = loc(6);
+ node->rparenToken = loc(8);
+ sym(1).Node = node;
+} break;
+./
+
+IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+/.
+case $rule_number: {
+ AST::LocalForStatement *node = new (pool) AST::LocalForStatement(
+ sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression,
+ sym(8).Expression, sym(10).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->varToken = loc(3);
+ node->firstSemicolonToken = loc(5);
+ node->secondSemicolonToken = loc(7);
+ node->rparenToken = loc(9);
+ sym(1).Node = node;
+} break;
+./
+
+IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ;
+/.
+case $rule_number: {
+ AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression,
+ sym(5).Expression, sym(7).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->inToken = loc(4);
+ node->rparenToken = loc(6);
+ sym(1).Node = node;
+} break;
+./
+
+IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ;
+/.
+case $rule_number: {
+ AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement(
+ sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->varToken = loc(3);
+ node->inToken = loc(5);
+ node->rparenToken = loc(7);
+ sym(1).Node = node;
+} break;
+./
+
+ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ContinueStatement: T_CONTINUE T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::ContinueStatement *node = new (pool) AST::ContinueStatement();
+ node->continueToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+ContinueStatement: T_CONTINUE JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ContinueStatement: T_CONTINUE JsIdentifier T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2));
+ node->continueToken = loc(1);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+BreakStatement: T_BREAK T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef());
+ node->breakToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+BreakStatement: T_BREAK JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+BreakStatement: T_BREAK JsIdentifier T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2));
+ node->breakToken = loc(1);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression);
+ node->returnToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ;
+/.
+case $rule_number: {
+ AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement);
+ node->withToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ;
+/.
+case $rule_number: {
+ AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock);
+ node->switchToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ;
+/.
+case $rule_number: {
+ AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ;
+/.
+case $rule_number: {
+ AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(5);
+ sym(1).Node = node;
+} break;
+./
+
+CaseClauses: CaseClause ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause);
+} break;
+./
+
+CaseClauses: CaseClauses CaseClause ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause);
+} break;
+./
+
+CaseClausesOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+CaseClausesOpt: CaseClauses ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(1).CaseClauses->finish ();
+} break;
+./
+
+CaseClause: T_CASE Expression T_COLON StatementListOpt ;
+/.
+case $rule_number: {
+ AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList);
+ node->caseToken = loc(1);
+ node->colonToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+DefaultClause: T_DEFAULT T_COLON StatementListOpt ;
+/.
+case $rule_number: {
+ AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList);
+ node->defaultToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+LabelledStatement: T_SIGNAL T_COLON Statement ;
+/.case $rule_number:./
+
+LabelledStatement: T_PROPERTY T_COLON Statement ;
+/.
+case $rule_number: {
+ AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
+ node->identifierToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+LabelledStatement: T_IDENTIFIER T_COLON Statement ;
+/.
+case $rule_number: {
+ AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
+ node->identifierToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+ThrowStatement: T_THROW Expression T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression);
+ node->throwToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+TryStatement: T_TRY Block Catch ;
+/.
+case $rule_number: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+TryStatement: T_TRY Block Finally ;
+/.
+case $rule_number: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+TryStatement: T_TRY Block Catch Finally ;
+/.
+case $rule_number: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+Catch: T_CATCH T_LPAREN JsIdentifier T_RPAREN Block ;
+/.
+case $rule_number: {
+ AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block);
+ node->catchToken = loc(1);
+ node->lparenToken = loc(2);
+ node->identifierToken = loc(3);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+./
+
+Finally: T_FINALLY Block ;
+/.
+case $rule_number: {
+ AST::Finally *node = new (pool) AST::Finally(sym(2).Block);
+ node->finallyToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
+DebuggerStatement: T_DEBUGGER T_SEMICOLON ;
+/.
+case $rule_number: {
+ AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement();
+ node->debuggerToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+} break;
+./
+
+FunctionDeclaration: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+/.
+case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ sym(1).Node = node;
+} break;
+./
+
+FunctionExpression: T_FUNCTION IdentifierOpt T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+/.
+case $rule_number: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
+ node->functionToken = loc(1);
+ if (! stringRef(2).isNull())
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ sym(1).Node = node;
+} break;
+./
+
+FormalParameterList: JsIdentifier ;
+/.
+case $rule_number: {
+ AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+} break;
+./
+
+FormalParameterList: FormalParameterList T_COMMA JsIdentifier ;
+/.
+case $rule_number: {
+ AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3));
+ node->commaToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+} break;
+./
+
+FormalParameterListOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+FormalParameterListOpt: FormalParameterList ;
+/.
+case $rule_number: {
+ sym(1).Node = sym(1).FormalParameterList->finish ();
+} break;
+./
+
+FunctionBodyOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+FunctionBodyOpt: FunctionBody ;
+
+FunctionBody: SourceElements ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ());
+} break;
+./
+
+Program: Empty ;
+
+Program: SourceElements ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ());
+} break;
+./
+
+SourceElements: SourceElement ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement);
+} break;
+./
+
+SourceElements: SourceElements SourceElement ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement);
+} break;
+./
+
+SourceElement: Statement ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement);
+} break;
+./
+
+SourceElement: FunctionDeclaration ;
+/.
+case $rule_number: {
+ sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration);
+} break;
+./
+
+IdentifierOpt: ;
+/.
+case $rule_number: {
+ stringRef(1) = QStringRef();
+} break;
+./
+
+IdentifierOpt: JsIdentifier ;
+
+PropertyNameAndValueListOpt: ;
+/.
+case $rule_number: {
+ sym(1).Node = 0;
+} break;
+./
+
+PropertyNameAndValueListOpt: PropertyNameAndValueList ;
+
+/.
+ } // switch
+ action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT);
+ } // if
+ } while (action != 0);
+
+ if (first_token == last_token) {
+ const int errorState = state_stack[tos];
+
+ // automatic insertion of `;'
+ if (yytoken != -1 && t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) {
+ SavedToken &tk = token_buffer[0];
+ tk.token = yytoken;
+ tk.dval = yylval;
+ tk.spell = yytokenspell;
+ tk.loc = yylloc;
+
+ yylloc = yyprevlloc;
+ yylloc.offset += yylloc.length;
+ yylloc.startColumn += yylloc.length;
+ yylloc.length = 0;
+
+ //const QString msg = qApp->translate("QmlParser", "Missing `;'");
+ //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg));
+
+ first_token = &token_buffer[0];
+ last_token = &token_buffer[1];
+
+ yytoken = T_SEMICOLON;
+ yylval = 0;
+
+ action = errorState;
+
+ goto _Lcheck_token;
+ }
+
+ hadErrors = true;
+
+ token_buffer[0].token = yytoken;
+ token_buffer[0].dval = yylval;
+ token_buffer[0].spell = yytokenspell;
+ token_buffer[0].loc = yylloc;
+
+ token_buffer[1].token = yytoken = lexer->lex();
+ token_buffer[1].dval = yylval = lexer->tokenValue();
+ token_buffer[1].spell = yytokenspell = lexer->tokenSpell();
+ token_buffer[1].loc = yylloc = location(lexer);
+
+ if (t_action(errorState, yytoken)) {
+ QString msg;
+ int token = token_buffer[0].token;
+ if (token < 0 || token >= TERMINAL_COUNT)
+ msg = qApp->translate("QmlParser", "Syntax error");
+ else
+ msg = qApp->translate("QmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token]));
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+
+ action = errorState;
+ goto _Lcheck_token;
+ }
+
+ static int tokens[] = {
+ T_PLUS,
+ T_EQ,
+
+ T_COMMA,
+ T_COLON,
+ T_SEMICOLON,
+
+ T_RPAREN, T_RBRACKET, T_RBRACE,
+
+ T_NUMERIC_LITERAL,
+ T_IDENTIFIER,
+
+ T_LPAREN, T_LBRACKET, T_LBRACE,
+
+ EOF_SYMBOL
+ };
+
+ for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) {
+ int a = t_action(errorState, *tk);
+ if (a > 0 && t_action(a, yytoken)) {
+ const QString msg = qApp->translate("QmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk]));
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+
+ yytoken = *tk;
+ yylval = 0;
+ yylloc = token_buffer[0].loc;
+ yylloc.length = 0;
+
+ first_token = &token_buffer[0];
+ last_token = &token_buffer[2];
+
+ action = errorState;
+ goto _Lcheck_token;
+ }
+ }
+
+ for (int tk = 1; tk < TERMINAL_COUNT; ++tk) {
+ if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM ||
+ tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION ||
+ tk == T_FEED_JS_SOURCE_ELEMENT)
+ continue;
+
+ int a = t_action(errorState, tk);
+ if (a > 0 && t_action(a, yytoken)) {
+ const QString msg = qApp->translate("QmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk]));
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+
+ yytoken = tk;
+ yylval = 0;
+ yylloc = token_buffer[0].loc;
+ yylloc.length = 0;
+
+ action = errorState;
+ goto _Lcheck_token;
+ }
+ }
+
+ const QString msg = qApp->translate("QmlParser", "Syntax error");
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+ }
+
+ return false;
+}
+
+QT_QML_END_NAMESPACE
+
+
+./
+/:
+QT_QML_END_NAMESPACE
+
+
+
+#endif // QMLJSPARSER_P_H
+:/
diff --git a/src/lib/corelib/parser/qmljsast.cpp b/src/lib/corelib/parser/qmljsast.cpp
new file mode 100644
index 000000000..3bf7ab968
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsast.cpp
@@ -0,0 +1,915 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 "qmljsast_p.h"
+
+#include "qmljsastvisitor_p.h"
+
+namespace QbsQmlJS {
+namespace AST {
+
+void Node::accept(Visitor *visitor)
+{
+ if (visitor->preVisit(this)) {
+ accept0(visitor);
+ }
+ visitor->postVisit(this);
+}
+
+void Node::accept(Node *node, Visitor *visitor)
+{
+ if (node)
+ node->accept(visitor);
+}
+
+ExpressionNode *Node::expressionCast()
+{
+ return 0;
+}
+
+BinaryExpression *Node::binaryExpressionCast()
+{
+ return 0;
+}
+
+Statement *Node::statementCast()
+{
+ return 0;
+}
+
+UiObjectMember *Node::uiObjectMemberCast()
+{
+ return 0;
+}
+
+ExpressionNode *ExpressionNode::expressionCast()
+{
+ return this;
+}
+
+BinaryExpression *BinaryExpression::binaryExpressionCast()
+{
+ return this;
+}
+
+Statement *Statement::statementCast()
+{
+ return this;
+}
+
+UiObjectMember *UiObjectMember::uiObjectMemberCast()
+{
+ return this;
+}
+
+void NestedExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+ visitor->endVisit(this);
+}
+
+void ThisExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void IdentifierExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void NullExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void TrueLiteral::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void FalseLiteral::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void StringLiteral::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void NumericLiteral::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void RegExpLiteral::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void ArrayLiteral::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(elements, visitor);
+ accept(elision, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ObjectLiteral::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(properties, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ElementList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (ElementList *it = this; it; it = it->next) {
+ accept(it->elision, visitor);
+ accept(it->expression, visitor);
+ }
+ }
+
+ visitor->endVisit(this);
+}
+
+void Elision::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ // ###
+ }
+
+ visitor->endVisit(this);
+}
+
+void PropertyNameAndValueList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (PropertyNameAndValueList *it = this; it; it = it->next) {
+ accept(it->name, visitor);
+ accept(it->value, visitor);
+ }
+ }
+
+ visitor->endVisit(this);
+}
+
+void IdentifierPropertyName::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void StringLiteralPropertyName::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void NumericLiteralPropertyName::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void ArrayMemberExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(base, visitor);
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void FieldMemberExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(base, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void NewMemberExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(base, visitor);
+ accept(arguments, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void NewExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void CallExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(base, visitor);
+ accept(arguments, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ArgumentList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (ArgumentList *it = this; it; it = it->next) {
+ accept(it->expression, visitor);
+ }
+ }
+
+ visitor->endVisit(this);
+}
+
+void PostIncrementExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(base, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PostDecrementExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(base, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void DeleteExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void VoidExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void TypeOfExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PreIncrementExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PreDecrementExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UnaryPlusExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UnaryMinusExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void TildeExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void NotExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void BinaryExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(left, visitor);
+ accept(right, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ConditionalExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ accept(ok, visitor);
+ accept(ko, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void Expression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(left, visitor);
+ accept(right, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void Block::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(statements, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void StatementList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (StatementList *it = this; it; it = it->next) {
+ accept(it->statement, visitor);
+ }
+ }
+
+ visitor->endVisit(this);
+}
+
+void VariableStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(declarations, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void VariableDeclarationList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (VariableDeclarationList *it = this; it; it = it->next) {
+ accept(it->declaration, visitor);
+ }
+ }
+
+ visitor->endVisit(this);
+}
+
+void VariableDeclaration::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void EmptyStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void ExpressionStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void IfStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ accept(ok, visitor);
+ accept(ko, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void DoWhileStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(statement, visitor);
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void WhileStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ForStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(initialiser, visitor);
+ accept(condition, visitor);
+ accept(expression, visitor);
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void LocalForStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(declarations, visitor);
+ accept(condition, visitor);
+ accept(expression, visitor);
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ForEachStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(initialiser, visitor);
+ accept(expression, visitor);
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void LocalForEachStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(declaration, visitor);
+ accept(expression, visitor);
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ContinueStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void BreakStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void ReturnStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void WithStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void SwitchStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ accept(block, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void CaseBlock::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(clauses, visitor);
+ accept(defaultClause, visitor);
+ accept(moreClauses, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void CaseClauses::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (CaseClauses *it = this; it; it = it->next) {
+ accept(it->clause, visitor);
+ }
+ }
+
+ visitor->endVisit(this);
+}
+
+void CaseClause::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ accept(statements, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void DefaultClause::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(statements, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void LabelledStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ThrowStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void TryStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(statement, visitor);
+ accept(catchExpression, visitor);
+ accept(finallyExpression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void Catch::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void Finally::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void FunctionDeclaration::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(formals, visitor);
+ accept(body, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void FunctionExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(formals, visitor);
+ accept(body, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void FormalParameterList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ // ###
+ }
+
+ visitor->endVisit(this);
+}
+
+void FunctionBody::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(elements, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void Program::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(elements, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void SourceElements::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (SourceElements *it = this; it; it = it->next) {
+ accept(it->element, visitor);
+ }
+ }
+
+ visitor->endVisit(this);
+}
+
+void FunctionSourceElement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(declaration, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void StatementSourceElement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void DebuggerStatement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiProgram::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(imports, visitor);
+ accept(members, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiPublicMember::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(statement, visitor);
+ accept(binding, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiObjectDefinition::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(qualifiedTypeNameId, visitor);
+ accept(initializer, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiObjectInitializer::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(members, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiObjectBinding::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(qualifiedId, visitor);
+ accept(qualifiedTypeNameId, visitor);
+ accept(initializer, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiScriptBinding::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(qualifiedId, visitor);
+ accept(statement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiArrayBinding::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(qualifiedId, visitor);
+ accept(members, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiObjectMemberList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (UiObjectMemberList *it = this; it; it = it->next)
+ accept(it->member, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiArrayMemberList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (UiArrayMemberList *it = this; it; it = it->next)
+ accept(it->member, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiQualifiedId::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiImport::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(importUri, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiImportList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(import, visitor);
+ accept(next, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void UiSourceElement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(sourceElement, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+} // namespace AST
+} // namespace QbsQmlJS
diff --git a/src/lib/corelib/parser/qmljsast_p.h b/src/lib/corelib/parser/qmljsast_p.h
new file mode 100644
index 000000000..3de1dd9f9
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsast_p.h
@@ -0,0 +1,2623 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QMLJSAST_P_H
+#define QMLJSAST_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmljsastvisitor_p.h"
+#include "qmljsglobal_p.h"
+#include "qmljsmemorypool_p.h"
+
+#include <QtCore/QString>
+
+namespace QbsQmlJS {
+
+#define QMLJS_DECLARE_AST_NODE(name) \
+ enum { K = Kind_##name };
+
+namespace QSOperator // ### rename
+{
+
+enum Op {
+ Add,
+ And,
+ InplaceAnd,
+ Assign,
+ BitAnd,
+ BitOr,
+ BitXor,
+ InplaceSub,
+ Div,
+ InplaceDiv,
+ Equal,
+ Ge,
+ Gt,
+ In,
+ InplaceAdd,
+ InstanceOf,
+ Le,
+ LShift,
+ InplaceLeftShift,
+ Lt,
+ Mod,
+ InplaceMod,
+ Mul,
+ InplaceMul,
+ NotEqual,
+ Or,
+ InplaceOr,
+ RShift,
+ InplaceRightShift,
+ StrictEqual,
+ StrictNotEqual,
+ Sub,
+ URShift,
+ InplaceURightShift,
+ InplaceXor
+};
+
+} // namespace QSOperator
+
+namespace AST {
+
+template <typename _T1, typename _T2>
+_T1 cast(_T2 *ast)
+{
+ if (ast && ast->kind == static_cast<_T1>(0)->K)
+ return static_cast<_T1>(ast);
+
+ return 0;
+}
+
+class QML_PARSER_EXPORT Node: public Managed
+{
+public:
+ enum Kind {
+ Kind_Undefined,
+
+ Kind_ArgumentList,
+ Kind_ArrayLiteral,
+ Kind_ArrayMemberExpression,
+ Kind_BinaryExpression,
+ Kind_Block,
+ Kind_BreakStatement,
+ Kind_CallExpression,
+ Kind_CaseBlock,
+ Kind_CaseClause,
+ Kind_CaseClauses,
+ Kind_Catch,
+ Kind_ConditionalExpression,
+ Kind_ContinueStatement,
+ Kind_DebuggerStatement,
+ Kind_DefaultClause,
+ Kind_DeleteExpression,
+ Kind_DoWhileStatement,
+ Kind_ElementList,
+ Kind_Elision,
+ Kind_EmptyStatement,
+ Kind_Expression,
+ Kind_ExpressionStatement,
+ Kind_FalseLiteral,
+ Kind_FieldMemberExpression,
+ Kind_Finally,
+ Kind_ForEachStatement,
+ Kind_ForStatement,
+ Kind_FormalParameterList,
+ Kind_FunctionBody,
+ Kind_FunctionDeclaration,
+ Kind_FunctionExpression,
+ Kind_FunctionSourceElement,
+ Kind_IdentifierExpression,
+ Kind_IdentifierPropertyName,
+ Kind_IfStatement,
+ Kind_LabelledStatement,
+ Kind_LocalForEachStatement,
+ Kind_LocalForStatement,
+ Kind_NewExpression,
+ Kind_NewMemberExpression,
+ Kind_NotExpression,
+ Kind_NullExpression,
+ Kind_NumericLiteral,
+ Kind_NumericLiteralPropertyName,
+ Kind_ObjectLiteral,
+ Kind_PostDecrementExpression,
+ Kind_PostIncrementExpression,
+ Kind_PreDecrementExpression,
+ Kind_PreIncrementExpression,
+ Kind_Program,
+ Kind_PropertyName,
+ Kind_PropertyNameAndValueList,
+ Kind_RegExpLiteral,
+ Kind_ReturnStatement,
+ Kind_SourceElement,
+ Kind_SourceElements,
+ Kind_StatementList,
+ Kind_StatementSourceElement,
+ Kind_StringLiteral,
+ Kind_StringLiteralPropertyName,
+ Kind_SwitchStatement,
+ Kind_ThisExpression,
+ Kind_ThrowStatement,
+ Kind_TildeExpression,
+ Kind_TrueLiteral,
+ Kind_TryStatement,
+ Kind_TypeOfExpression,
+ Kind_UnaryMinusExpression,
+ Kind_UnaryPlusExpression,
+ Kind_VariableDeclaration,
+ Kind_VariableDeclarationList,
+ Kind_VariableStatement,
+ Kind_VoidExpression,
+ Kind_WhileStatement,
+ Kind_WithStatement,
+ Kind_NestedExpression,
+
+ Kind_UiArrayBinding,
+ Kind_UiImport,
+ Kind_UiImportList,
+ Kind_UiObjectBinding,
+ Kind_UiObjectDefinition,
+ Kind_UiObjectInitializer,
+ Kind_UiObjectMemberList,
+ Kind_UiArrayMemberList,
+ Kind_UiProgram,
+ Kind_UiParameterList,
+ Kind_UiPublicMember,
+ Kind_UiQualifiedId,
+ Kind_UiScriptBinding,
+ Kind_UiSourceElement
+ };
+
+ inline Node()
+ : kind(Kind_Undefined) {}
+
+ // NOTE: node destructors are never called,
+ // instead we block free the memory
+ // (see the NodePool class)
+ virtual ~Node() {}
+
+ virtual ExpressionNode *expressionCast();
+ virtual BinaryExpression *binaryExpressionCast();
+ virtual Statement *statementCast();
+ virtual UiObjectMember *uiObjectMemberCast();
+
+ void accept(Visitor *visitor);
+ static void accept(Node *node, Visitor *visitor);
+
+ inline static void acceptChild(Node *node, Visitor *visitor)
+ { return accept(node, visitor); } // ### remove
+
+ virtual void accept0(Visitor *visitor) = 0;
+ virtual SourceLocation firstSourceLocation() const = 0;
+ virtual SourceLocation lastSourceLocation() const = 0;
+
+// attributes
+ int kind;
+};
+
+class QML_PARSER_EXPORT ExpressionNode: public Node
+{
+public:
+ ExpressionNode() {}
+
+ virtual ExpressionNode *expressionCast();
+};
+
+class QML_PARSER_EXPORT Statement: public Node
+{
+public:
+ Statement() {}
+
+ virtual Statement *statementCast();
+};
+
+class QML_PARSER_EXPORT NestedExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(NestedExpression)
+
+ NestedExpression(ExpressionNode *expression)
+ : expression(expression)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return lparenToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rparenToken; }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation lparenToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT ThisExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ThisExpression)
+
+ ThisExpression() { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return thisToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return thisToken; }
+
+// attributes
+ SourceLocation thisToken;
+};
+
+class QML_PARSER_EXPORT IdentifierExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(IdentifierExpression)
+
+ IdentifierExpression(const QStringRef &n):
+ name (n) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return identifierToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return identifierToken; }
+
+// attributes
+ QStringRef name;
+ SourceLocation identifierToken;
+};
+
+class QML_PARSER_EXPORT NullExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(NullExpression)
+
+ NullExpression() { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return nullToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return nullToken; }
+
+// attributes
+ SourceLocation nullToken;
+};
+
+class QML_PARSER_EXPORT TrueLiteral: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(TrueLiteral)
+
+ TrueLiteral() { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return trueToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return trueToken; }
+
+// attributes
+ SourceLocation trueToken;
+};
+
+class QML_PARSER_EXPORT FalseLiteral: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(FalseLiteral)
+
+ FalseLiteral() { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return falseToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return falseToken; }
+
+// attributes
+ SourceLocation falseToken;
+};
+
+class QML_PARSER_EXPORT NumericLiteral: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(NumericLiteral)
+
+ NumericLiteral(double v):
+ value(v) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return literalToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return literalToken; }
+
+// attributes:
+ double value;
+ SourceLocation literalToken;
+};
+
+class QML_PARSER_EXPORT StringLiteral: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(StringLiteral)
+
+ StringLiteral(const QStringRef &v):
+ value (v) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return literalToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return literalToken; }
+
+// attributes:
+ QStringRef value;
+ SourceLocation literalToken;
+};
+
+class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(RegExpLiteral)
+
+ RegExpLiteral(const QStringRef &p, int f):
+ pattern (p), flags (f) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return literalToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return literalToken; }
+
+// attributes:
+ QStringRef pattern;
+ int flags;
+ SourceLocation literalToken;
+};
+
+class QML_PARSER_EXPORT ArrayLiteral: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ArrayLiteral)
+
+ ArrayLiteral(Elision *e):
+ elements (0), elision (e)
+ { kind = K; }
+
+ ArrayLiteral(ElementList *elts):
+ elements (elts), elision (0)
+ { kind = K; }
+
+ ArrayLiteral(ElementList *elts, Elision *e):
+ elements (elts), elision (e)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return lbracketToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rbracketToken; }
+
+// attributes
+ ElementList *elements;
+ Elision *elision;
+ SourceLocation lbracketToken;
+ SourceLocation commaToken;
+ SourceLocation rbracketToken;
+};
+
+class QML_PARSER_EXPORT ObjectLiteral: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ObjectLiteral)
+
+ ObjectLiteral():
+ properties (0) { kind = K; }
+
+ ObjectLiteral(PropertyNameAndValueList *plist):
+ properties (plist) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return lbraceToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rbraceToken; }
+
+// attributes
+ PropertyNameAndValueList *properties;
+ SourceLocation lbraceToken;
+ SourceLocation rbraceToken;
+};
+
+class QML_PARSER_EXPORT Elision: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(Elision)
+
+ Elision():
+ next (this) { kind = K; }
+
+ Elision(Elision *previous)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return commaToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : commaToken; }
+
+ inline Elision *finish ()
+ {
+ Elision *front = next;
+ next = 0;
+ return front;
+ }
+
+// attributes
+ Elision *next;
+ SourceLocation commaToken;
+};
+
+class QML_PARSER_EXPORT ElementList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ElementList)
+
+ ElementList(Elision *e, ExpressionNode *expr):
+ elision (e), expression (expr), next (this)
+ { kind = K; }
+
+ ElementList(ElementList *previous, Elision *e, ExpressionNode *expr):
+ elision (e), expression (expr)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ inline ElementList *finish ()
+ {
+ ElementList *front = next;
+ next = 0;
+ return front;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ {
+ if (elision)
+ return elision->firstSourceLocation();
+ return expression->firstSourceLocation();
+ }
+
+ virtual SourceLocation lastSourceLocation() const
+ {
+ if (next)
+ return next->lastSourceLocation();
+ return expression->lastSourceLocation();
+ }
+
+// attributes
+ Elision *elision;
+ ExpressionNode *expression;
+ ElementList *next;
+ SourceLocation commaToken;
+};
+
+class QML_PARSER_EXPORT PropertyName: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(PropertyName)
+
+ PropertyName() { kind = K; }
+
+ virtual SourceLocation firstSourceLocation() const
+ { return propertyNameToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return propertyNameToken; }
+
+// attributes
+ SourceLocation propertyNameToken;
+};
+
+class QML_PARSER_EXPORT PropertyNameAndValueList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(PropertyNameAndValueList)
+
+ PropertyNameAndValueList(PropertyName *n, ExpressionNode *v):
+ name (n), value (v), next (this)
+ { kind = K; }
+
+ PropertyNameAndValueList(PropertyNameAndValueList *previous, PropertyName *n, ExpressionNode *v):
+ name (n), value (v)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return name->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ {
+ if (next)
+ return next->lastSourceLocation();
+ return value->lastSourceLocation();
+ }
+
+ inline PropertyNameAndValueList *finish ()
+ {
+ PropertyNameAndValueList *front = next;
+ next = 0;
+ return front;
+ }
+
+// attributes
+ PropertyName *name;
+ ExpressionNode *value;
+ PropertyNameAndValueList *next;
+ SourceLocation colonToken;
+ SourceLocation commaToken;
+};
+
+class QML_PARSER_EXPORT IdentifierPropertyName: public PropertyName
+{
+public:
+ QMLJS_DECLARE_AST_NODE(IdentifierPropertyName)
+
+ IdentifierPropertyName(const QStringRef &n):
+ id (n) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+// attributes
+ QStringRef id;
+};
+
+class QML_PARSER_EXPORT StringLiteralPropertyName: public PropertyName
+{
+public:
+ QMLJS_DECLARE_AST_NODE(StringLiteralPropertyName)
+
+ StringLiteralPropertyName(const QStringRef &n):
+ id (n) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+// attributes
+ QStringRef id;
+};
+
+class QML_PARSER_EXPORT NumericLiteralPropertyName: public PropertyName
+{
+public:
+ QMLJS_DECLARE_AST_NODE(NumericLiteralPropertyName)
+
+ NumericLiteralPropertyName(double n):
+ id (n) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+// attributes
+ double id;
+};
+
+class QML_PARSER_EXPORT ArrayMemberExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ArrayMemberExpression)
+
+ ArrayMemberExpression(ExpressionNode *b, ExpressionNode *e):
+ base (b), expression (e)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return base->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rbracketToken; }
+
+// attributes
+ ExpressionNode *base;
+ ExpressionNode *expression;
+ SourceLocation lbracketToken;
+ SourceLocation rbracketToken;
+};
+
+class QML_PARSER_EXPORT FieldMemberExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(FieldMemberExpression)
+
+ FieldMemberExpression(ExpressionNode *b, const QStringRef &n):
+ base (b), name (n)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return base->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return identifierToken; }
+
+ // attributes
+ ExpressionNode *base;
+ QStringRef name;
+ SourceLocation dotToken;
+ SourceLocation identifierToken;
+};
+
+class QML_PARSER_EXPORT NewMemberExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(NewMemberExpression)
+
+ NewMemberExpression(ExpressionNode *b, ArgumentList *a):
+ base (b), arguments (a)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return newToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rparenToken; }
+
+ // attributes
+ ExpressionNode *base;
+ ArgumentList *arguments;
+ SourceLocation newToken;
+ SourceLocation lparenToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT NewExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(NewExpression)
+
+ NewExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return newToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation newToken;
+};
+
+class QML_PARSER_EXPORT CallExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(CallExpression)
+
+ CallExpression(ExpressionNode *b, ArgumentList *a):
+ base (b), arguments (a)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return base->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rparenToken; }
+
+// attributes
+ ExpressionNode *base;
+ ArgumentList *arguments;
+ SourceLocation lparenToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT ArgumentList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ArgumentList)
+
+ ArgumentList(ExpressionNode *e):
+ expression (e), next (this)
+ { kind = K; }
+
+ ArgumentList(ArgumentList *previous, ExpressionNode *e):
+ expression (e)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return expression->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ {
+ if (next)
+ return next->lastSourceLocation();
+ return expression->lastSourceLocation();
+ }
+
+ inline ArgumentList *finish ()
+ {
+ ArgumentList *front = next;
+ next = 0;
+ return front;
+ }
+
+// attributes
+ ExpressionNode *expression;
+ ArgumentList *next;
+ SourceLocation commaToken;
+};
+
+class QML_PARSER_EXPORT PostIncrementExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(PostIncrementExpression)
+
+ PostIncrementExpression(ExpressionNode *b):
+ base (b) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return base->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return incrementToken; }
+
+// attributes
+ ExpressionNode *base;
+ SourceLocation incrementToken;
+};
+
+class QML_PARSER_EXPORT PostDecrementExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(PostDecrementExpression)
+
+ PostDecrementExpression(ExpressionNode *b):
+ base (b) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return base->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return decrementToken; }
+
+// attributes
+ ExpressionNode *base;
+ SourceLocation decrementToken;
+};
+
+class QML_PARSER_EXPORT DeleteExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(DeleteExpression)
+
+ DeleteExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return deleteToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation deleteToken;
+};
+
+class QML_PARSER_EXPORT VoidExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(VoidExpression)
+
+ VoidExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return voidToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation voidToken;
+};
+
+class QML_PARSER_EXPORT TypeOfExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(TypeOfExpression)
+
+ TypeOfExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return typeofToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation typeofToken;
+};
+
+class QML_PARSER_EXPORT PreIncrementExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(PreIncrementExpression)
+
+ PreIncrementExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return incrementToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation incrementToken;
+};
+
+class QML_PARSER_EXPORT PreDecrementExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(PreDecrementExpression)
+
+ PreDecrementExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return decrementToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation decrementToken;
+};
+
+class QML_PARSER_EXPORT UnaryPlusExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UnaryPlusExpression)
+
+ UnaryPlusExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return plusToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation plusToken;
+};
+
+class QML_PARSER_EXPORT UnaryMinusExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UnaryMinusExpression)
+
+ UnaryMinusExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return minusToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation minusToken;
+};
+
+class QML_PARSER_EXPORT TildeExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(TildeExpression)
+
+ TildeExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return tildeToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation tildeToken;
+};
+
+class QML_PARSER_EXPORT NotExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(NotExpression)
+
+ NotExpression(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return notToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation notToken;
+};
+
+class QML_PARSER_EXPORT BinaryExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(BinaryExpression)
+
+ BinaryExpression(ExpressionNode *l, int o, ExpressionNode *r):
+ left (l), op (o), right (r)
+ { kind = K; }
+
+ virtual BinaryExpression *binaryExpressionCast();
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return left->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return right->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *left;
+ int op;
+ ExpressionNode *right;
+ SourceLocation operatorToken;
+};
+
+class QML_PARSER_EXPORT ConditionalExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ConditionalExpression)
+
+ ConditionalExpression(ExpressionNode *e, ExpressionNode *t, ExpressionNode *f):
+ expression (e), ok (t), ko (f)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return expression->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return ko->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ ExpressionNode *ok;
+ ExpressionNode *ko;
+ SourceLocation questionToken;
+ SourceLocation colonToken;
+};
+
+class QML_PARSER_EXPORT Expression: public ExpressionNode // ### rename
+{
+public:
+ QMLJS_DECLARE_AST_NODE(Expression)
+
+ Expression(ExpressionNode *l, ExpressionNode *r):
+ left (l), right (r) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return left->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return right->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *left;
+ ExpressionNode *right;
+ SourceLocation commaToken;
+};
+
+class QML_PARSER_EXPORT Block: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(Block)
+
+ Block(StatementList *slist):
+ statements (slist) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return lbraceToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rbraceToken; }
+
+ // attributes
+ StatementList *statements;
+ SourceLocation lbraceToken;
+ SourceLocation rbraceToken;
+};
+
+class QML_PARSER_EXPORT StatementList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(StatementList)
+
+ StatementList(Statement *stmt):
+ statement (stmt), next (this)
+ { kind = K; }
+
+ StatementList(StatementList *previous, Statement *stmt):
+ statement (stmt)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return statement->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : statement->lastSourceLocation(); }
+
+ inline StatementList *finish ()
+ {
+ StatementList *front = next;
+ next = 0;
+ return front;
+ }
+
+// attributes
+ Statement *statement;
+ StatementList *next;
+};
+
+class QML_PARSER_EXPORT VariableStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(VariableStatement)
+
+ VariableStatement(VariableDeclarationList *vlist):
+ declarations (vlist)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return declarationKindToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+// attributes
+ VariableDeclarationList *declarations;
+ SourceLocation declarationKindToken;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT VariableDeclaration: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(VariableDeclaration)
+
+ VariableDeclaration(const QStringRef &n, ExpressionNode *e):
+ name (n), expression (e), readOnly(false)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return identifierToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return expression ? expression->lastSourceLocation() : identifierToken; }
+
+// attributes
+ QStringRef name;
+ ExpressionNode *expression;
+ bool readOnly;
+ SourceLocation identifierToken;
+};
+
+class QML_PARSER_EXPORT VariableDeclarationList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(VariableDeclarationList)
+
+ VariableDeclarationList(VariableDeclaration *decl):
+ declaration (decl), next (this)
+ { kind = K; }
+
+ VariableDeclarationList(VariableDeclarationList *previous, VariableDeclaration *decl):
+ declaration (decl)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return declaration->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ {
+ if (next)
+ return next->lastSourceLocation();
+ return declaration->lastSourceLocation();
+ }
+
+ inline VariableDeclarationList *finish (bool readOnly)
+ {
+ VariableDeclarationList *front = next;
+ next = 0;
+ if (readOnly) {
+ VariableDeclarationList *vdl;
+ for (vdl = front; vdl != 0; vdl = vdl->next)
+ vdl->declaration->readOnly = true;
+ }
+ return front;
+ }
+
+// attributes
+ VariableDeclaration *declaration;
+ VariableDeclarationList *next;
+ SourceLocation commaToken;
+};
+
+class QML_PARSER_EXPORT EmptyStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(EmptyStatement)
+
+ EmptyStatement() { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return semicolonToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+// attributes
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT ExpressionStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ExpressionStatement)
+
+ ExpressionStatement(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return expression->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT IfStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(IfStatement)
+
+ IfStatement(ExpressionNode *e, Statement *t, Statement *f = 0):
+ expression (e), ok (t), ko (f)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return ifToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ {
+ if (ko)
+ return ko->lastSourceLocation();
+
+ return ok->lastSourceLocation();
+ }
+
+// attributes
+ ExpressionNode *expression;
+ Statement *ok;
+ Statement *ko;
+ SourceLocation ifToken;
+ SourceLocation lparenToken;
+ SourceLocation rparenToken;
+ SourceLocation elseToken;
+};
+
+class QML_PARSER_EXPORT DoWhileStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(DoWhileStatement)
+
+ DoWhileStatement(Statement *stmt, ExpressionNode *e):
+ statement (stmt), expression (e)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return doToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+// attributes
+ Statement *statement;
+ ExpressionNode *expression;
+ SourceLocation doToken;
+ SourceLocation whileToken;
+ SourceLocation lparenToken;
+ SourceLocation rparenToken;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT WhileStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(WhileStatement)
+
+ WhileStatement(ExpressionNode *e, Statement *stmt):
+ expression (e), statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return whileToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ Statement *statement;
+ SourceLocation whileToken;
+ SourceLocation lparenToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT ForStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ForStatement)
+
+ ForStatement(ExpressionNode *i, ExpressionNode *c, ExpressionNode *e, Statement *stmt):
+ initialiser (i), condition (c), expression (e), statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return forToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *initialiser;
+ ExpressionNode *condition;
+ ExpressionNode *expression;
+ Statement *statement;
+ SourceLocation forToken;
+ SourceLocation lparenToken;
+ SourceLocation firstSemicolonToken;
+ SourceLocation secondSemicolonToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT LocalForStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(LocalForStatement)
+
+ LocalForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt):
+ declarations (vlist), condition (c), expression (e), statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return forToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+// attributes
+ VariableDeclarationList *declarations;
+ ExpressionNode *condition;
+ ExpressionNode *expression;
+ Statement *statement;
+ SourceLocation forToken;
+ SourceLocation lparenToken;
+ SourceLocation varToken;
+ SourceLocation firstSemicolonToken;
+ SourceLocation secondSemicolonToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT ForEachStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ForEachStatement)
+
+ ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt):
+ initialiser (i), expression (e), statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return forToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *initialiser;
+ ExpressionNode *expression;
+ Statement *statement;
+ SourceLocation forToken;
+ SourceLocation lparenToken;
+ SourceLocation inToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT LocalForEachStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(LocalForEachStatement)
+
+ LocalForEachStatement(VariableDeclaration *v, ExpressionNode *e, Statement *stmt):
+ declaration (v), expression (e), statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return forToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+// attributes
+ VariableDeclaration *declaration;
+ ExpressionNode *expression;
+ Statement *statement;
+ SourceLocation forToken;
+ SourceLocation lparenToken;
+ SourceLocation varToken;
+ SourceLocation inToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT ContinueStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ContinueStatement)
+
+ ContinueStatement(const QStringRef &l = QStringRef()):
+ label (l) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return continueToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+// attributes
+ QStringRef label;
+ SourceLocation continueToken;
+ SourceLocation identifierToken;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT BreakStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(BreakStatement)
+
+ BreakStatement(const QStringRef &l):
+ label (l) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return breakToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+ // attributes
+ QStringRef label;
+ SourceLocation breakToken;
+ SourceLocation identifierToken;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT ReturnStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ReturnStatement)
+
+ ReturnStatement(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return returnToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+// attributes
+ ExpressionNode *expression;
+ SourceLocation returnToken;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT WithStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(WithStatement)
+
+ WithStatement(ExpressionNode *e, Statement *stmt):
+ expression (e), statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return withToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+ Statement *statement;
+ SourceLocation withToken;
+ SourceLocation lparenToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT CaseBlock: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(CaseBlock)
+
+ CaseBlock(CaseClauses *c, DefaultClause *d = 0, CaseClauses *r = 0):
+ clauses (c), defaultClause (d), moreClauses (r)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return lbraceToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rbraceToken; }
+
+// attributes
+ CaseClauses *clauses;
+ DefaultClause *defaultClause;
+ CaseClauses *moreClauses;
+ SourceLocation lbraceToken;
+ SourceLocation rbraceToken;
+};
+
+class QML_PARSER_EXPORT SwitchStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(SwitchStatement)
+
+ SwitchStatement(ExpressionNode *e, CaseBlock *b):
+ expression (e), block (b)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return switchToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return block->rbraceToken; }
+
+// attributes
+ ExpressionNode *expression;
+ CaseBlock *block;
+ SourceLocation switchToken;
+ SourceLocation lparenToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT CaseClause: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(CaseClause)
+
+ CaseClause(ExpressionNode *e, StatementList *slist):
+ expression (e), statements (slist)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return caseToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statements ? statements->lastSourceLocation() : colonToken; }
+
+// attributes
+ ExpressionNode *expression;
+ StatementList *statements;
+ SourceLocation caseToken;
+ SourceLocation colonToken;
+};
+
+class QML_PARSER_EXPORT CaseClauses: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(CaseClauses)
+
+ CaseClauses(CaseClause *c):
+ clause (c), next (this)
+ { kind = K; }
+
+ CaseClauses(CaseClauses *previous, CaseClause *c):
+ clause (c)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return clause->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : clause->lastSourceLocation(); }
+
+ inline CaseClauses *finish ()
+ {
+ CaseClauses *front = next;
+ next = 0;
+ return front;
+ }
+
+//attributes
+ CaseClause *clause;
+ CaseClauses *next;
+};
+
+class QML_PARSER_EXPORT DefaultClause: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(DefaultClause)
+
+ DefaultClause(StatementList *slist):
+ statements (slist)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return defaultToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statements ? statements->lastSourceLocation() : colonToken; }
+
+// attributes
+ StatementList *statements;
+ SourceLocation defaultToken;
+ SourceLocation colonToken;
+};
+
+class QML_PARSER_EXPORT LabelledStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(LabelledStatement)
+
+ LabelledStatement(const QStringRef &l, Statement *stmt):
+ label (l), statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return identifierToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+// attributes
+ QStringRef label;
+ Statement *statement;
+ SourceLocation identifierToken;
+ SourceLocation colonToken;
+};
+
+class QML_PARSER_EXPORT ThrowStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(ThrowStatement)
+
+ ThrowStatement(ExpressionNode *e):
+ expression (e) { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return throwToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+ // attributes
+ ExpressionNode *expression;
+ SourceLocation throwToken;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT Catch: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(Catch)
+
+ Catch(const QStringRef &n, Block *stmt):
+ name (n), statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return catchToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+// attributes
+ QStringRef name;
+ Block *statement;
+ SourceLocation catchToken;
+ SourceLocation lparenToken;
+ SourceLocation identifierToken;
+ SourceLocation rparenToken;
+};
+
+class QML_PARSER_EXPORT Finally: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(Finally)
+
+ Finally(Block *stmt):
+ statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return finallyToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement ? statement->lastSourceLocation() : finallyToken; }
+
+// attributes
+ Block *statement;
+ SourceLocation finallyToken;
+};
+
+class QML_PARSER_EXPORT TryStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(TryStatement)
+
+ TryStatement(Statement *stmt, Catch *c, Finally *f):
+ statement (stmt), catchExpression (c), finallyExpression (f)
+ { kind = K; }
+
+ TryStatement(Statement *stmt, Finally *f):
+ statement (stmt), catchExpression (0), finallyExpression (f)
+ { kind = K; }
+
+ TryStatement(Statement *stmt, Catch *c):
+ statement (stmt), catchExpression (c), finallyExpression (0)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return tryToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ {
+ if (finallyExpression)
+ return finallyExpression->statement->rbraceToken;
+ else if (catchExpression)
+ return catchExpression->statement->rbraceToken;
+
+ return statement->lastSourceLocation();
+ }
+
+// attributes
+ Statement *statement;
+ Catch *catchExpression;
+ Finally *finallyExpression;
+ SourceLocation tryToken;
+};
+
+class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode
+{
+public:
+ QMLJS_DECLARE_AST_NODE(FunctionExpression)
+
+ FunctionExpression(const QStringRef &n, FormalParameterList *f, FunctionBody *b):
+ name (n), formals (f), body (b)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return functionToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rbraceToken; }
+
+// attributes
+ QStringRef name;
+ FormalParameterList *formals;
+ FunctionBody *body;
+ SourceLocation functionToken;
+ SourceLocation identifierToken;
+ SourceLocation lparenToken;
+ SourceLocation rparenToken;
+ SourceLocation lbraceToken;
+ SourceLocation rbraceToken;
+};
+
+class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression
+{
+public:
+ QMLJS_DECLARE_AST_NODE(FunctionDeclaration)
+
+ FunctionDeclaration(const QStringRef &n, FormalParameterList *f, FunctionBody *b):
+ FunctionExpression(n, f, b)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+};
+
+class QML_PARSER_EXPORT FormalParameterList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(FormalParameterList)
+
+ FormalParameterList(const QStringRef &n):
+ name (n), next (this)
+ { kind = K; }
+
+ FormalParameterList(FormalParameterList *previous, const QStringRef &n):
+ name (n)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return identifierToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : identifierToken; }
+
+ inline FormalParameterList *finish ()
+ {
+ FormalParameterList *front = next;
+ next = 0;
+ return front;
+ }
+
+// attributes
+ QStringRef name;
+ FormalParameterList *next;
+ SourceLocation commaToken;
+ SourceLocation identifierToken;
+};
+
+class QML_PARSER_EXPORT SourceElement: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(SourceElement)
+
+ inline SourceElement()
+ { kind = K; }
+};
+
+class QML_PARSER_EXPORT SourceElements: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(SourceElements)
+
+ SourceElements(SourceElement *elt):
+ element (elt), next (this)
+ { kind = K; }
+
+ SourceElements(SourceElements *previous, SourceElement *elt):
+ element (elt)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return element->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : element->lastSourceLocation(); }
+
+ inline SourceElements *finish ()
+ {
+ SourceElements *front = next;
+ next = 0;
+ return front;
+ }
+
+// attributes
+ SourceElement *element;
+ SourceElements *next;
+};
+
+class QML_PARSER_EXPORT FunctionBody: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(FunctionBody)
+
+ FunctionBody(SourceElements *elts):
+ elements (elts)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return elements ? elements->firstSourceLocation() : SourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return elements ? elements->lastSourceLocation() : SourceLocation(); }
+
+// attributes
+ SourceElements *elements;
+};
+
+class QML_PARSER_EXPORT Program: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(Program)
+
+ Program(SourceElements *elts):
+ elements (elts)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return elements ? elements->firstSourceLocation() : SourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return elements ? elements->lastSourceLocation() : SourceLocation(); }
+
+// attributes
+ SourceElements *elements;
+};
+
+class QML_PARSER_EXPORT FunctionSourceElement: public SourceElement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(FunctionSourceElement)
+
+ FunctionSourceElement(FunctionDeclaration *f):
+ declaration (f)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return declaration->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return declaration->lastSourceLocation(); }
+
+// attributes
+ FunctionDeclaration *declaration;
+};
+
+class QML_PARSER_EXPORT StatementSourceElement: public SourceElement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(StatementSourceElement)
+
+ StatementSourceElement(Statement *stmt):
+ statement (stmt)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return statement->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+// attributes
+ Statement *statement;
+};
+
+class QML_PARSER_EXPORT DebuggerStatement: public Statement
+{
+public:
+ QMLJS_DECLARE_AST_NODE(DebuggerStatement)
+
+ DebuggerStatement()
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return debuggerToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+// attributes
+ SourceLocation debuggerToken;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT UiQualifiedId: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiQualifiedId)
+
+ UiQualifiedId(const QStringRef &name)
+ : next(this), name(name)
+ { kind = K; }
+
+ UiQualifiedId(UiQualifiedId *previous, const QStringRef &name)
+ : name(name)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ UiQualifiedId *finish()
+ {
+ UiQualifiedId *head = next;
+ next = 0;
+ return head;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return identifierToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : identifierToken; }
+
+// attributes
+ UiQualifiedId *next;
+ QStringRef name;
+ SourceLocation identifierToken;
+};
+
+class QML_PARSER_EXPORT UiImport: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiImport)
+
+ UiImport(const QStringRef &fileName)
+ : fileName(fileName), importUri(0)
+ { kind = K; }
+
+ UiImport(UiQualifiedId *uri)
+ : importUri(uri)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return importToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return semicolonToken; }
+
+// attributes
+ QStringRef fileName;
+ UiQualifiedId *importUri;
+ QStringRef importId;
+ SourceLocation importToken;
+ SourceLocation fileNameToken;
+ SourceLocation versionToken;
+ SourceLocation asToken;
+ SourceLocation importIdToken;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT UiImportList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiImportList)
+
+ UiImportList(UiImport *import)
+ : import(import),
+ next(this)
+ { kind = K; }
+
+ UiImportList(UiImportList *previous, UiImport *import)
+ : import(import)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ UiImportList *finish()
+ {
+ UiImportList *head = next;
+ next = 0;
+ return head;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return import->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : import->lastSourceLocation(); }
+
+// attributes
+ UiImport *import;
+ UiImportList *next;
+};
+
+class QML_PARSER_EXPORT UiObjectMember: public Node
+{
+public:
+ virtual SourceLocation firstSourceLocation() const = 0;
+ virtual SourceLocation lastSourceLocation() const = 0;
+
+ virtual UiObjectMember *uiObjectMemberCast();
+};
+
+class QML_PARSER_EXPORT UiObjectMemberList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiObjectMemberList)
+
+ UiObjectMemberList(UiObjectMember *member)
+ : next(this), member(member)
+ { kind = K; }
+
+ UiObjectMemberList(UiObjectMemberList *previous, UiObjectMember *member)
+ : member(member)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return member->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : member->lastSourceLocation(); }
+
+ UiObjectMemberList *finish()
+ {
+ UiObjectMemberList *head = next;
+ next = 0;
+ return head;
+ }
+
+// attributes
+ UiObjectMemberList *next;
+ UiObjectMember *member;
+};
+
+class QML_PARSER_EXPORT UiProgram: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiProgram)
+
+ UiProgram(UiImportList *imports, UiObjectMemberList *members)
+ : imports(imports), members(members)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ {
+ if (imports)
+ return imports->firstSourceLocation();
+ else if (members)
+ return members->firstSourceLocation();
+ return SourceLocation();
+ }
+
+ virtual SourceLocation lastSourceLocation() const
+ {
+ if (members)
+ return members->lastSourceLocation();
+ else if (imports)
+ return imports->lastSourceLocation();
+ return SourceLocation();
+ }
+
+// attributes
+ UiImportList *imports;
+ UiObjectMemberList *members;
+};
+
+class QML_PARSER_EXPORT UiArrayMemberList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiArrayMemberList)
+
+ UiArrayMemberList(UiObjectMember *member)
+ : next(this), member(member)
+ { kind = K; }
+
+ UiArrayMemberList(UiArrayMemberList *previous, UiObjectMember *member)
+ : member(member)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return member->firstSourceLocation(); }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : member->lastSourceLocation(); }
+
+ UiArrayMemberList *finish()
+ {
+ UiArrayMemberList *head = next;
+ next = 0;
+ return head;
+ }
+
+// attributes
+ UiArrayMemberList *next;
+ UiObjectMember *member;
+ SourceLocation commaToken;
+};
+
+class QML_PARSER_EXPORT UiObjectInitializer: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiObjectInitializer)
+
+ UiObjectInitializer(UiObjectMemberList *members)
+ : members(members)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return lbraceToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rbraceToken; }
+
+// attributes
+ SourceLocation lbraceToken;
+ UiObjectMemberList *members;
+ SourceLocation rbraceToken;
+};
+
+class QML_PARSER_EXPORT UiParameterList: public Node
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiParameterList)
+
+ UiParameterList(const QStringRef &t, const QStringRef &n):
+ type (t), name (n), next (this)
+ { kind = K; }
+
+ UiParameterList(UiParameterList *previous, const QStringRef &t, const QStringRef &n):
+ type (t), name (n)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
+
+ virtual void accept0(Visitor *) {}
+
+ virtual SourceLocation firstSourceLocation() const
+ { return propertyTypeToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return next ? next->lastSourceLocation() : identifierToken; }
+
+ inline UiParameterList *finish ()
+ {
+ UiParameterList *front = next;
+ next = 0;
+ return front;
+ }
+
+// attributes
+ QStringRef type;
+ QStringRef name;
+ UiParameterList *next;
+ SourceLocation commaToken;
+ SourceLocation propertyTypeToken;
+ SourceLocation identifierToken;
+};
+
+class QML_PARSER_EXPORT UiPublicMember: public UiObjectMember
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiPublicMember)
+
+ UiPublicMember(const QStringRef &memberType,
+ const QStringRef &name)
+ : type(Property), memberType(memberType), name(name), statement(0), binding(0), isDefaultMember(false), isReadonlyMember(false), parameters(0)
+ { kind = K; }
+
+ UiPublicMember(const QStringRef &memberType,
+ const QStringRef &name,
+ Statement *statement)
+ : type(Property), memberType(memberType), name(name), statement(statement), binding(0), isDefaultMember(false), isReadonlyMember(false), parameters(0)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ {
+ if (defaultToken.isValid())
+ return defaultToken;
+ else if (readonlyToken.isValid())
+ return readonlyToken;
+
+ return propertyToken;
+ }
+
+ virtual SourceLocation lastSourceLocation() const
+ {
+ if (binding)
+ return binding->lastSourceLocation();
+ if (statement)
+ return statement->lastSourceLocation();
+
+ return semicolonToken;
+ }
+
+// attributes
+ enum { Signal, Property } type;
+ QStringRef typeModifier;
+ QStringRef memberType;
+ QStringRef name;
+ Statement *statement; // initialized with a JS expression
+ UiObjectMember *binding; // initialized with a QML object or array.
+ bool isDefaultMember;
+ bool isReadonlyMember;
+ UiParameterList *parameters;
+ SourceLocation defaultToken;
+ SourceLocation readonlyToken;
+ SourceLocation propertyToken;
+ SourceLocation typeModifierToken;
+ SourceLocation typeToken;
+ SourceLocation identifierToken;
+ SourceLocation colonToken;
+ SourceLocation semicolonToken;
+};
+
+class QML_PARSER_EXPORT UiObjectDefinition: public UiObjectMember
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiObjectDefinition)
+
+ UiObjectDefinition(UiQualifiedId *qualifiedTypeNameId,
+ UiObjectInitializer *initializer)
+ : qualifiedTypeNameId(qualifiedTypeNameId), initializer(initializer)
+ { kind = K; }
+
+ virtual void accept0(Visitor *visitor);
+
+ virtual SourceLocation firstSourceLocation() const
+ { return qualifiedTypeNameId->identifierToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return initializer->rbraceToken; }
+
+// attributes
+ UiQualifiedId *qualifiedTypeNameId;
+ UiObjectInitializer *initializer;
+};
+
+class QML_PARSER_EXPORT UiSourceElement: public UiObjectMember
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiSourceElement)
+
+ UiSourceElement(Node *sourceElement)
+ : sourceElement(sourceElement)
+ { kind = K; }
+
+ virtual SourceLocation firstSourceLocation() const
+ {
+ if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement))
+ return funDecl->firstSourceLocation();
+ else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement))
+ return varStmt->firstSourceLocation();
+
+ return SourceLocation();
+ }
+
+ virtual SourceLocation lastSourceLocation() const
+ {
+ if (FunctionDeclaration *funDecl = cast<FunctionDeclaration *>(sourceElement))
+ return funDecl->lastSourceLocation();
+ else if (VariableStatement *varStmt = cast<VariableStatement *>(sourceElement))
+ return varStmt->lastSourceLocation();
+
+ return SourceLocation();
+ }
+
+ virtual void accept0(Visitor *visitor);
+
+
+// attributes
+ Node *sourceElement;
+};
+
+class QML_PARSER_EXPORT UiObjectBinding: public UiObjectMember
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiObjectBinding)
+
+ UiObjectBinding(UiQualifiedId *qualifiedId,
+ UiQualifiedId *qualifiedTypeNameId,
+ UiObjectInitializer *initializer)
+ : qualifiedId(qualifiedId),
+ qualifiedTypeNameId(qualifiedTypeNameId),
+ initializer(initializer),
+ hasOnToken(false)
+ { kind = K; }
+
+ virtual SourceLocation firstSourceLocation() const
+ {
+ if (hasOnToken && qualifiedTypeNameId)
+ return qualifiedTypeNameId->identifierToken;
+
+ return qualifiedId->identifierToken;
+ }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return initializer->rbraceToken; }
+
+ virtual void accept0(Visitor *visitor);
+
+
+// attributes
+ UiQualifiedId *qualifiedId;
+ UiQualifiedId *qualifiedTypeNameId;
+ UiObjectInitializer *initializer;
+ SourceLocation colonToken;
+ bool hasOnToken;
+};
+
+class QML_PARSER_EXPORT UiScriptBinding: public UiObjectMember
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiScriptBinding)
+
+ UiScriptBinding(UiQualifiedId *qualifiedId,
+ Statement *statement)
+ : qualifiedId(qualifiedId),
+ statement(statement)
+ { kind = K; }
+
+ virtual SourceLocation firstSourceLocation() const
+ { return qualifiedId->identifierToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return statement->lastSourceLocation(); }
+
+ virtual void accept0(Visitor *visitor);
+
+// attributes
+ UiQualifiedId *qualifiedId;
+ Statement *statement;
+ SourceLocation colonToken;
+};
+
+class QML_PARSER_EXPORT UiArrayBinding: public UiObjectMember
+{
+public:
+ QMLJS_DECLARE_AST_NODE(UiArrayBinding)
+
+ UiArrayBinding(UiQualifiedId *qualifiedId,
+ UiArrayMemberList *members)
+ : qualifiedId(qualifiedId),
+ members(members)
+ { kind = K; }
+
+ virtual SourceLocation firstSourceLocation() const
+ { return qualifiedId->identifierToken; }
+
+ virtual SourceLocation lastSourceLocation() const
+ { return rbracketToken; }
+
+ virtual void accept0(Visitor *visitor);
+
+// attributes
+ UiQualifiedId *qualifiedId;
+ UiArrayMemberList *members;
+ SourceLocation colonToken;
+ SourceLocation lbracketToken;
+ SourceLocation rbracketToken;
+};
+
+} // namespace AST
+} // namespace QbsQmlJS
+
+#endif
diff --git a/src/lib/corelib/parser/qmljsastfwd_p.h b/src/lib/corelib/parser/qmljsastfwd_p.h
new file mode 100644
index 000000000..f8dbeb7ad
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsastfwd_p.h
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QMLJSAST_FWD_P_H
+#define QMLJSAST_FWD_P_H
+
+#include "qmljsglobal_p.h"
+
+#include <QtCore/qglobal.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+namespace QbsQmlJS {
+namespace AST {
+
+class SourceLocation
+{
+public:
+ SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0)
+ : offset(offset), length(length),
+ startLine(line), startColumn(column)
+ { }
+
+ bool isValid() const { return length != 0; }
+
+ quint32 begin() const { return offset; }
+ quint32 end() const { return offset + length; }
+
+// attributes
+ // ### encode
+ quint32 offset;
+ quint32 length;
+ quint32 startLine;
+ quint32 startColumn;
+};
+
+class Visitor;
+class Node;
+class ExpressionNode;
+class Statement;
+class ThisExpression;
+class IdentifierExpression;
+class NullExpression;
+class TrueLiteral;
+class FalseLiteral;
+class NumericLiteral;
+class StringLiteral;
+class RegExpLiteral;
+class ArrayLiteral;
+class ObjectLiteral;
+class ElementList;
+class Elision;
+class PropertyNameAndValueList;
+class PropertyName;
+class IdentifierPropertyName;
+class StringLiteralPropertyName;
+class NumericLiteralPropertyName;
+class ArrayMemberExpression;
+class FieldMemberExpression;
+class NewMemberExpression;
+class NewExpression;
+class CallExpression;
+class ArgumentList;
+class PostIncrementExpression;
+class PostDecrementExpression;
+class DeleteExpression;
+class VoidExpression;
+class TypeOfExpression;
+class PreIncrementExpression;
+class PreDecrementExpression;
+class UnaryPlusExpression;
+class UnaryMinusExpression;
+class TildeExpression;
+class NotExpression;
+class BinaryExpression;
+class ConditionalExpression;
+class Expression; // ### rename
+class Block;
+class StatementList;
+class VariableStatement;
+class VariableDeclarationList;
+class VariableDeclaration;
+class EmptyStatement;
+class ExpressionStatement;
+class IfStatement;
+class DoWhileStatement;
+class WhileStatement;
+class ForStatement;
+class LocalForStatement;
+class ForEachStatement;
+class LocalForEachStatement;
+class ContinueStatement;
+class BreakStatement;
+class ReturnStatement;
+class WithStatement;
+class SwitchStatement;
+class CaseBlock;
+class CaseClauses;
+class CaseClause;
+class DefaultClause;
+class LabelledStatement;
+class ThrowStatement;
+class TryStatement;
+class Catch;
+class Finally;
+class FunctionDeclaration;
+class FunctionExpression;
+class FormalParameterList;
+class FunctionBody;
+class Program;
+class SourceElements;
+class SourceElement;
+class FunctionSourceElement;
+class StatementSourceElement;
+class DebuggerStatement;
+class NestedExpression;
+
+// ui elements
+class UiProgram;
+class UiImportList;
+class UiImport;
+class UiPublicMember;
+class UiObjectDefinition;
+class UiObjectInitializer;
+class UiObjectBinding;
+class UiScriptBinding;
+class UiSourceElement;
+class UiArrayBinding;
+class UiObjectMember;
+class UiObjectMemberList;
+class UiArrayMemberList;
+class UiQualifiedId;
+
+} // namespace AST
+} // namespace QbsQmlJS
+
+#endif
diff --git a/src/lib/corelib/parser/qmljsastvisitor.cpp b/src/lib/corelib/parser/qmljsastvisitor.cpp
new file mode 100644
index 000000000..7cec7dcfc
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsastvisitor.cpp
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 "qmljsastvisitor_p.h"
+
+namespace QbsQmlJS {
+namespace AST {
+
+Visitor::Visitor()
+{
+}
+
+Visitor::~Visitor()
+{
+}
+
+} // namespace AST
+} // namespace QbsQmlJS
diff --git a/src/lib/corelib/parser/qmljsastvisitor_p.h b/src/lib/corelib/parser/qmljsastvisitor_p.h
new file mode 100644
index 000000000..135b07503
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsastvisitor_p.h
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QMLJSASTVISITOR_P_H
+#define QMLJSASTVISITOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmljsastfwd_p.h"
+#include "qmljsglobal_p.h"
+
+namespace QbsQmlJS {
+namespace AST {
+
+class QML_PARSER_EXPORT Visitor
+{
+public:
+ Visitor();
+ virtual ~Visitor();
+
+ virtual bool preVisit(Node *) { return true; }
+ virtual void postVisit(Node *) {}
+
+ // Ui
+ virtual bool visit(UiProgram *) { return true; }
+ virtual bool visit(UiImportList *) { return true; }
+ virtual bool visit(UiImport *) { return true; }
+ virtual bool visit(UiPublicMember *) { return true; }
+ virtual bool visit(UiSourceElement *) { return true; }
+ virtual bool visit(UiObjectDefinition *) { return true; }
+ virtual bool visit(UiObjectInitializer *) { return true; }
+ virtual bool visit(UiObjectBinding *) { return true; }
+ virtual bool visit(UiScriptBinding *) { return true; }
+ virtual bool visit(UiArrayBinding *) { return true; }
+ virtual bool visit(UiObjectMemberList *) { return true; }
+ virtual bool visit(UiArrayMemberList *) { return true; }
+ virtual bool visit(UiQualifiedId *) { return true; }
+
+ virtual void endVisit(UiProgram *) {}
+ virtual void endVisit(UiImportList *) {}
+ virtual void endVisit(UiImport *) {}
+ virtual void endVisit(UiPublicMember *) {}
+ virtual void endVisit(UiSourceElement *) {}
+ virtual void endVisit(UiObjectDefinition *) {}
+ virtual void endVisit(UiObjectInitializer *) {}
+ virtual void endVisit(UiObjectBinding *) {}
+ virtual void endVisit(UiScriptBinding *) {}
+ virtual void endVisit(UiArrayBinding *) {}
+ virtual void endVisit(UiObjectMemberList *) {}
+ virtual void endVisit(UiArrayMemberList *) {}
+ virtual void endVisit(UiQualifiedId *) {}
+
+ // QbsQmlJS
+ virtual bool visit(ThisExpression *) { return true; }
+ virtual void endVisit(ThisExpression *) {}
+
+ virtual bool visit(IdentifierExpression *) { return true; }
+ virtual void endVisit(IdentifierExpression *) {}
+
+ virtual bool visit(NullExpression *) { return true; }
+ virtual void endVisit(NullExpression *) {}
+
+ virtual bool visit(TrueLiteral *) { return true; }
+ virtual void endVisit(TrueLiteral *) {}
+
+ virtual bool visit(FalseLiteral *) { return true; }
+ virtual void endVisit(FalseLiteral *) {}
+
+ virtual bool visit(StringLiteral *) { return true; }
+ virtual void endVisit(StringLiteral *) {}
+
+ virtual bool visit(NumericLiteral *) { return true; }
+ virtual void endVisit(NumericLiteral *) {}
+
+ virtual bool visit(RegExpLiteral *) { return true; }
+ virtual void endVisit(RegExpLiteral *) {}
+
+ virtual bool visit(ArrayLiteral *) { return true; }
+ virtual void endVisit(ArrayLiteral *) {}
+
+ virtual bool visit(ObjectLiteral *) { return true; }
+ virtual void endVisit(ObjectLiteral *) {}
+
+ virtual bool visit(ElementList *) { return true; }
+ virtual void endVisit(ElementList *) {}
+
+ virtual bool visit(Elision *) { return true; }
+ virtual void endVisit(Elision *) {}
+
+ virtual bool visit(PropertyNameAndValueList *) { return true; }
+ virtual void endVisit(PropertyNameAndValueList *) {}
+
+ virtual bool visit(NestedExpression *) { return true; }
+ virtual void endVisit(NestedExpression *) {}
+
+ virtual bool visit(IdentifierPropertyName *) { return true; }
+ virtual void endVisit(IdentifierPropertyName *) {}
+
+ virtual bool visit(StringLiteralPropertyName *) { return true; }
+ virtual void endVisit(StringLiteralPropertyName *) {}
+
+ virtual bool visit(NumericLiteralPropertyName *) { return true; }
+ virtual void endVisit(NumericLiteralPropertyName *) {}
+
+ virtual bool visit(ArrayMemberExpression *) { return true; }
+ virtual void endVisit(ArrayMemberExpression *) {}
+
+ virtual bool visit(FieldMemberExpression *) { return true; }
+ virtual void endVisit(FieldMemberExpression *) {}
+
+ virtual bool visit(NewMemberExpression *) { return true; }
+ virtual void endVisit(NewMemberExpression *) {}
+
+ virtual bool visit(NewExpression *) { return true; }
+ virtual void endVisit(NewExpression *) {}
+
+ virtual bool visit(CallExpression *) { return true; }
+ virtual void endVisit(CallExpression *) {}
+
+ virtual bool visit(ArgumentList *) { return true; }
+ virtual void endVisit(ArgumentList *) {}
+
+ virtual bool visit(PostIncrementExpression *) { return true; }
+ virtual void endVisit(PostIncrementExpression *) {}
+
+ virtual bool visit(PostDecrementExpression *) { return true; }
+ virtual void endVisit(PostDecrementExpression *) {}
+
+ virtual bool visit(DeleteExpression *) { return true; }
+ virtual void endVisit(DeleteExpression *) {}
+
+ virtual bool visit(VoidExpression *) { return true; }
+ virtual void endVisit(VoidExpression *) {}
+
+ virtual bool visit(TypeOfExpression *) { return true; }
+ virtual void endVisit(TypeOfExpression *) {}
+
+ virtual bool visit(PreIncrementExpression *) { return true; }
+ virtual void endVisit(PreIncrementExpression *) {}
+
+ virtual bool visit(PreDecrementExpression *) { return true; }
+ virtual void endVisit(PreDecrementExpression *) {}
+
+ virtual bool visit(UnaryPlusExpression *) { return true; }
+ virtual void endVisit(UnaryPlusExpression *) {}
+
+ virtual bool visit(UnaryMinusExpression *) { return true; }
+ virtual void endVisit(UnaryMinusExpression *) {}
+
+ virtual bool visit(TildeExpression *) { return true; }
+ virtual void endVisit(TildeExpression *) {}
+
+ virtual bool visit(NotExpression *) { return true; }
+ virtual void endVisit(NotExpression *) {}
+
+ virtual bool visit(BinaryExpression *) { return true; }
+ virtual void endVisit(BinaryExpression *) {}
+
+ virtual bool visit(ConditionalExpression *) { return true; }
+ virtual void endVisit(ConditionalExpression *) {}
+
+ virtual bool visit(Expression *) { return true; }
+ virtual void endVisit(Expression *) {}
+
+ virtual bool visit(Block *) { return true; }
+ virtual void endVisit(Block *) {}
+
+ virtual bool visit(StatementList *) { return true; }
+ virtual void endVisit(StatementList *) {}
+
+ virtual bool visit(VariableStatement *) { return true; }
+ virtual void endVisit(VariableStatement *) {}
+
+ virtual bool visit(VariableDeclarationList *) { return true; }
+ virtual void endVisit(VariableDeclarationList *) {}
+
+ virtual bool visit(VariableDeclaration *) { return true; }
+ virtual void endVisit(VariableDeclaration *) {}
+
+ virtual bool visit(EmptyStatement *) { return true; }
+ virtual void endVisit(EmptyStatement *) {}
+
+ virtual bool visit(ExpressionStatement *) { return true; }
+ virtual void endVisit(ExpressionStatement *) {}
+
+ virtual bool visit(IfStatement *) { return true; }
+ virtual void endVisit(IfStatement *) {}
+
+ virtual bool visit(DoWhileStatement *) { return true; }
+ virtual void endVisit(DoWhileStatement *) {}
+
+ virtual bool visit(WhileStatement *) { return true; }
+ virtual void endVisit(WhileStatement *) {}
+
+ virtual bool visit(ForStatement *) { return true; }
+ virtual void endVisit(ForStatement *) {}
+
+ virtual bool visit(LocalForStatement *) { return true; }
+ virtual void endVisit(LocalForStatement *) {}
+
+ virtual bool visit(ForEachStatement *) { return true; }
+ virtual void endVisit(ForEachStatement *) {}
+
+ virtual bool visit(LocalForEachStatement *) { return true; }
+ virtual void endVisit(LocalForEachStatement *) {}
+
+ virtual bool visit(ContinueStatement *) { return true; }
+ virtual void endVisit(ContinueStatement *) {}
+
+ virtual bool visit(BreakStatement *) { return true; }
+ virtual void endVisit(BreakStatement *) {}
+
+ virtual bool visit(ReturnStatement *) { return true; }
+ virtual void endVisit(ReturnStatement *) {}
+
+ virtual bool visit(WithStatement *) { return true; }
+ virtual void endVisit(WithStatement *) {}
+
+ virtual bool visit(SwitchStatement *) { return true; }
+ virtual void endVisit(SwitchStatement *) {}
+
+ virtual bool visit(CaseBlock *) { return true; }
+ virtual void endVisit(CaseBlock *) {}
+
+ virtual bool visit(CaseClauses *) { return true; }
+ virtual void endVisit(CaseClauses *) {}
+
+ virtual bool visit(CaseClause *) { return true; }
+ virtual void endVisit(CaseClause *) {}
+
+ virtual bool visit(DefaultClause *) { return true; }
+ virtual void endVisit(DefaultClause *) {}
+
+ virtual bool visit(LabelledStatement *) { return true; }
+ virtual void endVisit(LabelledStatement *) {}
+
+ virtual bool visit(ThrowStatement *) { return true; }
+ virtual void endVisit(ThrowStatement *) {}
+
+ virtual bool visit(TryStatement *) { return true; }
+ virtual void endVisit(TryStatement *) {}
+
+ virtual bool visit(Catch *) { return true; }
+ virtual void endVisit(Catch *) {}
+
+ virtual bool visit(Finally *) { return true; }
+ virtual void endVisit(Finally *) {}
+
+ virtual bool visit(FunctionDeclaration *) { return true; }
+ virtual void endVisit(FunctionDeclaration *) {}
+
+ virtual bool visit(FunctionExpression *) { return true; }
+ virtual void endVisit(FunctionExpression *) {}
+
+ virtual bool visit(FormalParameterList *) { return true; }
+ virtual void endVisit(FormalParameterList *) {}
+
+ virtual bool visit(FunctionBody *) { return true; }
+ virtual void endVisit(FunctionBody *) {}
+
+ virtual bool visit(Program *) { return true; }
+ virtual void endVisit(Program *) {}
+
+ virtual bool visit(SourceElements *) { return true; }
+ virtual void endVisit(SourceElements *) {}
+
+ virtual bool visit(FunctionSourceElement *) { return true; }
+ virtual void endVisit(FunctionSourceElement *) {}
+
+ virtual bool visit(StatementSourceElement *) { return true; }
+ virtual void endVisit(StatementSourceElement *) {}
+
+ virtual bool visit(DebuggerStatement *) { return true; }
+ virtual void endVisit(DebuggerStatement *) {}
+};
+
+} // namespace AST
+} // namespace QbsQmlJS
+
+#endif // QMLJSASTVISITOR_P_H
diff --git a/src/lib/corelib/parser/qmljsengine_p.cpp b/src/lib/corelib/parser/qmljsengine_p.cpp
new file mode 100644
index 000000000..410de608f
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsengine_p.cpp
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 "qmljsengine_p.h"
+#include "qmljsglobal_p.h"
+
+#include <qnumeric.h>
+#include <QHash>
+#include <QDebug>
+
+namespace QbsQmlJS {
+
+static int toDigit(char c)
+{
+ if ((c >= '0') && (c <= '9'))
+ return c - '0';
+ else if ((c >= 'a') && (c <= 'z'))
+ return 10 + c - 'a';
+ else if ((c >= 'A') && (c <= 'Z'))
+ return 10 + c - 'A';
+ return -1;
+}
+
+double integerFromString(const char *buf, int size, int radix)
+{
+ if (size == 0)
+ return qSNaN();
+
+ double sign = 1.0;
+ int i = 0;
+ if (buf[0] == '+') {
+ ++i;
+ } else if (buf[0] == '-') {
+ sign = -1.0;
+ ++i;
+ }
+
+ if (((size-i) >= 2) && (buf[i] == '0')) {
+ if (((buf[i+1] == 'x') || (buf[i+1] == 'X'))
+ && (radix < 34)) {
+ if ((radix != 0) && (radix != 16))
+ return 0;
+ radix = 16;
+ i += 2;
+ } else {
+ if (radix == 0) {
+ radix = 8;
+ ++i;
+ }
+ }
+ } else if (radix == 0) {
+ radix = 10;
+ }
+
+ int j = i;
+ for ( ; i < size; ++i) {
+ int d = toDigit(buf[i]);
+ if ((d == -1) || (d >= radix))
+ break;
+ }
+ double result;
+ if (j == i) {
+ if (!qstrcmp(buf, "Infinity"))
+ result = qInf();
+ else
+ result = qSNaN();
+ } else {
+ result = 0;
+ double multiplier = 1;
+ for (--i ; i >= j; --i, multiplier *= radix)
+ result += toDigit(buf[i]) * multiplier;
+ }
+ result *= sign;
+ return result;
+}
+
+double integerFromString(const QString &str, int radix)
+{
+ QByteArray ba = str.trimmed().toLatin1();
+ return integerFromString(ba.constData(), ba.size(), radix);
+}
+
+
+Engine::Engine()
+ : _lexer(0), _directives(0)
+{ }
+
+Engine::~Engine()
+{ }
+
+void Engine::setCode(const QString &code)
+{ _code = code; }
+
+void Engine::addComment(int pos, int len, int line, int col)
+{ if (len > 0) _comments.append(QbsQmlJS::AST::SourceLocation(pos, len, line, col)); }
+
+QList<QbsQmlJS::AST::SourceLocation> Engine::comments() const
+{ return _comments; }
+
+Lexer *Engine::lexer() const
+{ return _lexer; }
+
+void Engine::setLexer(Lexer *lexer)
+{ _lexer = lexer; }
+
+void Engine::setDirectives(Directives *directives)
+{ _directives = directives; }
+
+Directives *Engine::directives() const
+{ return _directives; }
+
+MemoryPool *Engine::pool()
+{ return &_pool; }
+
+QStringRef Engine::newStringRef(const QString &text)
+{
+ const int pos = _extraCode.length();
+ _extraCode += text;
+ return _extraCode.midRef(pos, text.length());
+}
+
+QStringRef Engine::newStringRef(const QChar *chars, int size)
+{ return newStringRef(QString(chars, size)); }
+
+} // end of namespace QbsQmlJS
diff --git a/src/lib/corelib/parser/qmljsengine_p.h b/src/lib/corelib/parser/qmljsengine_p.h
new file mode 100644
index 000000000..6965e5c20
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsengine_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QMLJSENGINE_P_H
+#define QMLJSENGINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmljsglobal_p.h"
+#include "qmljsastfwd_p.h"
+#include "qmljsmemorypool_p.h"
+
+#include <QString>
+#include <QSet>
+
+namespace QbsQmlJS {
+
+class Lexer;
+class Directives;
+class MemoryPool;
+
+class QML_PARSER_EXPORT DiagnosticMessage
+{
+public:
+ enum Kind { Warning, Error };
+
+ DiagnosticMessage()
+ : kind(Error) {}
+
+ DiagnosticMessage(Kind kind, const AST::SourceLocation &loc, const QString &message)
+ : kind(kind), loc(loc), message(message) {}
+
+ bool isWarning() const
+ { return kind == Warning; }
+
+ bool isError() const
+ { return kind == Error; }
+
+ Kind kind;
+ AST::SourceLocation loc;
+ QString message;
+};
+
+class QML_PARSER_EXPORT Engine
+{
+ Lexer *_lexer;
+ Directives *_directives;
+ MemoryPool _pool;
+ QList<AST::SourceLocation> _comments;
+ QString _extraCode;
+ QString _code;
+
+public:
+ Engine();
+ ~Engine();
+
+ void setCode(const QString &code);
+
+ void addComment(int pos, int len, int line, int col);
+ QList<AST::SourceLocation> comments() const;
+
+ Lexer *lexer() const;
+ void setLexer(Lexer *lexer);
+
+ void setDirectives(Directives *directives);
+ Directives *directives() const;
+
+ MemoryPool *pool();
+
+ inline QStringRef midRef(int position, int size) { return _code.midRef(position, size); }
+
+ QStringRef newStringRef(const QString &s);
+ QStringRef newStringRef(const QChar *chars, int size);
+};
+
+double integerFromString(const char *buf, int size, int radix);
+
+} // end of namespace QbsQmlJS
+
+#endif // QMLJSENGINE_P_H
diff --git a/src/lib/corelib/parser/qmljsglobal_p.h b/src/lib/corelib/parser/qmljsglobal_p.h
new file mode 100644
index 000000000..82b7f092e
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsglobal_p.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QMLJSGLOBAL_P_H
+#define QMLJSGLOBAL_P_H
+
+#include <QtCore/qglobal.h>
+
+#ifdef QT_CREATOR
+# ifdef QMLJS_BUILD_DIR
+# define QML_PARSER_EXPORT Q_DECL_EXPORT
+# elif QML_BUILD_STATIC_LIB
+# define QML_PARSER_EXPORT
+# else
+# define QML_PARSER_EXPORT Q_DECL_IMPORT
+# endif // QMLJS_BUILD_DIR
+
+#else // !QT_CREATOR
+# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB)
+ // QmlDevTools is a static library
+# define QML_PARSER_EXPORT
+# else
+# define QML_PARSER_EXPORT Q_AUTOTEST_EXPORT
+# endif
+#endif // QT_CREATOR
+
+#endif // QMLJSGLOBAL_P_H
diff --git a/src/lib/corelib/parser/qmljsgrammar.cpp b/src/lib/corelib/parser/qmljsgrammar.cpp
new file mode 100644
index 000000000..534519fad
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsgrammar.cpp
@@ -0,0 +1,1001 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+// This file was generated by qlalr - DO NOT EDIT!
+#include "qmljsgrammar_p.h"
+
+namespace QbsQmlJS {
+
+const char *const QmlJSGrammar::spell [] = {
+ "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ",", "continue",
+ "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===",
+ "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier",
+ "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=",
+ "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=",
+ "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return",
+ ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch",
+ "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^",
+ "^=", "null", "true", "false", "const", "debugger", "reserved word", "multiline string literal", "comment", "public",
+ "import", "as", "on", 0, 0, 0, 0, 0, 0, 0,
+ 0, 0};
+
+const short QmlJSGrammar::lhs [] = {
+ 102, 102, 102, 102, 102, 102, 103, 109, 109, 112,
+ 112, 114, 113, 113, 113, 113, 113, 113, 113, 113,
+ 116, 111, 110, 119, 119, 120, 120, 121, 121, 118,
+ 107, 107, 107, 107, 123, 123, 123, 123, 123, 123,
+ 123, 107, 131, 131, 131, 132, 132, 133, 133, 107,
+ 107, 107, 107, 107, 107, 107, 107, 107, 107, 107,
+ 107, 107, 107, 107, 107, 107, 117, 117, 117, 117,
+ 117, 136, 136, 136, 136, 136, 136, 136, 136, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 122,
+ 138, 138, 138, 138, 137, 137, 140, 140, 142, 142,
+ 142, 142, 142, 142, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 144, 144, 115, 115, 115,
+ 115, 115, 147, 147, 148, 148, 148, 148, 146, 146,
+ 149, 149, 150, 150, 151, 151, 151, 152, 152, 152,
+ 152, 152, 152, 152, 152, 152, 152, 153, 153, 153,
+ 153, 154, 154, 154, 155, 155, 155, 155, 156, 156,
+ 156, 156, 156, 156, 156, 157, 157, 157, 157, 157,
+ 157, 158, 158, 158, 158, 158, 159, 159, 159, 159,
+ 159, 160, 160, 161, 161, 162, 162, 163, 163, 164,
+ 164, 165, 165, 166, 166, 167, 167, 168, 168, 169,
+ 169, 170, 170, 171, 171, 141, 141, 172, 172, 173,
+ 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
+ 173, 105, 105, 174, 174, 175, 175, 176, 176, 104,
+ 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,
+ 104, 104, 104, 104, 124, 185, 185, 184, 184, 135,
+ 135, 186, 186, 187, 187, 189, 189, 188, 190, 193,
+ 191, 191, 194, 192, 192, 125, 126, 126, 127, 127,
+ 177, 177, 177, 177, 177, 177, 177, 178, 178, 178,
+ 178, 179, 179, 179, 179, 180, 180, 128, 129, 195,
+ 195, 198, 198, 196, 196, 199, 197, 181, 181, 181,
+ 182, 182, 130, 130, 130, 200, 201, 183, 183, 134,
+ 145, 205, 205, 202, 202, 203, 203, 206, 108, 108,
+ 207, 207, 106, 106, 204, 204, 139, 139, 208};
+
+const short QmlJSGrammar::rhs [] = {
+ 2, 2, 2, 2, 2, 2, 2, 1, 1, 1,
+ 2, 1, 2, 2, 3, 3, 5, 5, 4, 4,
+ 2, 0, 1, 1, 2, 1, 3, 2, 3, 2,
+ 1, 5, 4, 4, 1, 1, 1, 1, 1, 1,
+ 1, 3, 1, 1, 1, 0, 1, 2, 4, 6,
+ 6, 3, 3, 7, 7, 4, 4, 5, 5, 5,
+ 6, 6, 10, 6, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 3, 3, 4, 5, 3, 4, 3, 1,
+ 1, 2, 3, 4, 1, 2, 3, 5, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,
+ 3, 5, 1, 2, 4, 4, 4, 3, 0, 1,
+ 1, 3, 1, 1, 1, 2, 2, 1, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 1, 3, 3,
+ 3, 1, 3, 3, 1, 3, 3, 3, 1, 3,
+ 3, 3, 3, 3, 3, 1, 3, 3, 3, 3,
+ 3, 1, 3, 3, 3, 3, 1, 3, 3, 3,
+ 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
+ 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
+ 3, 1, 5, 1, 5, 1, 3, 1, 3, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 3, 0, 1, 1, 3, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 3, 1, 2, 0, 1, 3,
+ 3, 1, 1, 1, 3, 1, 3, 2, 2, 2,
+ 0, 1, 2, 0, 1, 1, 2, 2, 7, 5,
+ 7, 7, 5, 9, 10, 7, 8, 2, 2, 3,
+ 3, 2, 2, 3, 3, 3, 3, 5, 5, 3,
+ 5, 1, 2, 0, 1, 4, 3, 3, 3, 3,
+ 3, 3, 3, 3, 4, 5, 2, 2, 2, 8,
+ 8, 1, 3, 0, 1, 0, 1, 1, 1, 1,
+ 1, 2, 1, 1, 0, 1, 0, 1, 2};
+
+const short QmlJSGrammar::action_default [] = {
+ 0, 0, 22, 0, 0, 0, 22, 0, 175, 242,
+ 206, 214, 210, 154, 226, 202, 3, 139, 73, 155,
+ 218, 222, 143, 172, 153, 158, 138, 192, 179, 0,
+ 80, 81, 76, 345, 67, 347, 0, 0, 0, 0,
+ 78, 0, 0, 74, 77, 71, 0, 0, 68, 70,
+ 69, 79, 72, 0, 75, 0, 0, 168, 0, 0,
+ 155, 174, 157, 156, 0, 0, 0, 170, 171, 169,
+ 173, 0, 203, 0, 0, 0, 0, 193, 0, 0,
+ 0, 0, 0, 0, 183, 0, 0, 0, 177, 178,
+ 176, 181, 185, 184, 182, 180, 195, 194, 196, 0,
+ 211, 0, 207, 0, 0, 149, 136, 148, 137, 105,
+ 106, 107, 132, 108, 133, 109, 110, 111, 112, 113,
+ 114, 115, 116, 117, 118, 119, 120, 121, 134, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 135,
+ 0, 0, 147, 243, 150, 0, 151, 0, 152, 146,
+ 0, 239, 232, 230, 237, 238, 236, 235, 241, 234,
+ 233, 231, 240, 227, 0, 215, 0, 0, 219, 0,
+ 0, 223, 0, 0, 149, 141, 0, 140, 0, 145,
+ 159, 0, 346, 334, 335, 0, 332, 0, 333, 0,
+ 336, 250, 257, 256, 264, 252, 0, 253, 337, 0,
+ 344, 254, 255, 260, 258, 341, 338, 343, 261, 0,
+ 272, 0, 0, 0, 0, 345, 67, 0, 347, 68,
+ 244, 286, 69, 0, 0, 0, 273, 0, 0, 262,
+ 263, 0, 251, 259, 287, 288, 331, 342, 0, 302,
+ 303, 304, 305, 0, 298, 299, 300, 301, 328, 329,
+ 0, 0, 0, 0, 0, 291, 292, 248, 246, 208,
+ 216, 212, 228, 204, 249, 0, 155, 220, 224, 197,
+ 186, 0, 0, 205, 0, 0, 0, 0, 198, 0,
+ 0, 0, 0, 0, 190, 188, 191, 189, 187, 200,
+ 199, 201, 0, 213, 0, 209, 0, 247, 155, 0,
+ 229, 244, 245, 0, 244, 0, 0, 294, 0, 0,
+ 0, 296, 0, 217, 0, 0, 221, 0, 0, 225,
+ 284, 0, 276, 285, 279, 0, 283, 0, 244, 277,
+ 0, 244, 0, 0, 295, 0, 0, 0, 297, 346,
+ 334, 0, 0, 336, 0, 330, 0, 320, 0, 0,
+ 0, 290, 0, 289, 0, 348, 0, 104, 266, 269,
+ 0, 105, 272, 108, 133, 110, 111, 76, 115, 116,
+ 67, 117, 120, 74, 77, 68, 244, 69, 79, 123,
+ 72, 125, 75, 127, 128, 273, 130, 131, 135, 0,
+ 97, 0, 0, 99, 103, 101, 88, 100, 102, 0,
+ 98, 87, 267, 265, 143, 144, 149, 0, 142, 0,
+ 319, 0, 306, 307, 0, 318, 0, 0, 0, 309,
+ 314, 312, 315, 0, 0, 313, 314, 0, 310, 0,
+ 311, 268, 317, 0, 268, 316, 0, 321, 322, 0,
+ 268, 323, 324, 0, 0, 325, 0, 0, 0, 326,
+ 327, 161, 160, 0, 0, 0, 293, 0, 0, 0,
+ 308, 281, 274, 0, 282, 278, 0, 280, 270, 0,
+ 271, 275, 91, 0, 0, 95, 82, 0, 84, 93,
+ 0, 85, 94, 96, 86, 92, 83, 0, 89, 165,
+ 163, 167, 164, 162, 166, 339, 6, 340, 4, 2,
+ 65, 90, 0, 0, 68, 70, 69, 31, 5, 0,
+ 66, 0, 45, 44, 43, 0, 0, 58, 0, 59,
+ 35, 36, 37, 38, 40, 41, 62, 39, 0, 45,
+ 0, 0, 0, 0, 0, 54, 0, 55, 0, 0,
+ 26, 0, 0, 63, 27, 0, 30, 28, 24, 0,
+ 29, 25, 0, 56, 0, 57, 143, 0, 60, 64,
+ 0, 0, 0, 0, 61, 0, 52, 46, 53, 47,
+ 0, 0, 0, 0, 49, 0, 50, 51, 48, 0,
+ 0, 143, 268, 0, 0, 42, 105, 272, 108, 133,
+ 110, 111, 76, 115, 116, 67, 117, 120, 74, 77,
+ 68, 244, 69, 79, 123, 72, 125, 75, 127, 128,
+ 273, 130, 131, 135, 0, 32, 33, 0, 34, 8,
+ 0, 10, 0, 9, 0, 1, 21, 12, 0, 13,
+ 0, 14, 0, 19, 20, 0, 15, 16, 0, 17,
+ 18, 11, 23, 7, 349};
+
+const short QmlJSGrammar::goto_default [] = {
+ 7, 625, 207, 196, 205, 508, 496, 624, 643, 495,
+ 623, 621, 626, 22, 622, 18, 507, 549, 539, 546,
+ 541, 526, 191, 195, 197, 201, 233, 208, 230, 530,
+ 570, 569, 200, 232, 26, 474, 473, 356, 355, 9,
+ 354, 357, 107, 17, 145, 24, 13, 144, 19, 25,
+ 57, 23, 8, 28, 27, 269, 15, 263, 10, 259,
+ 12, 261, 11, 260, 20, 267, 21, 268, 14, 262,
+ 258, 299, 411, 264, 265, 202, 193, 192, 204, 203,
+ 229, 194, 360, 359, 231, 463, 462, 321, 322, 465,
+ 324, 464, 323, 419, 423, 426, 422, 421, 441, 442,
+ 185, 199, 181, 184, 198, 206, 0};
+
+const short QmlJSGrammar::action_index [] = {
+ 404, 1275, 2411, 2411, 2509, 1000, 68, 92, 90, -102,
+ 88, 62, 60, 256, -102, 298, 86, -102, -102, 638,
+ 83, 134, 172, 219, -102, -102, -102, 454, 194, 1275,
+ -102, -102, -102, 381, -102, 2215, 1555, 1275, 1275, 1275,
+ -102, 790, 1275, -102, -102, -102, 1275, 1275, -102, -102,
+ -102, -102, -102, 1275, -102, 1275, 1275, -102, 1275, 1275,
+ 102, 217, -102, -102, 1275, 1275, 1275, -102, -102, -102,
+ 204, 1275, 304, 1275, 1275, 1275, 1275, 539, 1275, 1275,
+ 1275, 1275, 1275, 1275, 308, 1275, 1275, 1275, 103, 131,
+ 135, 308, 210, 225, 216, 308, 444, 390, 434, 1275,
+ 82, 1275, 100, 2117, 1275, 1275, -102, -102, -102, -102,
+ -102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
+ -102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
+ -102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
+ 139, 1275, -102, -102, 91, 10, -102, 1275, -102, -102,
+ 1275, -102, -102, -102, -102, -102, -102, -102, -102, -102,
+ -102, -102, -102, -102, 1275, 26, 1275, 1275, 69, 66,
+ 1275, -102, 2117, 1275, 1275, -102, 97, -102, 44, -102,
+ -102, 67, -102, 297, 78, 24, -102, 291, -102, 36,
+ 2411, -102, -102, -102, -102, -102, 234, -102, -102, 12,
+ -102, -102, -102, -102, -102, -102, 2411, -102, -102, 464,
+ -102, 461, 115, 2509, 42, 381, 58, 46, 2705, 70,
+ 1275, -102, 74, 57, 1275, 65, -102, 59, 61, -102,
+ -102, 367, -102, -102, -102, -102, -102, -102, 106, -102,
+ -102, -102, -102, 87, -102, -102, -102, -102, -102, -102,
+ 56, 55, 1275, 99, 84, -102, -102, 1461, -102, 75,
+ 48, 52, -102, 306, 72, 53, 579, 77, 110, 370,
+ 230, 381, 1275, 286, 1275, 1275, 1275, 1275, 380, 1275,
+ 1275, 1275, 1275, 1275, 184, 169, 166, 190, 198, 460,
+ 363, 353, 1275, 50, 1275, 63, 1275, -102, 638, 1275,
+ -102, 1275, 64, 39, 1275, 30, 2509, -102, 1275, 173,
+ 2509, -102, 1275, 79, 1275, 1275, 81, 80, 1275, -102,
+ 71, 149, 32, -102, -102, 1275, -102, 381, 1275, -102,
+ 73, 1275, 76, 2509, -102, 1275, 142, 2509, -102, -16,
+ 381, -42, -12, 2411, -39, -102, 2509, -102, 1275, 154,
+ 2509, 14, 2509, -102, 20, 16, -32, -102, -102, 2509,
+ -51, 519, -4, 511, 136, 1275, 2509, -2, -35, 395,
+ -1, -27, 908, 4, 6, -102, 1370, -102, 0, -36,
+ 27, 1275, 47, 22, 1275, 45, 1275, 21, 17, 1275,
+ -102, 2313, 144, -102, -102, -102, -102, -102, -102, 1275,
+ -102, -102, -102, -102, 274, -102, 1275, -21, -102, 2509,
+ -102, 138, -102, -102, 2509, -102, 1275, 132, 5, -102,
+ 40, -102, 41, 101, 1275, -102, 38, 34, -102, -38,
+ -102, 2509, -102, 105, 2509, -102, 245, -102, -102, 96,
+ 2509, 11, -102, -7, -11, -102, 352, 8, 18, -102,
+ -102, -102, -102, 1275, 129, 2509, -102, 1275, 130, 2509,
+ -102, 49, -102, 226, -102, -102, 1275, -102, -102, 362,
+ -102, -102, -102, 107, 1837, -102, -102, 1649, -102, -102,
+ 1743, -102, -102, -102, -102, -102, -102, 114, -102, -102,
+ -102, -102, -102, -102, -102, -102, -102, 2411, -102, -102,
+ -102, 94, 9, 818, 189, -10, 31, -102, -102, 223,
+ -102, 191, -102, -102, -102, 300, 178, -102, 1928, -102,
+ -102, -102, -102, -102, -102, -102, -102, -102, 257, -25,
+ 381, 195, -22, 305, 240, -102, -6, -102, 818, 127,
+ -102, -18, 818, -102, -102, 1184, -102, -102, -102, 1092,
+ -102, -102, 237, -102, 1928, -102, 294, -8, -102, -102,
+ 176, 381, 19, 1928, -102, 165, -102, 174, -102, 2,
+ -52, 381, 183, 381, -102, 117, -102, -102, -102, 2019,
+ 880, 285, 2607, 1555, 3, -102, 522, 35, 453, 108,
+ 1275, 2509, 51, 23, 475, 54, -17, 700, 7, 43,
+ -102, 1370, -102, 28, -3, 33, 1275, 37, 15, 1275,
+ 25, 1275, 1, 13, 124, -102, -102, 29, -102, -102,
+ 728, -102, 250, -43, 627, -102, -102, 231, 372, -102,
+ 222, -102, 111, -102, -102, 381, -102, -102, 104, -102,
+ -102, -102, -102, -102, -102,
+
+ -107, 9, -103, 2, 5, 266, 1, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -39,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, 86,
+ -107, -107, -107, 8, -107, -107, -22, 19, 71, 174,
+ -107, 186, 171, -107, -107, -107, 184, 178, -107, -107,
+ -107, -107, -107, 144, -107, 124, 150, -107, 165, 161,
+ -107, -107, -107, -107, 156, 160, 157, -107, -107, -107,
+ -107, 147, -107, 142, 135, 179, 166, -107, 177, 170,
+ 117, 72, 134, 92, -107, 75, 94, 66, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, 181,
+ -107, 106, -107, 143, 78, 55, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -5, -107, -107, -107, -107, -107, 54, -107, -107,
+ 51, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, 114, -107, 113, 38, -107, -107,
+ 41, -107, 231, 63, 112, -107, -107, -107, -107, -107,
+ -107, -107, -107, 30, -107, -107, -107, 52, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, 36, -107, -107, 45,
+ -107, 42, -107, 40, -107, 80, -107, -107, 77, -107,
+ 88, -107, -107, -107, 83, 74, -107, -107, -107, -107,
+ -107, -10, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, 23, -107, -107, -107, -107, 100, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, 4, 223, -107, 230, 236, 222, 205, -107, 127,
+ 125, 115, 96, 102, -107, -107, -107, -107, -107, -107,
+ -107, -107, 234, -107, 215, -107, 199, -107, -107, 197,
+ -107, 190, -107, -107, 163, -107, 90, -107, 0, -107,
+ -1, -107, 203, -107, 189, 211, -107, -107, 195, -107,
+ -107, -107, -107, -107, -107, 191, -107, 98, 119, -107,
+ -107, 95, -107, 81, -107, 79, -107, 82, -107, -107,
+ 101, -107, -107, -16, -107, -107, 53, -107, 46, -107,
+ 57, -107, 59, -107, -107, -107, -107, -107, -107, 35,
+ -107, 33, -107, 39, -107, 89, 67, -107, -107, 58,
+ -107, -107, 84, -107, -107, -107, 73, -107, -107, -107,
+ -107, 65, -107, 43, 93, -107, 109, -107, -107, 49,
+ -107, 47, -107, -107, -107, -107, -107, -107, -107, 50,
+ -107, -107, -107, -107, -107, -107, 108, -107, -107, 61,
+ -107, -107, -107, -107, 62, -107, 68, -107, -107, -107,
+ -107, -107, -23, -107, 69, -107, -19, -107, -107, -107,
+ -107, 97, -107, -107, 99, -107, -107, -107, -107, -107,
+ 60, -61, -107, -107, 34, -107, 37, -107, 29, -107,
+ -107, -107, -107, 32, -107, 76, -107, 44, -107, 56,
+ -107, -107, -107, -107, -107, -107, 31, -107, -107, 116,
+ -107, -107, -107, -107, -6, -107, -107, 70, -107, -107,
+ 64, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, 193, -107, -107,
+ -107, -107, -107, 7, -107, -107, -107, -107, -107, -107,
+ -107, -20, -107, -107, -107, -7, -107, -107, 290, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -2, -25, -107, -15, -107, -107, -107, -107, 172, -107,
+ -107, -107, 287, -107, -107, 288, -107, -107, -107, 291,
+ -107, -107, -107, -107, 336, -107, -107, 20, -107, -107,
+ 15, 3, -107, 304, -107, -107, -107, 24, -107, -107,
+ -107, 28, 21, 26, -107, -107, -107, -107, -107, 320,
+ 104, -107, 13, 381, -3, -107, 6, -107, 10, -107,
+ 167, 22, -107, -107, 12, -107, -107, 87, -107, -107,
+ -107, 25, -107, -107, -107, -107, 11, -107, 14, 85,
+ -107, 121, -107, -107, -107, -107, -107, 27, -107, -107,
+ 17, -107, -107, 18, 91, -107, -107, -107, 16, -107,
+ -107, -107, -107, -107, -107, -4, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107};
+
+const short QmlJSGrammar::action_info [] = {
+ 416, 257, 533, -132, 403, -113, 346, -102, 575, 348,
+ 572, -121, 531, -103, -121, 545, 345, 430, 342, 348,
+ 340, 343, 440, 401, 391, 545, 563, 389, 538, 446,
+ 352, 444, -129, 416, -124, -102, 545, 453, 420, 408,
+ -124, 431, -132, 424, -126, 424, 424, 620, 440, 457,
+ -103, 440, -129, 457, -126, 440, 560, 453, -113, 257,
+ 565, 346, 545, 335, 272, 346, 466, 236, 448, 190,
+ 149, 164, 141, 170, 99, 511, 272, 409, 257, 312,
+ 296, 414, 348, 312, 189, 164, 187, 318, 325, 71,
+ 306, 252, 644, 416, 141, 453, 292, 457, 440, 147,
+ 304, 71, 443, 183, 179, 141, 0, 141, 0, 172,
+ 99, 427, 434, 141, 301, 477, 444, 0, 0, 0,
+ 0, 0, 141, 0, 0, 0, 0, 292, 173, 294,
+ 58, 294, 542, 251, 331, 542, 333, 141, 141, 101,
+ 141, 59, 0, 58, 62, 256, 255, 141, 247, 246,
+ 141, 399, 0, 177, 59, 63, 428, 327, 620, 254,
+ 314, 101, 141, 478, 315, 640, 639, 242, 241, 249,
+ 248, 58, 634, 633, 488, 58, 249, 248, 577, 576,
+ 615, 141, 59, 543, 166, 518, 59, 172, 167, 455,
+ 459, 85, 418, 86, 85, 142, 86, 249, 248, 413,
+ 412, 567, 337, 512, 87, 512, 173, 87, 174, 85,
+ 328, 86, 512, 0, 350, 85, 64, 86, 529, 85,
+ 512, 86, 87, 85, 512, 86, 568, 566, 87, 64,
+ 579, 64, 87, 310, 469, 85, 87, 86, 0, 519,
+ 517, 85, 141, 86, 554, 0, 172, 536, 87, 514,
+ 85, 514, 86, 141, 87, 85, 545, 86, 514, 0,
+ 513, 65, 513, 87, 514, 173, 514, 66, 87, 513,
+ 514, 103, 172, 0, 65, 513, 65, 513, 0, 0,
+ 66, 513, 66, 637, 636, 0, 0, 470, 468, 172,
+ 104, 173, 105, 406, 0, 235, 234, 630, 555, 553,
+ 172, 537, 535, 0, 274, 275, 438, 437, 173, 172,
+ 406, 631, 629, 635, 0, 580, 73, 74, -90, 173,
+ 34, 174, 73, 74, 274, 275, 34, -90, 173, 34,
+ 174, 276, 277, 85, 34, 86, 0, 0, 0, 0,
+ 0, 628, 0, 75, 76, 0, 87, 0, 0, 75,
+ 76, 276, 277, 0, 0, 0, 0, 48, 50, 49,
+ 0, 0, 0, 48, 50, 49, 48, 50, 49, 0,
+ 0, 48, 50, 49, 0, 0, 279, 280, 0, 0,
+ 0, 34, 0, 45, 0, 281, 279, 280, 282, 45,
+ 283, 34, 45, 279, 280, 281, 34, 45, 282, 0,
+ 283, 34, 281, 279, 280, 282, 0, 283, 0, 0,
+ 34, 0, 281, 78, 79, 282, 0, 283, 48, 50,
+ 49, 80, 81, 0, 34, 82, 0, 83, 48, 50,
+ 49, -345, 0, 48, 50, 49, 0, 0, 48, 50,
+ 49, 0, 0, 0, 45, 0, 0, 48, 50, 49,
+ 0, 0, 0, 0, 45, 0, 0, 78, 79, 45,
+ 0, 48, 50, 49, 45, 80, 81, 78, 79, 82,
+ 0, 83, 0, 45, 0, 80, 81, 78, 79, 82,
+ 0, 83, 34, 279, 280, 80, 81, 45, 0, 82,
+ 34, 83, 281, 34, 0, 282, 0, 283, 6, 5,
+ 4, 1, 3, 2, 34, 0, 0, 0, 0, 0,
+ 0, -345, 0, 0, 245, 244, 0, 0, 0, 48,
+ 50, 49, 245, 244, 0, 240, 239, 48, 50, 49,
+ 48, 50, 49, 0, 0, 0, 0, 0, 0, 0,
+ 34, 48, 50, 49, 0, 45, 0, 0, 34, 0,
+ 0, 34, 0, 45, 0, 0, 45, 0, 0, 0,
+ 0, 0, 78, 79, 0, 0, 0, 45, 0, 0,
+ 80, 81, 245, 244, 82, 0, 83, 48, 50, 49,
+ 240, 239, 151, 240, 239, 48, 50, 49, 48, 50,
+ 49, 0, 152, 0, 0, 0, 153, 0, 0, 0,
+ 0, 0, 0, 45, 0, 154, 0, 155, 0, 0,
+ 308, 45, 0, 0, 45, 0, 0, 0, 156, 0,
+ 157, 62, 0, 0, 0, 0, 0, 0, 158, 0,
+ 0, 159, 63, 0, 0, 0, 0, 160, 0, 30,
+ 31, 151, 0, 161, 0, 0, 0, 0, 0, 33,
+ 0, 152, 0, 0, 0, 153, 34, 0, 0, 162,
+ 35, 36, 0, 37, 154, 0, 155, 0, 0, 0,
+ 503, 0, 0, 0, 44, 0, 0, 156, 0, 157,
+ 62, 0, 0, 0, 0, 0, 0, 158, 0, 0,
+ 159, 63, 51, 48, 50, 49, 160, 52, 0, 0,
+ 0, 0, 161, 0, 0, 0, 0, 0, 43, 54,
+ 32, 0, 30, 31, 40, 0, 0, 0, 162, 45,
+ 0, 0, 33, 0, 0, 0, 0, 0, 0, 34,
+ 0, 0, 0, 35, 36, 0, 37, 0, 0, 0,
+ 30, 31, 0, 41, 0, 0, 0, 44, 0, 0,
+ 33, 0, 0, 0, 0, 0, 0, 34, 0, 0,
+ 0, 35, 36, 0, 37, 51, 48, 50, 49, 0,
+ 52, 503, 0, 0, 0, 44, 0, 0, 0, 0,
+ 0, 43, 54, 32, 0, 0, 0, 40, 0, 0,
+ 0, 0, 45, 51, 48, 50, 49, 0, 52, 0,
+ 0, 0, 30, 31, 0, 0, 0, 0, 0, 43,
+ 54, 32, 33, 0, 0, 40, 0, 0, 0, 34,
+ 45, 0, 0, 35, 36, 0, 37, 0, 0, 0,
+ 30, 31, 0, 41, 0, 0, 0, 44, 0, 0,
+ 33, 0, 0, 0, 0, 0, 0, 34, 0, 0,
+ 0, 35, 36, 0, 37, 51, 48, 50, 49, 0,
+ 52, 503, 0, 0, 0, 44, 0, 0, 0, 0,
+ 0, 43, 54, 32, 0, 0, 0, 40, 0, 0,
+ 0, 0, 45, 51, 48, 50, 49, 0, 52, 0,
+ 0, 0, 30, 31, 0, 0, 0, 0, 0, 43,
+ 54, 32, 33, 0, 0, 40, 0, 0, 0, 34,
+ 45, 0, 0, 35, 36, 0, 37, 0, 0, 0,
+ 30, 31, 0, 503, 0, 0, 0, 44, 0, 0,
+ 33, 0, 0, 0, 0, 0, 0, 34, 0, 0,
+ 0, 35, 36, 0, 37, 51, 48, 50, 49, 0,
+ 52, 41, 0, 0, 0, 44, 0, 0, 0, 0,
+ 0, 43, 54, 32, 0, 0, 0, 40, 0, 0,
+ 0, 0, 45, 51, 48, 50, 49, 0, 52, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 43,
+ 54, 32, 0, 0, 0, 40, 0, 0, 0, 0,
+ 45, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 502, 0, 30, 31, 0, 0, 0, 0, 0, 0,
+ 0, 0, 215, 0, 0, 0, 0, 0, 0, 34,
+ 0, 0, 0, 35, 36, 0, 37, 0, 0, 0,
+ 0, 0, 0, 503, 0, 0, 0, 44, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 51, 504, 506, 505, 0,
+ 52, 0, 0, 0, 0, 226, 0, 0, 0, 0,
+ 0, 43, 54, 32, 210, 0, 0, 40, 0, 0,
+ 0, 0, 45, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 502, 0, 30, 31, 0, 0, 0, 0,
+ 0, 0, 0, 0, 215, 0, 0, 0, 0, 0,
+ 0, 34, 0, 0, 0, 35, 36, 0, 37, 0,
+ 0, 0, 0, 0, 0, 503, 0, 0, 0, 44,
+ 0, 0, 0, 0, 0, 0, 0, 550, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 51, 504, 506,
+ 505, 0, 52, 0, 0, 0, 0, 226, 0, 0,
+ 0, 0, 0, 43, 54, 32, 210, 0, 0, 40,
+ 0, 0, 0, 0, 45, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 502, 0, 30, 31, 0, 0,
+ 0, 0, 0, 0, 0, 0, 215, 0, 0, 0,
+ 0, 0, 0, 34, 0, 0, 0, 35, 36, 0,
+ 37, 0, 0, 0, 0, 0, 0, 503, 0, 0,
+ 0, 44, 0, 0, 0, 0, 0, 0, 0, 547,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 51,
+ 504, 506, 505, 0, 52, 0, 0, 0, 0, 226,
+ 0, 0, 0, 0, 0, 43, 54, 32, 210, 0,
+ 0, 40, 0, 0, 0, 0, 45, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 29, 30, 31, 0,
+ 0, 0, 0, 0, 0, 0, 0, 33, 0, 0,
+ 0, 0, 0, 0, 34, 0, 0, 0, 35, 36,
+ 0, 37, 0, 0, 0, 38, 0, 39, 41, 42,
+ 0, 0, 44, 0, 0, 0, 46, 0, 47, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 51, 48, 50, 49, 0, 52, 0, 53, 0, 55,
+ 0, 56, 0, 0, 0, 0, 43, 54, 32, 0,
+ 0, 0, 40, 0, 0, 0, 0, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, -122, 0, 0,
+ 0, 29, 30, 31, 0, 0, 0, 0, 0, 0,
+ 0, 0, 33, 0, 0, 0, 0, 0, 0, 34,
+ 0, 0, 0, 35, 36, 0, 37, 0, 0, 0,
+ 38, 0, 39, 41, 42, 0, 0, 44, 0, 0,
+ 0, 46, 0, 47, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 51, 48, 50, 49, 0,
+ 52, 0, 53, 0, 55, 0, 56, 0, 0, 0,
+ 0, 43, 54, 32, 0, 0, 0, 40, 0, 0,
+ 0, 0, 45, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 29, 30, 31, 0, 0, 0, 0, 0,
+ 0, 0, 0, 33, 0, 0, 0, 0, 0, 0,
+ 34, 0, 0, 0, 35, 36, 0, 37, 0, 0,
+ 0, 38, 0, 39, 41, 42, 0, 0, 44, 0,
+ 0, 0, 46, 0, 47, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 51, 48, 50, 49,
+ 0, 52, 0, 53, 0, 55, 271, 56, 0, 0,
+ 0, 0, 43, 54, 32, 0, 0, 0, 40, 0,
+ 0, 0, 0, 45, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 475, 0, 0, 29, 30, 31, 0,
+ 0, 0, 0, 0, 0, 0, 0, 33, 0, 0,
+ 0, 0, 0, 0, 34, 0, 0, 0, 35, 36,
+ 0, 37, 0, 0, 0, 38, 0, 39, 41, 42,
+ 0, 0, 44, 0, 0, 0, 46, 0, 47, 0,
+ 0, 476, 0, 0, 0, 0, 0, 0, 0, 0,
+ 51, 48, 50, 49, 0, 52, 0, 53, 0, 55,
+ 0, 56, 0, 0, 0, 0, 43, 54, 32, 0,
+ 0, 0, 40, 0, 0, 0, 0, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 475, 0, 0,
+ 29, 30, 31, 0, 0, 0, 0, 0, 0, 0,
+ 0, 33, 0, 0, 0, 0, 0, 0, 34, 0,
+ 0, 0, 35, 36, 0, 37, 0, 0, 0, 38,
+ 0, 39, 41, 42, 0, 0, 44, 0, 0, 0,
+ 46, 0, 47, 0, 0, 481, 0, 0, 0, 0,
+ 0, 0, 0, 0, 51, 48, 50, 49, 0, 52,
+ 0, 53, 0, 55, 0, 56, 0, 0, 0, 0,
+ 43, 54, 32, 0, 0, 0, 40, 0, 0, 0,
+ 0, 45, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 483, 0, 0, 29, 30, 31, 0, 0, 0,
+ 0, 0, 0, 0, 0, 33, 0, 0, 0, 0,
+ 0, 0, 34, 0, 0, 0, 35, 36, 0, 37,
+ 0, 0, 0, 38, 0, 39, 41, 42, 0, 0,
+ 44, 0, 0, 0, 46, 0, 47, 0, 0, 484,
+ 0, 0, 0, 0, 0, 0, 0, 0, 51, 48,
+ 50, 49, 0, 52, 0, 53, 0, 55, 0, 56,
+ 0, 0, 0, 0, 43, 54, 32, 0, 0, 0,
+ 40, 0, 0, 0, 0, 45, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 483, 0, 0, 29, 30,
+ 31, 0, 0, 0, 0, 0, 0, 0, 0, 33,
+ 0, 0, 0, 0, 0, 0, 34, 0, 0, 0,
+ 35, 36, 0, 37, 0, 0, 0, 38, 0, 39,
+ 41, 42, 0, 0, 44, 0, 0, 0, 46, 0,
+ 47, 0, 0, 486, 0, 0, 0, 0, 0, 0,
+ 0, 0, 51, 48, 50, 49, 0, 52, 0, 53,
+ 0, 55, 0, 56, 0, 0, 0, 0, 43, 54,
+ 32, 0, 0, 0, 40, 0, 0, 0, 0, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 29,
+ 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
+ 33, 0, 0, 0, 0, 0, 0, 34, 217, 0,
+ 0, 218, 36, 0, 37, 0, 0, 0, 38, 0,
+ 39, 41, 42, 0, 0, 44, 0, 0, 0, 46,
+ 0, 47, 0, 0, 0, 0, 0, 0, 0, 221,
+ 0, 0, 0, 51, 48, 50, 49, 223, 52, 0,
+ 53, 225, 55, 0, 56, 0, 228, 0, 0, 43,
+ 54, 32, 0, 0, 0, 40, 0, 0, 0, 0,
+ 45, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 30, 31, 0, 0, 0, 0, 0, 0, 0,
+ 0, 33, 0, 0, 0, 0, 0, 0, 34, 217,
+ 0, 0, 582, 583, 0, 37, 0, 0, 0, 38,
+ 0, 39, 41, 42, 0, 0, 44, 0, 0, 0,
+ 46, 0, 47, 0, 0, 0, 0, 0, 0, 0,
+ 221, 0, 0, 0, 51, 48, 50, 49, 223, 52,
+ 0, 53, 225, 55, 0, 56, 0, 228, 0, 0,
+ 43, 54, 32, 0, 0, 0, 40, 0, 0, 0,
+ 0, 45, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 109, 110, 111, 0, 0, 113, 115, 116, 0,
+ 0, 117, 0, 118, 0, 0, 0, 120, 121, 122,
+ 0, 0, 0, 0, 0, 0, 34, 123, 124, 125,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 126, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 129, 0, 0, 0,
+ 0, 0, 0, 48, 50, 49, 130, 131, 132, 0,
+ 134, 135, 136, 137, 138, 139, 0, 0, 127, 133,
+ 119, 112, 114, 128, 0, 0, 0, 0, 0, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 109,
+ 110, 111, 0, 0, 113, 115, 116, 0, 0, 117,
+ 0, 118, 0, 0, 0, 120, 121, 122, 0, 0,
+ 0, 0, 0, 0, 393, 123, 124, 125, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 126, 0,
+ 0, 0, 394, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 129, 0, 0, 0, 0, 0,
+ 398, 395, 397, 0, 130, 131, 132, 0, 134, 135,
+ 136, 137, 138, 139, 0, 0, 127, 133, 119, 112,
+ 114, 128, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 109, 110, 111,
+ 0, 0, 113, 115, 116, 0, 0, 117, 0, 118,
+ 0, 0, 0, 120, 121, 122, 0, 0, 0, 0,
+ 0, 0, 393, 123, 124, 125, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 126, 0, 0, 0,
+ 394, 0, 0, 0, 0, 0, 0, 0, 396, 0,
+ 0, 0, 129, 0, 0, 0, 0, 0, 398, 395,
+ 397, 0, 130, 131, 132, 0, 134, 135, 136, 137,
+ 138, 139, 0, 0, 127, 133, 119, 112, 114, 128,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 209, 0, 0, 0, 0,
+ 211, 0, 29, 30, 31, 213, 0, 0, 0, 0,
+ 0, 0, 214, 215, 0, 0, 0, 0, 0, 0,
+ 216, 217, 0, 0, 218, 36, 0, 37, 0, 0,
+ 0, 38, 0, 39, 41, 42, 0, 0, 44, 0,
+ 0, 0, 46, 0, 47, 0, 0, 0, 0, 0,
+ 220, 0, 221, 0, 0, 0, 51, 219, 222, 49,
+ 223, 52, 224, 53, 225, 55, 226, 56, 227, 228,
+ 0, 0, 43, 54, 32, 210, 212, 0, 40, 0,
+ 0, 0, 0, 45, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 209, 0, 0, 0, 0, 211, 0,
+ 29, 30, 31, 213, 0, 0, 0, 0, 0, 0,
+ 214, 33, 0, 0, 0, 0, 0, 0, 216, 217,
+ 0, 0, 218, 36, 0, 37, 0, 0, 0, 38,
+ 0, 39, 41, 42, 0, 0, 44, 0, 0, 0,
+ 46, 0, 47, 0, 0, 0, 0, 0, 220, 0,
+ 221, 0, 0, 0, 51, 219, 222, 49, 223, 52,
+ 224, 53, 225, 55, 226, 56, 227, 228, 0, 0,
+ 43, 54, 32, 210, 212, 0, 40, 0, 0, 0,
+ 0, 45, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 586, 110, 111, 0, 0, 588, 115, 590, 30,
+ 31, 591, 0, 118, 0, 0, 0, 120, 593, 594,
+ 0, 0, 0, 0, 0, 0, 595, 596, 124, 125,
+ 218, 36, 0, 37, 0, 0, 0, 38, 0, 39,
+ 597, 42, 0, 0, 599, 0, 0, 0, 46, 0,
+ 47, 0, 0, 0, 0, 0, 601, 0, 221, 0,
+ 0, 0, 603, 600, 602, 49, 604, 605, 606, 53,
+ 608, 609, 610, 611, 612, 613, 0, 0, 598, 607,
+ 592, 587, 589, 128, 40, 0, 0, 0, 0, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 361,
+ 110, 111, 0, 0, 363, 115, 365, 30, 31, 366,
+ 0, 118, 0, 0, 0, 120, 368, 369, 0, 0,
+ 0, 0, 0, 0, 370, 371, 124, 125, 218, 36,
+ 0, 37, 0, 0, 0, 38, 0, 39, 372, 42,
+ 0, 0, 374, 0, 0, 0, 46, 0, 47, 0,
+ -268, 0, 0, 0, 376, 0, 221, 0, 0, 0,
+ 378, 375, 377, 49, 379, 380, 381, 53, 383, 384,
+ 385, 386, 387, 388, 0, 0, 373, 382, 367, 362,
+ 364, 128, 40, 0, 0, 0, 0, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,
+
+ 534, 311, 497, 309, 532, 461, 498, 499, 516, 515,
+ 619, 638, 16, 552, 436, 358, 616, 472, 562, 320,
+ 528, 238, 487, 182, 250, 243, 253, 182, 302, 641,
+ 627, 632, 150, 485, 143, 454, 439, 402, 445, 559,
+ 237, 574, 250, 578, 561, 186, 618, 458, 238, 349,
+ 573, 449, 447, 571, 243, 347, 450, 243, 460, 351,
+ 238, 353, 358, 410, 415, 439, 176, 188, 436, 250,
+ 467, 417, 433, 182, 425, 429, 302, 169, 456, 358,
+ 171, 140, 336, 334, 338, 344, 436, 392, 390, 400,
+ 163, 302, 307, 148, 146, 339, 439, 404, 302, 358,
+ 404, 358, 0, 482, 501, 480, 0, 642, 0, 479,
+ 0, 0, 0, 320, 60, 0, 186, 501, 90, 60,
+ 60, 489, 302, 60, 617, 93, 0, 88, 0, 405,
+ 0, 461, 405, 60, 60, 451, 180, 60, 0, 180,
+ 60, 60, 60, 451, 60, 95, 89, 146, 266, 287,
+ 60, 146, 407, 270, 60, 288, 178, 60, 106, 452,
+ 0, 60, 60, 60, 102, 60, 302, 332, 286, 60,
+ 92, 452, 60, 60, 451, 60, 165, 168, 285, 432,
+ 284, 435, 60, 60, 108, 501, 329, 94, 540, 96,
+ 60, 330, 60, 302, 494, 60, 77, 237, 60, 404,
+ 452, 341, 471, 72, 60, 60, 67, 69, 60, 60,
+ 68, 0, 70, 60, 60, 60, 61, 180, 60, 60,
+ 98, 491, 60, 91, 490, 60, 60, 60, 493, 60,
+ 84, 405, 60, 97, 492, 305, 0, 60, 0, 298,
+ 0, 100, 270, 298, 270, 298, 106, 298, 270, 0,
+ 270, 60, 270, 60, 316, 0, 270, 0, 270, 298,
+ 291, 326, 303, 60, 270, 319, 313, 300, 270, 297,
+ 60, 60, 108, 175, 295, 270, 270, 290, 60, 501,
+ 273, 317, 60, 270, 60, 278, 509, 270, 0, 270,
+ 0, 289, 0, 548, 0, 293, 551, 0, 500, 510,
+ 501, 501, 0, 544, 501, 0, 0, 0, 509, 0,
+ 0, 509, 520, 521, 522, 523, 527, 524, 525, 0,
+ 500, 510, 0, 500, 510, 564, 520, 521, 522, 523,
+ 527, 524, 525, 581, 0, 0, 0, 0, 0, 0,
+ 584, 585, 520, 521, 522, 523, 527, 524, 525, 556,
+ 0, 0, 0, 0, 0, 0, 557, 558, 520, 521,
+ 522, 523, 527, 524, 525, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 556, 0, 0, 540, 0, 614,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 472, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+
+const short QmlJSGrammar::action_check [] = {
+ 36, 36, 24, 7, 55, 7, 7, 7, 60, 36,
+ 8, 7, 37, 7, 7, 33, 55, 55, 60, 36,
+ 36, 33, 33, 55, 8, 33, 7, 7, 34, 36,
+ 16, 20, 7, 36, 7, 7, 33, 36, 33, 60,
+ 7, 7, 7, 5, 7, 5, 5, 90, 33, 36,
+ 7, 33, 7, 36, 7, 33, 66, 36, 7, 36,
+ 29, 7, 33, 31, 1, 7, 17, 55, 60, 33,
+ 60, 2, 8, 7, 48, 66, 1, 7, 36, 2,
+ 8, 7, 36, 2, 60, 2, 8, 7, 17, 1,
+ 60, 36, 0, 36, 8, 36, 48, 36, 33, 8,
+ 61, 1, 6, 36, 60, 8, -1, 8, -1, 15,
+ 48, 10, 7, 8, 61, 8, 20, -1, -1, -1,
+ -1, -1, 8, -1, -1, -1, -1, 48, 34, 79,
+ 40, 79, 8, 77, 61, 8, 60, 8, 8, 79,
+ 8, 51, -1, 40, 42, 61, 62, 8, 61, 62,
+ 8, 7, -1, 56, 51, 53, 55, 8, 90, 60,
+ 50, 79, 8, 56, 54, 61, 62, 61, 62, 61,
+ 62, 40, 61, 62, 60, 40, 61, 62, 61, 62,
+ 56, 8, 51, 56, 50, 7, 51, 15, 54, 60,
+ 60, 25, 60, 27, 25, 56, 27, 61, 62, 61,
+ 62, 36, 60, 29, 38, 29, 34, 38, 36, 25,
+ 61, 27, 29, -1, 60, 25, 12, 27, 29, 25,
+ 29, 27, 38, 25, 29, 27, 61, 62, 38, 12,
+ 7, 12, 38, 60, 8, 25, 38, 27, -1, 61,
+ 62, 25, 8, 27, 7, -1, 15, 7, 38, 75,
+ 25, 75, 27, 8, 38, 25, 33, 27, 75, -1,
+ 86, 57, 86, 38, 75, 34, 75, 63, 38, 86,
+ 75, 15, 15, -1, 57, 86, 57, 86, -1, -1,
+ 63, 86, 63, 61, 62, -1, -1, 61, 62, 15,
+ 34, 34, 36, 36, -1, 61, 62, 47, 61, 62,
+ 15, 61, 62, -1, 18, 19, 61, 62, 34, 15,
+ 36, 61, 62, 91, -1, 92, 18, 19, 33, 34,
+ 29, 36, 18, 19, 18, 19, 29, 33, 34, 29,
+ 36, 45, 46, 25, 29, 27, -1, -1, -1, -1,
+ -1, 91, -1, 45, 46, -1, 38, -1, -1, 45,
+ 46, 45, 46, -1, -1, -1, -1, 66, 67, 68,
+ -1, -1, -1, 66, 67, 68, 66, 67, 68, -1,
+ -1, 66, 67, 68, -1, -1, 23, 24, -1, -1,
+ -1, 29, -1, 92, -1, 32, 23, 24, 35, 92,
+ 37, 29, 92, 23, 24, 32, 29, 92, 35, -1,
+ 37, 29, 32, 23, 24, 35, -1, 37, -1, -1,
+ 29, -1, 32, 23, 24, 35, -1, 37, 66, 67,
+ 68, 31, 32, -1, 29, 35, -1, 37, 66, 67,
+ 68, 36, -1, 66, 67, 68, -1, -1, 66, 67,
+ 68, -1, -1, -1, 92, -1, -1, 66, 67, 68,
+ -1, -1, -1, -1, 92, -1, -1, 23, 24, 92,
+ -1, 66, 67, 68, 92, 31, 32, 23, 24, 35,
+ -1, 37, -1, 92, -1, 31, 32, 23, 24, 35,
+ -1, 37, 29, 23, 24, 31, 32, 92, -1, 35,
+ 29, 37, 32, 29, -1, 35, -1, 37, 94, 95,
+ 96, 97, 98, 99, 29, -1, -1, -1, -1, -1,
+ -1, 36, -1, -1, 61, 62, -1, -1, -1, 66,
+ 67, 68, 61, 62, -1, 61, 62, 66, 67, 68,
+ 66, 67, 68, -1, -1, -1, -1, -1, -1, -1,
+ 29, 66, 67, 68, -1, 92, -1, -1, 29, -1,
+ -1, 29, -1, 92, -1, -1, 92, -1, -1, -1,
+ -1, -1, 23, 24, -1, -1, -1, 92, -1, -1,
+ 31, 32, 61, 62, 35, -1, 37, 66, 67, 68,
+ 61, 62, 3, 61, 62, 66, 67, 68, 66, 67,
+ 68, -1, 13, -1, -1, -1, 17, -1, -1, -1,
+ -1, -1, -1, 92, -1, 26, -1, 28, -1, -1,
+ 31, 92, -1, -1, 92, -1, -1, -1, 39, -1,
+ 41, 42, -1, -1, -1, -1, -1, -1, 49, -1,
+ -1, 52, 53, -1, -1, -1, -1, 58, -1, 12,
+ 13, 3, -1, 64, -1, -1, -1, -1, -1, 22,
+ -1, 13, -1, -1, -1, 17, 29, -1, -1, 80,
+ 33, 34, -1, 36, 26, -1, 28, -1, -1, -1,
+ 43, -1, -1, -1, 47, -1, -1, 39, -1, 41,
+ 42, -1, -1, -1, -1, -1, -1, 49, -1, -1,
+ 52, 53, 65, 66, 67, 68, 58, 70, -1, -1,
+ -1, -1, 64, -1, -1, -1, -1, -1, 81, 82,
+ 83, -1, 12, 13, 87, -1, -1, -1, 80, 92,
+ -1, -1, 22, -1, -1, -1, -1, -1, -1, 29,
+ -1, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 12, 13, -1, 43, -1, -1, -1, 47, -1, -1,
+ 22, -1, -1, -1, -1, -1, -1, 29, -1, -1,
+ -1, 33, 34, -1, 36, 65, 66, 67, 68, -1,
+ 70, 43, -1, -1, -1, 47, -1, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, 87, -1, -1,
+ -1, -1, 92, 65, 66, 67, 68, -1, 70, -1,
+ -1, -1, 12, 13, -1, -1, -1, -1, -1, 81,
+ 82, 83, 22, -1, -1, 87, -1, -1, -1, 29,
+ 92, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 12, 13, -1, 43, -1, -1, -1, 47, -1, -1,
+ 22, -1, -1, -1, -1, -1, -1, 29, -1, -1,
+ -1, 33, 34, -1, 36, 65, 66, 67, 68, -1,
+ 70, 43, -1, -1, -1, 47, -1, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, 87, -1, -1,
+ -1, -1, 92, 65, 66, 67, 68, -1, 70, -1,
+ -1, -1, 12, 13, -1, -1, -1, -1, -1, 81,
+ 82, 83, 22, -1, -1, 87, -1, -1, -1, 29,
+ 92, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 12, 13, -1, 43, -1, -1, -1, 47, -1, -1,
+ 22, -1, -1, -1, -1, -1, -1, 29, -1, -1,
+ -1, 33, 34, -1, 36, 65, 66, 67, 68, -1,
+ 70, 43, -1, -1, -1, 47, -1, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, 87, -1, -1,
+ -1, -1, 92, 65, 66, 67, 68, -1, 70, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 81,
+ 82, 83, -1, -1, -1, 87, -1, -1, -1, -1,
+ 92, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, -1, 12, 13, -1, -1, -1, -1, -1, -1,
+ -1, -1, 22, -1, -1, -1, -1, -1, -1, 29,
+ -1, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ -1, -1, -1, 43, -1, -1, -1, 47, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 65, 66, 67, 68, -1,
+ 70, -1, -1, -1, -1, 75, -1, -1, -1, -1,
+ -1, 81, 82, 83, 84, -1, -1, 87, -1, -1,
+ -1, -1, 92, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 10, -1, 12, 13, -1, -1, -1, -1,
+ -1, -1, -1, -1, 22, -1, -1, -1, -1, -1,
+ -1, 29, -1, -1, -1, 33, 34, -1, 36, -1,
+ -1, -1, -1, -1, -1, 43, -1, -1, -1, 47,
+ -1, -1, -1, -1, -1, -1, -1, 55, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 65, 66, 67,
+ 68, -1, 70, -1, -1, -1, -1, 75, -1, -1,
+ -1, -1, -1, 81, 82, 83, 84, -1, -1, 87,
+ -1, -1, -1, -1, 92, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 10, -1, 12, 13, -1, -1,
+ -1, -1, -1, -1, -1, -1, 22, -1, -1, -1,
+ -1, -1, -1, 29, -1, -1, -1, 33, 34, -1,
+ 36, -1, -1, -1, -1, -1, -1, 43, -1, -1,
+ -1, 47, -1, -1, -1, -1, -1, -1, -1, 55,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 65,
+ 66, 67, 68, -1, 70, -1, -1, -1, -1, 75,
+ -1, -1, -1, -1, -1, 81, 82, 83, 84, -1,
+ -1, 87, -1, -1, -1, -1, 92, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 11, 12, 13, -1,
+ -1, -1, -1, -1, -1, -1, -1, 22, -1, -1,
+ -1, -1, -1, -1, 29, -1, -1, -1, 33, 34,
+ -1, 36, -1, -1, -1, 40, -1, 42, 43, 44,
+ -1, -1, 47, -1, -1, -1, 51, -1, 53, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 65, 66, 67, 68, -1, 70, -1, 72, -1, 74,
+ -1, 76, -1, -1, -1, -1, 81, 82, 83, -1,
+ -1, -1, 87, -1, -1, -1, -1, 92, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 7, -1, -1,
+ -1, 11, 12, 13, -1, -1, -1, -1, -1, -1,
+ -1, -1, 22, -1, -1, -1, -1, -1, -1, 29,
+ -1, -1, -1, 33, 34, -1, 36, -1, -1, -1,
+ 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
+ -1, 51, -1, 53, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 65, 66, 67, 68, -1,
+ 70, -1, 72, -1, 74, -1, 76, -1, -1, -1,
+ -1, 81, 82, 83, -1, -1, -1, 87, -1, -1,
+ -1, -1, 92, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 11, 12, 13, -1, -1, -1, -1, -1,
+ -1, -1, -1, 22, -1, -1, -1, -1, -1, -1,
+ 29, -1, -1, -1, 33, 34, -1, 36, -1, -1,
+ -1, 40, -1, 42, 43, 44, -1, -1, 47, -1,
+ -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 65, 66, 67, 68,
+ -1, 70, -1, 72, -1, 74, 75, 76, -1, -1,
+ -1, -1, 81, 82, 83, -1, -1, -1, 87, -1,
+ -1, -1, -1, 92, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 8, -1, -1, 11, 12, 13, -1,
+ -1, -1, -1, -1, -1, -1, -1, 22, -1, -1,
+ -1, -1, -1, -1, 29, -1, -1, -1, 33, 34,
+ -1, 36, -1, -1, -1, 40, -1, 42, 43, 44,
+ -1, -1, 47, -1, -1, -1, 51, -1, 53, -1,
+ -1, 56, -1, -1, -1, -1, -1, -1, -1, -1,
+ 65, 66, 67, 68, -1, 70, -1, 72, -1, 74,
+ -1, 76, -1, -1, -1, -1, 81, 82, 83, -1,
+ -1, -1, 87, -1, -1, -1, -1, 92, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 8, -1, -1,
+ 11, 12, 13, -1, -1, -1, -1, -1, -1, -1,
+ -1, 22, -1, -1, -1, -1, -1, -1, 29, -1,
+ -1, -1, 33, 34, -1, 36, -1, -1, -1, 40,
+ -1, 42, 43, 44, -1, -1, 47, -1, -1, -1,
+ 51, -1, 53, -1, -1, 56, -1, -1, -1, -1,
+ -1, -1, -1, -1, 65, 66, 67, 68, -1, 70,
+ -1, 72, -1, 74, -1, 76, -1, -1, -1, -1,
+ 81, 82, 83, -1, -1, -1, 87, -1, -1, -1,
+ -1, 92, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 8, -1, -1, 11, 12, 13, -1, -1, -1,
+ -1, -1, -1, -1, -1, 22, -1, -1, -1, -1,
+ -1, -1, 29, -1, -1, -1, 33, 34, -1, 36,
+ -1, -1, -1, 40, -1, 42, 43, 44, -1, -1,
+ 47, -1, -1, -1, 51, -1, 53, -1, -1, 56,
+ -1, -1, -1, -1, -1, -1, -1, -1, 65, 66,
+ 67, 68, -1, 70, -1, 72, -1, 74, -1, 76,
+ -1, -1, -1, -1, 81, 82, 83, -1, -1, -1,
+ 87, -1, -1, -1, -1, 92, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 8, -1, -1, 11, 12,
+ 13, -1, -1, -1, -1, -1, -1, -1, -1, 22,
+ -1, -1, -1, -1, -1, -1, 29, -1, -1, -1,
+ 33, 34, -1, 36, -1, -1, -1, 40, -1, 42,
+ 43, 44, -1, -1, 47, -1, -1, -1, 51, -1,
+ 53, -1, -1, 56, -1, -1, -1, -1, -1, -1,
+ -1, -1, 65, 66, 67, 68, -1, 70, -1, 72,
+ -1, 74, -1, 76, -1, -1, -1, -1, 81, 82,
+ 83, -1, -1, -1, 87, -1, -1, -1, -1, 92,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 11,
+ 12, 13, -1, -1, -1, -1, -1, -1, -1, -1,
+ 22, -1, -1, -1, -1, -1, -1, 29, 30, -1,
+ -1, 33, 34, -1, 36, -1, -1, -1, 40, -1,
+ 42, 43, 44, -1, -1, 47, -1, -1, -1, 51,
+ -1, 53, -1, -1, -1, -1, -1, -1, -1, 61,
+ -1, -1, -1, 65, 66, 67, 68, 69, 70, -1,
+ 72, 73, 74, -1, 76, -1, 78, -1, -1, 81,
+ 82, 83, -1, -1, -1, 87, -1, -1, -1, -1,
+ 92, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 11, 12, 13, -1, -1, -1, -1, -1, -1, -1,
+ -1, 22, -1, -1, -1, -1, -1, -1, 29, 30,
+ -1, -1, 33, 34, -1, 36, -1, -1, -1, 40,
+ -1, 42, 43, 44, -1, -1, 47, -1, -1, -1,
+ 51, -1, 53, -1, -1, -1, -1, -1, -1, -1,
+ 61, -1, -1, -1, 65, 66, 67, 68, 69, 70,
+ -1, 72, 73, 74, -1, 76, -1, 78, -1, -1,
+ 81, 82, 83, -1, -1, -1, 87, -1, -1, -1,
+ -1, 92, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 4, 5, 6, -1, -1, 9, 10, 11, -1,
+ -1, 14, -1, 16, -1, -1, -1, 20, 21, 22,
+ -1, -1, -1, -1, -1, -1, 29, 30, 31, 32,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 43, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 59, -1, -1, -1,
+ -1, -1, -1, 66, 67, 68, 69, 70, 71, -1,
+ 73, 74, 75, 76, 77, 78, -1, -1, 81, 82,
+ 83, 84, 85, 86, -1, -1, -1, -1, -1, 92,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,
+ 5, 6, -1, -1, 9, 10, 11, -1, -1, 14,
+ -1, 16, -1, -1, -1, 20, 21, 22, -1, -1,
+ -1, -1, -1, -1, 29, 30, 31, 32, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 43, -1,
+ -1, -1, 47, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
+ 65, 66, 67, -1, 69, 70, 71, -1, 73, 74,
+ 75, 76, 77, 78, -1, -1, 81, 82, 83, 84,
+ 85, 86, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 4, 5, 6,
+ -1, -1, 9, 10, 11, -1, -1, 14, -1, 16,
+ -1, -1, -1, 20, 21, 22, -1, -1, -1, -1,
+ -1, -1, 29, 30, 31, 32, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 43, -1, -1, -1,
+ 47, -1, -1, -1, -1, -1, -1, -1, 55, -1,
+ -1, -1, 59, -1, -1, -1, -1, -1, 65, 66,
+ 67, -1, 69, 70, 71, -1, 73, 74, 75, 76,
+ 77, 78, -1, -1, 81, 82, 83, 84, 85, 86,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 4, -1, -1, -1, -1,
+ 9, -1, 11, 12, 13, 14, -1, -1, -1, -1,
+ -1, -1, 21, 22, -1, -1, -1, -1, -1, -1,
+ 29, 30, -1, -1, 33, 34, -1, 36, -1, -1,
+ -1, 40, -1, 42, 43, 44, -1, -1, 47, -1,
+ -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
+ 59, -1, 61, -1, -1, -1, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
+ -1, -1, 81, 82, 83, 84, 85, -1, 87, -1,
+ -1, -1, -1, 92, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 4, -1, -1, -1, -1, 9, -1,
+ 11, 12, 13, 14, -1, -1, -1, -1, -1, -1,
+ 21, 22, -1, -1, -1, -1, -1, -1, 29, 30,
+ -1, -1, 33, 34, -1, 36, -1, -1, -1, 40,
+ -1, 42, 43, 44, -1, -1, 47, -1, -1, -1,
+ 51, -1, 53, -1, -1, -1, -1, -1, 59, -1,
+ 61, -1, -1, -1, 65, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, 77, 78, -1, -1,
+ 81, 82, 83, 84, 85, -1, 87, -1, -1, -1,
+ -1, 92, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 4, 5, 6, -1, -1, 9, 10, 11, 12,
+ 13, 14, -1, 16, -1, -1, -1, 20, 21, 22,
+ -1, -1, -1, -1, -1, -1, 29, 30, 31, 32,
+ 33, 34, -1, 36, -1, -1, -1, 40, -1, 42,
+ 43, 44, -1, -1, 47, -1, -1, -1, 51, -1,
+ 53, -1, -1, -1, -1, -1, 59, -1, 61, -1,
+ -1, -1, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, -1, -1, 81, 82,
+ 83, 84, 85, 86, 87, -1, -1, -1, -1, 92,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,
+ 5, 6, -1, -1, 9, 10, 11, 12, 13, 14,
+ -1, 16, -1, -1, -1, 20, 21, 22, -1, -1,
+ -1, -1, -1, -1, 29, 30, 31, 32, 33, 34,
+ -1, 36, -1, -1, -1, 40, -1, 42, 43, 44,
+ -1, -1, 47, -1, -1, -1, 51, -1, 53, -1,
+ 55, -1, -1, -1, 59, -1, 61, -1, -1, -1,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, -1, -1, 81, 82, 83, 84,
+ 85, 86, 87, -1, -1, -1, -1, 92, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1,
+
+ 15, 2, 105, 3, 29, 15, 4, 2, 15, 29,
+ 9, 15, 3, 15, 3, 2, 19, 39, 15, 15,
+ 13, 15, 3, 15, 2, 15, 3, 15, 3, 11,
+ 13, 15, 71, 39, 39, 3, 22, 2, 99, 19,
+ 4, 15, 2, 15, 29, 15, 19, 3, 15, 3,
+ 29, 22, 15, 29, 15, 2, 22, 15, 2, 2,
+ 15, 2, 2, 2, 2, 22, 3, 15, 3, 2,
+ 39, 3, 3, 15, 97, 94, 3, 39, 2, 2,
+ 39, 3, 3, 2, 2, 101, 3, 40, 39, 39,
+ 39, 3, 2, 39, 39, 15, 22, 13, 3, 2,
+ 13, 2, -1, 39, 13, 35, -1, 16, -1, 39,
+ -1, -1, -1, 15, 48, -1, 15, 13, 52, 48,
+ 48, 50, 3, 48, 20, 53, -1, 52, -1, 45,
+ -1, 15, 45, 48, 48, 50, 50, 48, -1, 50,
+ 48, 48, 48, 50, 48, 53, 52, 39, 48, 53,
+ 48, 39, 44, 53, 48, 53, 44, 48, 15, 50,
+ -1, 48, 48, 48, 58, 48, 3, 72, 53, 48,
+ 53, 50, 48, 48, 50, 48, 62, 64, 53, 82,
+ 53, 82, 48, 48, 41, 13, 88, 53, 16, 54,
+ 48, 72, 48, 3, 50, 48, 54, 4, 48, 13,
+ 50, 100, 86, 56, 48, 48, 50, 50, 48, 48,
+ 50, -1, 51, 48, 48, 48, 51, 50, 48, 48,
+ 54, 50, 48, 53, 50, 48, 48, 48, 50, 48,
+ 53, 45, 48, 54, 50, 72, -1, 48, -1, 48,
+ -1, 60, 53, 48, 53, 48, 15, 48, 53, -1,
+ 53, 48, 53, 48, 65, -1, 53, -1, 53, 48,
+ 55, 70, 72, 48, 53, 70, 63, 70, 53, 70,
+ 48, 48, 41, 42, 59, 53, 53, 55, 48, 13,
+ 57, 70, 48, 53, 48, 55, 20, 53, -1, 53,
+ -1, 55, -1, 5, -1, 61, 5, -1, 32, 33,
+ 13, 13, -1, 16, 13, -1, -1, -1, 20, -1,
+ -1, 20, 22, 23, 24, 25, 26, 27, 28, -1,
+ 32, 33, -1, 32, 33, 21, 22, 23, 24, 25,
+ 26, 27, 28, 13, -1, -1, -1, -1, -1, -1,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 13,
+ -1, -1, -1, -1, -1, -1, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 13, -1, -1, 16, -1, 18,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 39, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1};
+
+} // namespace QbsQmlJS
diff --git a/src/lib/corelib/parser/qmljsgrammar_p.h b/src/lib/corelib/parser/qmljsgrammar_p.h
new file mode 100644
index 000000000..545476e60
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsgrammar_p.h
@@ -0,0 +1,201 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// This file was generated by qlalr - DO NOT EDIT!
+#ifndef QMLJSGRAMMAR_P_H
+#define QMLJSGRAMMAR_P_H
+
+#include "qmljsglobal_p.h"
+#include <QtCore/qglobal.h>
+
+namespace QbsQmlJS {
+
+class QML_PARSER_EXPORT QmlJSGrammar
+{
+public:
+ enum VariousConstants {
+ EOF_SYMBOL = 0,
+ REDUCE_HERE = 101,
+ SHIFT_THERE = 100,
+ T_AND = 1,
+ T_AND_AND = 2,
+ T_AND_EQ = 3,
+ T_AS = 91,
+ T_AUTOMATIC_SEMICOLON = 62,
+ T_BREAK = 4,
+ T_CASE = 5,
+ T_CATCH = 6,
+ T_COLON = 7,
+ T_COMMA = 8,
+ T_COMMENT = 88,
+ T_CONST = 84,
+ T_CONTINUE = 9,
+ T_DEBUGGER = 85,
+ T_DEFAULT = 10,
+ T_DELETE = 11,
+ T_DIVIDE_ = 12,
+ T_DIVIDE_EQ = 13,
+ T_DO = 14,
+ T_DOT = 15,
+ T_ELSE = 16,
+ T_EQ = 17,
+ T_EQ_EQ = 18,
+ T_EQ_EQ_EQ = 19,
+ T_ERROR = 93,
+ T_FALSE = 83,
+ T_FEED_JS_EXPRESSION = 97,
+ T_FEED_JS_PROGRAM = 99,
+ T_FEED_JS_SOURCE_ELEMENT = 98,
+ T_FEED_JS_STATEMENT = 96,
+ T_FEED_UI_OBJECT_MEMBER = 95,
+ T_FEED_UI_PROGRAM = 94,
+ T_FINALLY = 20,
+ T_FOR = 21,
+ T_FUNCTION = 22,
+ T_GE = 23,
+ T_GT = 24,
+ T_GT_GT = 25,
+ T_GT_GT_EQ = 26,
+ T_GT_GT_GT = 27,
+ T_GT_GT_GT_EQ = 28,
+ T_IDENTIFIER = 29,
+ T_IF = 30,
+ T_IMPORT = 90,
+ T_IN = 31,
+ T_INSTANCEOF = 32,
+ T_LBRACE = 33,
+ T_LBRACKET = 34,
+ T_LE = 35,
+ T_LPAREN = 36,
+ T_LT = 37,
+ T_LT_LT = 38,
+ T_LT_LT_EQ = 39,
+ T_MINUS = 40,
+ T_MINUS_EQ = 41,
+ T_MINUS_MINUS = 42,
+ T_MULTILINE_STRING_LITERAL = 87,
+ T_NEW = 43,
+ T_NOT = 44,
+ T_NOT_EQ = 45,
+ T_NOT_EQ_EQ = 46,
+ T_NULL = 81,
+ T_NUMERIC_LITERAL = 47,
+ T_ON = 92,
+ T_OR = 48,
+ T_OR_EQ = 49,
+ T_OR_OR = 50,
+ T_PLUS = 51,
+ T_PLUS_EQ = 52,
+ T_PLUS_PLUS = 53,
+ T_PROPERTY = 66,
+ T_PUBLIC = 89,
+ T_QUESTION = 54,
+ T_RBRACE = 55,
+ T_RBRACKET = 56,
+ T_READONLY = 68,
+ T_REMAINDER = 57,
+ T_REMAINDER_EQ = 58,
+ T_RESERVED_WORD = 86,
+ T_RETURN = 59,
+ T_RPAREN = 60,
+ T_SEMICOLON = 61,
+ T_SIGNAL = 67,
+ T_STAR = 63,
+ T_STAR_EQ = 64,
+ T_STRING_LITERAL = 65,
+ T_SWITCH = 69,
+ T_THIS = 70,
+ T_THROW = 71,
+ T_TILDE = 72,
+ T_TRUE = 82,
+ T_TRY = 73,
+ T_TYPEOF = 74,
+ T_VAR = 75,
+ T_VOID = 76,
+ T_WHILE = 77,
+ T_WITH = 78,
+ T_XOR = 79,
+ T_XOR_EQ = 80,
+
+ ACCEPT_STATE = 644,
+ RULE_COUNT = 349,
+ STATE_COUNT = 645,
+ TERMINAL_COUNT = 102,
+ NON_TERMINAL_COUNT = 107,
+
+ GOTO_INDEX_OFFSET = 645,
+ GOTO_INFO_OFFSET = 2807,
+ GOTO_CHECK_OFFSET = 2807
+ };
+
+ static const char *const spell [];
+ static const short lhs [];
+ static const short rhs [];
+ static const short goto_default [];
+ static const short action_default [];
+ static const short action_index [];
+ static const short action_info [];
+ static const short action_check [];
+
+ static inline int nt_action (int state, int nt)
+ {
+ const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt;
+ if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt)
+ return goto_default [nt];
+
+ return action_info [GOTO_INFO_OFFSET + yyn];
+ }
+
+ static inline int t_action (int state, int token)
+ {
+ const int yyn = action_index [state] + token;
+
+ if (yyn < 0 || action_check [yyn] != token)
+ return - action_default [state];
+
+ return action_info [yyn];
+ }
+};
+
+
+} // namespace QbsQmlJS
+
+#endif // QMLJSGRAMMAR_P_H
+
diff --git a/src/lib/corelib/parser/qmljskeywords_p.h b/src/lib/corelib/parser/qmljskeywords_p.h
new file mode 100644
index 000000000..373d2ad01
--- /dev/null
+++ b/src/lib/corelib/parser/qmljskeywords_p.h
@@ -0,0 +1,852 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QMLJSKEYWORDS_P_H
+#define QMLJSKEYWORDS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+namespace QbsQmlJS {
+
+static inline int classify2(const QChar *s, bool qmlMode) {
+ if (s[0].unicode() == 'a') {
+ if (s[1].unicode() == 's') {
+ return qmlMode ? Lexer::T_AS : Lexer::T_RESERVED_WORD;
+ }
+ }
+ else if (s[0].unicode() == 'd') {
+ if (s[1].unicode() == 'o') {
+ return Lexer::T_DO;
+ }
+ }
+ else if (s[0].unicode() == 'i') {
+ if (s[1].unicode() == 'f') {
+ return Lexer::T_IF;
+ }
+ else if (s[1].unicode() == 'n') {
+ return Lexer::T_IN;
+ }
+ }
+ else if (qmlMode && s[0].unicode() == 'o') {
+ if (s[1].unicode() == 'n') {
+ return Lexer::T_ON;
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+static inline int classify3(const QChar *s, bool /*qmlMode*/) {
+ if (s[0].unicode() == 'f') {
+ if (s[1].unicode() == 'o') {
+ if (s[2].unicode() == 'r') {
+ return Lexer::T_FOR;
+ }
+ }
+ }
+ else if (s[0].unicode() == 'i') {
+ if (s[1].unicode() == 'n') {
+ if (s[2].unicode() == 't') {
+ return Lexer::T_INT;
+ }
+ }
+ }
+ else if (s[0].unicode() == 'n') {
+ if (s[1].unicode() == 'e') {
+ if (s[2].unicode() == 'w') {
+ return Lexer::T_NEW;
+ }
+ }
+ }
+ else if (s[0].unicode() == 't') {
+ if (s[1].unicode() == 'r') {
+ if (s[2].unicode() == 'y') {
+ return Lexer::T_TRY;
+ }
+ }
+ }
+ else if (s[0].unicode() == 'v') {
+ if (s[1].unicode() == 'a') {
+ if (s[2].unicode() == 'r') {
+ return Lexer::T_VAR;
+ }
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+static inline int classify4(const QChar *s, bool /*qmlMode*/) {
+ if (s[0].unicode() == 'b') {
+ if (s[1].unicode() == 'y') {
+ if (s[2].unicode() == 't') {
+ if (s[3].unicode() == 'e') {
+ return Lexer::T_BYTE;
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'c') {
+ if (s[1].unicode() == 'a') {
+ if (s[2].unicode() == 's') {
+ if (s[3].unicode() == 'e') {
+ return Lexer::T_CASE;
+ }
+ }
+ }
+ else if (s[1].unicode() == 'h') {
+ if (s[2].unicode() == 'a') {
+ if (s[3].unicode() == 'r') {
+ return Lexer::T_CHAR;
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'e') {
+ if (s[1].unicode() == 'l') {
+ if (s[2].unicode() == 's') {
+ if (s[3].unicode() == 'e') {
+ return Lexer::T_ELSE;
+ }
+ }
+ }
+ else if (s[1].unicode() == 'n') {
+ if (s[2].unicode() == 'u') {
+ if (s[3].unicode() == 'm') {
+ return Lexer::T_ENUM;
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'g') {
+ if (s[1].unicode() == 'o') {
+ if (s[2].unicode() == 't') {
+ if (s[3].unicode() == 'o') {
+ return Lexer::T_GOTO;
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'l') {
+ if (s[1].unicode() == 'o') {
+ if (s[2].unicode() == 'n') {
+ if (s[3].unicode() == 'g') {
+ return Lexer::T_LONG;
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'n') {
+ if (s[1].unicode() == 'u') {
+ if (s[2].unicode() == 'l') {
+ if (s[3].unicode() == 'l') {
+ return Lexer::T_NULL;
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 't') {
+ if (s[1].unicode() == 'h') {
+ if (s[2].unicode() == 'i') {
+ if (s[3].unicode() == 's') {
+ return Lexer::T_THIS;
+ }
+ }
+ }
+ else if (s[1].unicode() == 'r') {
+ if (s[2].unicode() == 'u') {
+ if (s[3].unicode() == 'e') {
+ return Lexer::T_TRUE;
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'v') {
+ if (s[1].unicode() == 'o') {
+ if (s[2].unicode() == 'i') {
+ if (s[3].unicode() == 'd') {
+ return Lexer::T_VOID;
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'w') {
+ if (s[1].unicode() == 'i') {
+ if (s[2].unicode() == 't') {
+ if (s[3].unicode() == 'h') {
+ return Lexer::T_WITH;
+ }
+ }
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+static inline int classify5(const QChar *s, bool /*qmlMode*/) {
+ if (s[0].unicode() == 'b') {
+ if (s[1].unicode() == 'r') {
+ if (s[2].unicode() == 'e') {
+ if (s[3].unicode() == 'a') {
+ if (s[4].unicode() == 'k') {
+ return Lexer::T_BREAK;
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'c') {
+ if (s[1].unicode() == 'a') {
+ if (s[2].unicode() == 't') {
+ if (s[3].unicode() == 'c') {
+ if (s[4].unicode() == 'h') {
+ return Lexer::T_CATCH;
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'l') {
+ if (s[2].unicode() == 'a') {
+ if (s[3].unicode() == 's') {
+ if (s[4].unicode() == 's') {
+ return Lexer::T_CLASS;
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'o') {
+ if (s[2].unicode() == 'n') {
+ if (s[3].unicode() == 's') {
+ if (s[4].unicode() == 't') {
+ return Lexer::T_CONST;
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'f') {
+ if (s[1].unicode() == 'a') {
+ if (s[2].unicode() == 'l') {
+ if (s[3].unicode() == 's') {
+ if (s[4].unicode() == 'e') {
+ return Lexer::T_FALSE;
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'i') {
+ if (s[2].unicode() == 'n') {
+ if (s[3].unicode() == 'a') {
+ if (s[4].unicode() == 'l') {
+ return Lexer::T_FINAL;
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'l') {
+ if (s[2].unicode() == 'o') {
+ if (s[3].unicode() == 'a') {
+ if (s[4].unicode() == 't') {
+ return Lexer::T_FLOAT;
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 's') {
+ if (s[1].unicode() == 'h') {
+ if (s[2].unicode() == 'o') {
+ if (s[3].unicode() == 'r') {
+ if (s[4].unicode() == 't') {
+ return Lexer::T_SHORT;
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'u') {
+ if (s[2].unicode() == 'p') {
+ if (s[3].unicode() == 'e') {
+ if (s[4].unicode() == 'r') {
+ return Lexer::T_SUPER;
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 't') {
+ if (s[1].unicode() == 'h') {
+ if (s[2].unicode() == 'r') {
+ if (s[3].unicode() == 'o') {
+ if (s[4].unicode() == 'w') {
+ return Lexer::T_THROW;
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'w') {
+ if (s[1].unicode() == 'h') {
+ if (s[2].unicode() == 'i') {
+ if (s[3].unicode() == 'l') {
+ if (s[4].unicode() == 'e') {
+ return Lexer::T_WHILE;
+ }
+ }
+ }
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+static inline int classify6(const QChar *s, bool qmlMode) {
+ if (s[0].unicode() == 'd') {
+ if (s[1].unicode() == 'e') {
+ if (s[2].unicode() == 'l') {
+ if (s[3].unicode() == 'e') {
+ if (s[4].unicode() == 't') {
+ if (s[5].unicode() == 'e') {
+ return Lexer::T_DELETE;
+ }
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'o') {
+ if (s[2].unicode() == 'u') {
+ if (s[3].unicode() == 'b') {
+ if (s[4].unicode() == 'l') {
+ if (s[5].unicode() == 'e') {
+ return Lexer::T_DOUBLE;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'e') {
+ if (s[1].unicode() == 'x') {
+ if (s[2].unicode() == 'p') {
+ if (s[3].unicode() == 'o') {
+ if (s[4].unicode() == 'r') {
+ if (s[5].unicode() == 't') {
+ return Lexer::T_EXPORT;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'i') {
+ if (s[1].unicode() == 'm') {
+ if (s[2].unicode() == 'p') {
+ if (s[3].unicode() == 'o') {
+ if (s[4].unicode() == 'r') {
+ if (s[5].unicode() == 't') {
+ return qmlMode ? Lexer::T_IMPORT : Lexer::T_RESERVED_WORD;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'n') {
+ if (s[1].unicode() == 'a') {
+ if (s[2].unicode() == 't') {
+ if (s[3].unicode() == 'i') {
+ if (s[4].unicode() == 'v') {
+ if (s[5].unicode() == 'e') {
+ return Lexer::T_NATIVE;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'p') {
+ if (s[1].unicode() == 'u') {
+ if (s[2].unicode() == 'b') {
+ if (s[3].unicode() == 'l') {
+ if (s[4].unicode() == 'i') {
+ if (s[5].unicode() == 'c') {
+ return qmlMode ? Lexer::T_PUBLIC : Lexer::T_RESERVED_WORD;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'r') {
+ if (s[1].unicode() == 'e') {
+ if (s[2].unicode() == 't') {
+ if (s[3].unicode() == 'u') {
+ if (s[4].unicode() == 'r') {
+ if (s[5].unicode() == 'n') {
+ return Lexer::T_RETURN;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 's') {
+ if (qmlMode && s[1].unicode() == 'i') {
+ if (s[2].unicode() == 'g') {
+ if (s[3].unicode() == 'n') {
+ if (s[4].unicode() == 'a') {
+ if (s[5].unicode() == 'l') {
+ return Lexer::T_SIGNAL;
+ }
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 't') {
+ if (s[2].unicode() == 'a') {
+ if (s[3].unicode() == 't') {
+ if (s[4].unicode() == 'i') {
+ if (s[5].unicode() == 'c') {
+ return Lexer::T_STATIC;
+ }
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'w') {
+ if (s[2].unicode() == 'i') {
+ if (s[3].unicode() == 't') {
+ if (s[4].unicode() == 'c') {
+ if (s[5].unicode() == 'h') {
+ return Lexer::T_SWITCH;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 't') {
+ if (s[1].unicode() == 'h') {
+ if (s[2].unicode() == 'r') {
+ if (s[3].unicode() == 'o') {
+ if (s[4].unicode() == 'w') {
+ if (s[5].unicode() == 's') {
+ return Lexer::T_THROWS;
+ }
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'y') {
+ if (s[2].unicode() == 'p') {
+ if (s[3].unicode() == 'e') {
+ if (s[4].unicode() == 'o') {
+ if (s[5].unicode() == 'f') {
+ return Lexer::T_TYPEOF;
+ }
+ }
+ }
+ }
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+static inline int classify7(const QChar *s, bool /*qmlMode*/) {
+ if (s[0].unicode() == 'b') {
+ if (s[1].unicode() == 'o') {
+ if (s[2].unicode() == 'o') {
+ if (s[3].unicode() == 'l') {
+ if (s[4].unicode() == 'e') {
+ if (s[5].unicode() == 'a') {
+ if (s[6].unicode() == 'n') {
+ return Lexer::T_BOOLEAN;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'd') {
+ if (s[1].unicode() == 'e') {
+ if (s[2].unicode() == 'f') {
+ if (s[3].unicode() == 'a') {
+ if (s[4].unicode() == 'u') {
+ if (s[5].unicode() == 'l') {
+ if (s[6].unicode() == 't') {
+ return Lexer::T_DEFAULT;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'e') {
+ if (s[1].unicode() == 'x') {
+ if (s[2].unicode() == 't') {
+ if (s[3].unicode() == 'e') {
+ if (s[4].unicode() == 'n') {
+ if (s[5].unicode() == 'd') {
+ if (s[6].unicode() == 's') {
+ return Lexer::T_EXTENDS;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'f') {
+ if (s[1].unicode() == 'i') {
+ if (s[2].unicode() == 'n') {
+ if (s[3].unicode() == 'a') {
+ if (s[4].unicode() == 'l') {
+ if (s[5].unicode() == 'l') {
+ if (s[6].unicode() == 'y') {
+ return Lexer::T_FINALLY;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'p') {
+ if (s[1].unicode() == 'a') {
+ if (s[2].unicode() == 'c') {
+ if (s[3].unicode() == 'k') {
+ if (s[4].unicode() == 'a') {
+ if (s[5].unicode() == 'g') {
+ if (s[6].unicode() == 'e') {
+ return Lexer::T_PACKAGE;
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'r') {
+ if (s[2].unicode() == 'i') {
+ if (s[3].unicode() == 'v') {
+ if (s[4].unicode() == 'a') {
+ if (s[5].unicode() == 't') {
+ if (s[6].unicode() == 'e') {
+ return Lexer::T_PRIVATE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+static inline int classify8(const QChar *s, bool qmlMode) {
+ if (s[0].unicode() == 'a') {
+ if (s[1].unicode() == 'b') {
+ if (s[2].unicode() == 's') {
+ if (s[3].unicode() == 't') {
+ if (s[4].unicode() == 'r') {
+ if (s[5].unicode() == 'a') {
+ if (s[6].unicode() == 'c') {
+ if (s[7].unicode() == 't') {
+ return Lexer::T_ABSTRACT;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'c') {
+ if (s[1].unicode() == 'o') {
+ if (s[2].unicode() == 'n') {
+ if (s[3].unicode() == 't') {
+ if (s[4].unicode() == 'i') {
+ if (s[5].unicode() == 'n') {
+ if (s[6].unicode() == 'u') {
+ if (s[7].unicode() == 'e') {
+ return Lexer::T_CONTINUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'd') {
+ if (s[1].unicode() == 'e') {
+ if (s[2].unicode() == 'b') {
+ if (s[3].unicode() == 'u') {
+ if (s[4].unicode() == 'g') {
+ if (s[5].unicode() == 'g') {
+ if (s[6].unicode() == 'e') {
+ if (s[7].unicode() == 'r') {
+ return Lexer::T_DEBUGGER;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'f') {
+ if (s[1].unicode() == 'u') {
+ if (s[2].unicode() == 'n') {
+ if (s[3].unicode() == 'c') {
+ if (s[4].unicode() == 't') {
+ if (s[5].unicode() == 'i') {
+ if (s[6].unicode() == 'o') {
+ if (s[7].unicode() == 'n') {
+ return Lexer::T_FUNCTION;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (qmlMode && s[0].unicode() == 'p') {
+ if (s[1].unicode() == 'r') {
+ if (s[2].unicode() == 'o') {
+ if (s[3].unicode() == 'p') {
+ if (s[4].unicode() == 'e') {
+ if (s[5].unicode() == 'r') {
+ if (s[6].unicode() == 't') {
+ if (s[7].unicode() == 'y') {
+ return Lexer::T_PROPERTY;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (qmlMode && s[0].unicode() == 'r') {
+ if (s[1].unicode() == 'e') {
+ if (s[2].unicode() == 'a') {
+ if (s[3].unicode() == 'd') {
+ if (s[4].unicode() == 'o') {
+ if (s[5].unicode() == 'n') {
+ if (s[6].unicode() == 'l') {
+ if (s[7].unicode() == 'y') {
+ return Lexer::T_READONLY;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'v') {
+ if (s[1].unicode() == 'o') {
+ if (s[2].unicode() == 'l') {
+ if (s[3].unicode() == 'a') {
+ if (s[4].unicode() == 't') {
+ if (s[5].unicode() == 'i') {
+ if (s[6].unicode() == 'l') {
+ if (s[7].unicode() == 'e') {
+ return Lexer::T_VOLATILE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+static inline int classify9(const QChar *s, bool /*qmlMode*/) {
+ if (s[0].unicode() == 'i') {
+ if (s[1].unicode() == 'n') {
+ if (s[2].unicode() == 't') {
+ if (s[3].unicode() == 'e') {
+ if (s[4].unicode() == 'r') {
+ if (s[5].unicode() == 'f') {
+ if (s[6].unicode() == 'a') {
+ if (s[7].unicode() == 'c') {
+ if (s[8].unicode() == 'e') {
+ return Lexer::T_INTERFACE;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'p') {
+ if (s[1].unicode() == 'r') {
+ if (s[2].unicode() == 'o') {
+ if (s[3].unicode() == 't') {
+ if (s[4].unicode() == 'e') {
+ if (s[5].unicode() == 'c') {
+ if (s[6].unicode() == 't') {
+ if (s[7].unicode() == 'e') {
+ if (s[8].unicode() == 'd') {
+ return Lexer::T_PROTECTED;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 't') {
+ if (s[1].unicode() == 'r') {
+ if (s[2].unicode() == 'a') {
+ if (s[3].unicode() == 'n') {
+ if (s[4].unicode() == 's') {
+ if (s[5].unicode() == 'i') {
+ if (s[6].unicode() == 'e') {
+ if (s[7].unicode() == 'n') {
+ if (s[8].unicode() == 't') {
+ return Lexer::T_TRANSIENT;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+static inline int classify10(const QChar *s, bool /*qmlMode*/) {
+ if (s[0].unicode() == 'i') {
+ if (s[1].unicode() == 'm') {
+ if (s[2].unicode() == 'p') {
+ if (s[3].unicode() == 'l') {
+ if (s[4].unicode() == 'e') {
+ if (s[5].unicode() == 'm') {
+ if (s[6].unicode() == 'e') {
+ if (s[7].unicode() == 'n') {
+ if (s[8].unicode() == 't') {
+ if (s[9].unicode() == 's') {
+ return Lexer::T_IMPLEMENTS;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (s[1].unicode() == 'n') {
+ if (s[2].unicode() == 's') {
+ if (s[3].unicode() == 't') {
+ if (s[4].unicode() == 'a') {
+ if (s[5].unicode() == 'n') {
+ if (s[6].unicode() == 'c') {
+ if (s[7].unicode() == 'e') {
+ if (s[8].unicode() == 'o') {
+ if (s[9].unicode() == 'f') {
+ return Lexer::T_INSTANCEOF;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+static inline int classify12(const QChar *s, bool /*qmlMode*/) {
+ if (s[0].unicode() == 's') {
+ if (s[1].unicode() == 'y') {
+ if (s[2].unicode() == 'n') {
+ if (s[3].unicode() == 'c') {
+ if (s[4].unicode() == 'h') {
+ if (s[5].unicode() == 'r') {
+ if (s[6].unicode() == 'o') {
+ if (s[7].unicode() == 'n') {
+ if (s[8].unicode() == 'i') {
+ if (s[9].unicode() == 'z') {
+ if (s[10].unicode() == 'e') {
+ if (s[11].unicode() == 'd') {
+ return Lexer::T_SYNCHRONIZED;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return Lexer::T_IDENTIFIER;
+}
+
+int Lexer::classify(const QChar *s, int n, bool qmlMode) {
+ switch (n) {
+ case 2: return classify2(s, qmlMode);
+ case 3: return classify3(s, qmlMode);
+ case 4: return classify4(s, qmlMode);
+ case 5: return classify5(s, qmlMode);
+ case 6: return classify6(s, qmlMode);
+ case 7: return classify7(s, qmlMode);
+ case 8: return classify8(s, qmlMode);
+ case 9: return classify9(s, qmlMode);
+ case 10: return classify10(s, qmlMode);
+ case 12: return classify12(s, qmlMode);
+ default: return Lexer::T_IDENTIFIER;
+ } // switch
+}
+
+} // namespace QbsQmlJS
+
+#endif // QMLJSKEYWORDS_P_H
diff --git a/src/lib/corelib/parser/qmljslexer.cpp b/src/lib/corelib/parser/qmljslexer.cpp
new file mode 100644
index 000000000..515de2654
--- /dev/null
+++ b/src/lib/corelib/parser/qmljslexer.cpp
@@ -0,0 +1,1141 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 "qmljslexer_p.h"
+#include "qmljsengine_p.h"
+#include "qmljsmemorypool_p.h"
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QVarLengthArray>
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
+QT_END_NAMESPACE
+
+namespace QbsQmlJS {
+
+static int regExpFlagFromChar(const QChar &ch)
+{
+ switch (ch.unicode()) {
+ case 'g': return Lexer::RegExp_Global;
+ case 'i': return Lexer::RegExp_IgnoreCase;
+ case 'm': return Lexer::RegExp_Multiline;
+ }
+ return 0;
+}
+
+static unsigned char convertHex(ushort c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else
+ return (c - 'A' + 10);
+}
+
+static QChar convertHex(QChar c1, QChar c2)
+{
+ return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
+}
+
+static QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4)
+{
+ return QChar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode()),
+ (convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
+}
+
+Lexer::Lexer(Engine *engine)
+ : _engine(engine)
+ , _codePtr(0)
+ , _lastLinePtr(0)
+ , _tokenLinePtr(0)
+ , _tokenStartPtr(0)
+ , _char(QLatin1Char('\n'))
+ , _errorCode(NoError)
+ , _currentLineNumber(0)
+ , _tokenValue(0)
+ , _parenthesesState(IgnoreParentheses)
+ , _parenthesesCount(0)
+ , _stackToken(-1)
+ , _patternFlags(0)
+ , _tokenKind(0)
+ , _tokenLength(0)
+ , _tokenLine(0)
+ , _validTokenText(false)
+ , _prohibitAutomaticSemicolon(false)
+ , _restrictedKeyword(false)
+ , _terminator(false)
+ , _followsClosingBrace(false)
+ , _delimited(true)
+ , _qmlMode(true)
+{
+ if (engine)
+ engine->setLexer(this);
+}
+
+bool Lexer::qmlMode() const
+{
+ return _qmlMode;
+}
+
+QString Lexer::code() const
+{
+ return _code;
+}
+
+void Lexer::setCode(const QString &code, int lineno, bool qmlMode)
+{
+ if (_engine)
+ _engine->setCode(code);
+
+ _qmlMode = qmlMode;
+ _code = code;
+ _tokenText.clear();
+ _tokenText.reserve(1024);
+ _errorMessage.clear();
+ _tokenSpell = QStringRef();
+
+ _codePtr = code.unicode();
+ _lastLinePtr = _codePtr;
+ _tokenLinePtr = _codePtr;
+ _tokenStartPtr = _codePtr;
+
+ _char = QLatin1Char('\n');
+ _errorCode = NoError;
+
+ _currentLineNumber = lineno;
+ _tokenValue = 0;
+
+ // parentheses state
+ _parenthesesState = IgnoreParentheses;
+ _parenthesesCount = 0;
+
+ _stackToken = -1;
+
+ _patternFlags = 0;
+ _tokenLength = 0;
+ _tokenLine = lineno;
+
+ _validTokenText = false;
+ _prohibitAutomaticSemicolon = false;
+ _restrictedKeyword = false;
+ _terminator = false;
+ _followsClosingBrace = false;
+ _delimited = true;
+}
+
+void Lexer::scanChar()
+{
+ _char = *_codePtr++;
+
+ if (_char == QLatin1Char('\n')) {
+ _lastLinePtr = _codePtr; // points to the first character after the newline
+ ++_currentLineNumber;
+ }
+}
+
+int Lexer::lex()
+{
+ const int previousTokenKind = _tokenKind;
+
+ _tokenSpell = QStringRef();
+ _tokenKind = scanToken();
+ _tokenLength = _codePtr - _tokenStartPtr - 1;
+
+ _delimited = false;
+ _restrictedKeyword = false;
+ _followsClosingBrace = (previousTokenKind == T_RBRACE);
+
+ // update the flags
+ switch (_tokenKind) {
+ case T_LBRACE:
+ case T_SEMICOLON:
+ case T_COLON:
+ _delimited = true;
+ break;
+
+ case T_IF:
+ case T_FOR:
+ case T_WHILE:
+ case T_WITH:
+ _parenthesesState = CountParentheses;
+ _parenthesesCount = 0;
+ break;
+
+ case T_DO:
+ _parenthesesState = BalancedParentheses;
+ break;
+
+ case T_CONTINUE:
+ case T_BREAK:
+ case T_RETURN:
+ case T_THROW:
+ _restrictedKeyword = true;
+ break;
+ } // switch
+
+ // update the parentheses state
+ switch (_parenthesesState) {
+ case IgnoreParentheses:
+ break;
+
+ case CountParentheses:
+ if (_tokenKind == T_RPAREN) {
+ --_parenthesesCount;
+ if (_parenthesesCount == 0)
+ _parenthesesState = BalancedParentheses;
+ } else if (_tokenKind == T_LPAREN) {
+ ++_parenthesesCount;
+ }
+ break;
+
+ case BalancedParentheses:
+ _parenthesesState = IgnoreParentheses;
+ break;
+ } // switch
+
+ return _tokenKind;
+}
+
+bool Lexer::isUnicodeEscapeSequence(const QChar *chars)
+{
+ if (isHexDigit(chars[0]) && isHexDigit(chars[1]) && isHexDigit(chars[2]) && isHexDigit(chars[3]))
+ return true;
+
+ return false;
+}
+
+QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok)
+{
+ if (_char == QLatin1Char('u') && isUnicodeEscapeSequence(&_codePtr[0])) {
+ scanChar(); // skip u
+
+ const QChar c1 = _char;
+ scanChar();
+
+ const QChar c2 = _char;
+ scanChar();
+
+ const QChar c3 = _char;
+ scanChar();
+
+ const QChar c4 = _char;
+ scanChar();
+
+ if (ok)
+ *ok = true;
+
+ return convertUnicode(c1, c2, c3, c4);
+ }
+
+ *ok = false;
+ return QChar();
+}
+
+int Lexer::scanToken()
+{
+ if (_stackToken != -1) {
+ int tk = _stackToken;
+ _stackToken = -1;
+ return tk;
+ }
+
+ _terminator = false;
+
+again:
+ _validTokenText = false;
+ _tokenLinePtr = _lastLinePtr;
+
+ while (_char.isSpace()) {
+ if (_char == QLatin1Char('\n')) {
+ _tokenLinePtr = _codePtr;
+
+ if (_restrictedKeyword) {
+ // automatic semicolon insertion
+ _tokenLine = _currentLineNumber;
+ _tokenStartPtr = _codePtr - 1; // ### TODO: insert it before the optional \r sequence.
+ return T_SEMICOLON;
+ } else {
+ _terminator = true;
+ syncProhibitAutomaticSemicolon();
+ }
+ }
+
+ scanChar();
+ }
+
+ _tokenStartPtr = _codePtr - 1;
+ _tokenLine = _currentLineNumber;
+
+ if (_char.isNull())
+ return EOF_SYMBOL;
+
+ const QChar ch = _char;
+ scanChar();
+
+ switch (ch.unicode()) {
+ case '~': return T_TILDE;
+ case '}': return T_RBRACE;
+
+ case '|':
+ if (_char == QLatin1Char('|')) {
+ scanChar();
+ return T_OR_OR;
+ } else if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_OR_EQ;
+ }
+ return T_OR;
+
+ case '{': return T_LBRACE;
+
+ case '^':
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_XOR_EQ;
+ }
+ return T_XOR;
+
+ case ']': return T_RBRACKET;
+ case '[': return T_LBRACKET;
+ case '?': return T_QUESTION;
+
+ case '>':
+ if (_char == QLatin1Char('>')) {
+ scanChar();
+ if (_char == QLatin1Char('>')) {
+ scanChar();
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_GT_GT_GT_EQ;
+ }
+ return T_GT_GT_GT;
+ } else if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_GT_GT_EQ;
+ }
+ return T_GT_GT;
+ } else if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_GE;
+ }
+ return T_GT;
+
+ case '=':
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_EQ_EQ_EQ;
+ }
+ return T_EQ_EQ;
+ }
+ return T_EQ;
+
+ case '<':
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_LE;
+ } else if (_char == QLatin1Char('<')) {
+ scanChar();
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_LT_LT_EQ;
+ }
+ return T_LT_LT;
+ }
+ return T_LT;
+
+ case ';': return T_SEMICOLON;
+ case ':': return T_COLON;
+
+ case '/':
+ if (_char == QLatin1Char('*')) {
+ scanChar();
+ while (!_char.isNull()) {
+ if (_char == QLatin1Char('*')) {
+ scanChar();
+ if (_char == QLatin1Char('/')) {
+ scanChar();
+
+ if (_engine) {
+ _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 4,
+ tokenStartLine(), tokenStartColumn() + 2);
+ }
+
+ goto again;
+ }
+ } else {
+ scanChar();
+ }
+ }
+ } else if (_char == QLatin1Char('/')) {
+ while (!_char.isNull() && _char != QLatin1Char('\n')) {
+ scanChar();
+ }
+ if (_engine) {
+ _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2,
+ tokenStartLine(), tokenStartColumn() + 2);
+ }
+ goto again;
+ } if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_DIVIDE_EQ;
+ }
+ return T_DIVIDE_;
+
+ case '.':
+ if (_char.isDigit()) {
+ QVarLengthArray<char,32> chars;
+
+ chars.append(ch.unicode()); // append the `.'
+
+ while (_char.isDigit()) {
+ chars.append(_char.unicode());
+ scanChar();
+ }
+
+ if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
+ if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
+ _codePtr[1].isDigit())) {
+
+ chars.append(_char.unicode());
+ scanChar(); // consume `e'
+
+ if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) {
+ chars.append(_char.unicode());
+ scanChar(); // consume the sign
+ }
+
+ while (_char.isDigit()) {
+ chars.append(_char.unicode());
+ scanChar();
+ }
+ }
+ }
+
+ chars.append('\0');
+
+ const char *begin = chars.constData();
+ const char *end = 0;
+ bool ok = false;
+
+ _tokenValue = qstrtod(begin, &end, &ok);
+
+ if (end - begin != chars.size() - 1) {
+ _errorCode = IllegalExponentIndicator;
+ _errorMessage = QCoreApplication::translate("QmlParser", "Illegal syntax for exponential number");
+ return T_ERROR;
+ }
+
+ return T_NUMERIC_LITERAL;
+ }
+ return T_DOT;
+
+ case '-':
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_MINUS_EQ;
+ } else if (_char == QLatin1Char('-')) {
+ scanChar();
+
+ if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) {
+ _stackToken = T_MINUS_MINUS;
+ return T_SEMICOLON;
+ }
+
+ return T_MINUS_MINUS;
+ }
+ return T_MINUS;
+
+ case ',': return T_COMMA;
+
+ case '+':
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_PLUS_EQ;
+ } else if (_char == QLatin1Char('+')) {
+ scanChar();
+
+ if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) {
+ _stackToken = T_PLUS_PLUS;
+ return T_SEMICOLON;
+ }
+
+ return T_PLUS_PLUS;
+ }
+ return T_PLUS;
+
+ case '*':
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_STAR_EQ;
+ }
+ return T_STAR;
+
+ case ')': return T_RPAREN;
+ case '(': return T_LPAREN;
+
+ case '&':
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_AND_EQ;
+ } else if (_char == QLatin1Char('&')) {
+ scanChar();
+ return T_AND_AND;
+ }
+ return T_AND;
+
+ case '%':
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_REMAINDER_EQ;
+ }
+ return T_REMAINDER;
+
+ case '!':
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_NOT_EQ_EQ;
+ }
+ return T_NOT_EQ;
+ }
+ return T_NOT;
+
+ case '\'':
+ case '"': {
+ const QChar quote = ch;
+ bool multilineStringLiteral = false;
+
+ const QChar *startCode = _codePtr;
+
+ if (_engine) {
+ while (!_char.isNull()) {
+ if (_char == QLatin1Char('\n') || _char == QLatin1Char('\\')) {
+ break;
+ } else if (_char == quote) {
+ _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode);
+ scanChar();
+
+ return T_STRING_LITERAL;
+ }
+ scanChar();
+ }
+ }
+
+ _validTokenText = true;
+ _tokenText.resize(0);
+ startCode--;
+ while (startCode != _codePtr - 1)
+ _tokenText += *startCode++;
+
+ while (! _char.isNull()) {
+ if (_char == QLatin1Char('\n')) {
+ multilineStringLiteral = true;
+ _tokenText += _char;
+ scanChar();
+ } else if (_char == quote) {
+ scanChar();
+
+ if (_engine)
+ _tokenSpell = _engine->newStringRef(_tokenText);
+
+ return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
+ } else if (_char == QLatin1Char('\\')) {
+ scanChar();
+
+ QChar u;
+ bool ok = false;
+
+ switch (_char.unicode()) {
+ // unicode escape sequence
+ case 'u':
+ u = decodeUnicodeEscapeCharacter(&ok);
+ if (! ok)
+ u = _char;
+ break;
+
+ // hex escape sequence
+ case 'x':
+ case 'X':
+ if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) {
+ scanChar();
+
+ const QChar c1 = _char;
+ scanChar();
+
+ const QChar c2 = _char;
+ scanChar();
+
+ u = convertHex(c1, c2);
+ } else {
+ u = _char;
+ }
+ break;
+
+ // single character escape sequence
+ case '\\': u = QLatin1Char('\\'); scanChar(); break;
+ case '\'': u = QLatin1Char('\''); scanChar(); break;
+ case '\"': u = QLatin1Char('\"'); scanChar(); break;
+ case 'b': u = QLatin1Char('\b'); scanChar(); break;
+ case 'f': u = QLatin1Char('\f'); scanChar(); break;
+ case 'n': u = QLatin1Char('\n'); scanChar(); break;
+ case 'r': u = QLatin1Char('\r'); scanChar(); break;
+ case 't': u = QLatin1Char('\t'); scanChar(); break;
+ case 'v': u = QLatin1Char('\v'); scanChar(); break;
+
+ case '0':
+ if (! _codePtr[1].isDigit()) {
+ scanChar();
+ u = QLatin1Char('\0');
+ } else {
+ // ### parse deprecated octal escape sequence ?
+ u = _char;
+ }
+ break;
+
+ case '\r':
+ while (_char == QLatin1Char('\r'))
+ scanChar();
+
+ if (_char == QLatin1Char('\n')) {
+ u = _char;
+ scanChar();
+ } else {
+ u = QLatin1Char('\n');
+ }
+
+ break;
+
+ case '\n':
+ u = _char;
+ scanChar();
+ break;
+
+ default:
+ // non escape character
+ u = _char;
+ scanChar();
+ }
+
+ _tokenText += u;
+ } else {
+ _tokenText += _char;
+ scanChar();
+ }
+ }
+
+ _errorCode = UnclosedStringLiteral;
+ _errorMessage = QCoreApplication::translate("QmlParser", "Unclosed string at end of line");
+ return T_ERROR;
+ }
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return scanNumber(ch);
+
+ default:
+ if (ch.isLetter() || ch == QLatin1Char('$') || ch == QLatin1Char('_') || (ch == QLatin1Char('\\') && _char == QLatin1Char('u'))) {
+ bool identifierWithEscapeChars = false;
+ if (ch == QLatin1Char('\\')) {
+ identifierWithEscapeChars = true;
+ _tokenText.resize(0);
+ bool ok = false;
+ _tokenText += decodeUnicodeEscapeCharacter(&ok);
+ _validTokenText = true;
+ if (! ok) {
+ _errorCode = IllegalUnicodeEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QmlParser", "Illegal unicode escape sequence");
+ return T_ERROR;
+ }
+ }
+ while (true) {
+ if (_char.isLetterOrNumber() || _char == QLatin1Char('$') || _char == QLatin1Char('_')) {
+ if (identifierWithEscapeChars)
+ _tokenText += _char;
+
+ scanChar();
+ } else if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) {
+ if (! identifierWithEscapeChars) {
+ identifierWithEscapeChars = true;
+ _tokenText.resize(0);
+ _tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1);
+ _validTokenText = true;
+ }
+
+ scanChar(); // skip '\\'
+ bool ok = false;
+ _tokenText += decodeUnicodeEscapeCharacter(&ok);
+ if (! ok) {
+ _errorCode = IllegalUnicodeEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QmlParser", "Illegal unicode escape sequence");
+ return T_ERROR;
+ }
+ } else {
+ _tokenLength = _codePtr - _tokenStartPtr - 1;
+
+ int kind = T_IDENTIFIER;
+
+ if (! identifierWithEscapeChars)
+ kind = classify(_tokenStartPtr, _tokenLength, _qmlMode);
+
+ if (_engine) {
+ if (kind == T_IDENTIFIER && identifierWithEscapeChars)
+ _tokenSpell = _engine->newStringRef(_tokenText);
+ else
+ _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength);
+ }
+
+ return kind;
+ }
+ }
+ }
+
+ break;
+ }
+
+ return T_ERROR;
+}
+
+int Lexer::scanNumber(QChar ch)
+{
+ if (ch != QLatin1Char('0')) {
+ double integer = ch.unicode() - '0';
+
+ QChar n = _char;
+ const QChar *code = _codePtr;
+ while (n.isDigit()) {
+ integer = integer * 10 + (n.unicode() - '0');
+ n = *code++;
+ }
+
+ if (n != QLatin1Char('.') && n != QLatin1Char('e') && n != QLatin1Char('E')) {
+ if (code != _codePtr) {
+ _codePtr = code - 1;
+ scanChar();
+ }
+ _tokenValue = integer;
+ return T_NUMERIC_LITERAL;
+ }
+ }
+
+ QVarLengthArray<char,32> chars;
+ chars.append(ch.unicode());
+
+ if (ch == QLatin1Char('0') && (_char == QLatin1Char('x') || _char == QLatin1Char('X'))) {
+ // parse hex integer literal
+
+ chars.append(_char.unicode());
+ scanChar(); // consume `x'
+
+ while (isHexDigit(_char)) {
+ chars.append(_char.unicode());
+ scanChar();
+ }
+
+ _tokenValue = integerFromString(chars.constData(), chars.size(), 16);
+ return T_NUMERIC_LITERAL;
+ }
+
+ // decimal integer literal
+ while (_char.isDigit()) {
+ chars.append(_char.unicode());
+ scanChar(); // consume the digit
+ }
+
+ if (_char == QLatin1Char('.')) {
+ chars.append(_char.unicode());
+ scanChar(); // consume `.'
+
+ while (_char.isDigit()) {
+ chars.append(_char.unicode());
+ scanChar();
+ }
+
+ if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
+ if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
+ _codePtr[1].isDigit())) {
+
+ chars.append(_char.unicode());
+ scanChar(); // consume `e'
+
+ if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) {
+ chars.append(_char.unicode());
+ scanChar(); // consume the sign
+ }
+
+ while (_char.isDigit()) {
+ chars.append(_char.unicode());
+ scanChar();
+ }
+ }
+ }
+ } else if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
+ if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
+ _codePtr[1].isDigit())) {
+
+ chars.append(_char.unicode());
+ scanChar(); // consume `e'
+
+ if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) {
+ chars.append(_char.unicode());
+ scanChar(); // consume the sign
+ }
+
+ while (_char.isDigit()) {
+ chars.append(_char.unicode());
+ scanChar();
+ }
+ }
+ }
+
+ if (chars.size() == 1) {
+ // if we ended up with a single digit, then it was a '0'
+ _tokenValue = 0;
+ return T_NUMERIC_LITERAL;
+ }
+
+ chars.append('\0');
+
+ const char *begin = chars.constData();
+ const char *end = 0;
+ bool ok = false;
+
+ _tokenValue = qstrtod(begin, &end, &ok);
+
+ if (end - begin != chars.size() - 1) {
+ _errorCode = IllegalExponentIndicator;
+ _errorMessage = QCoreApplication::translate("QmlParser", "Illegal syntax for exponential number");
+ return T_ERROR;
+ }
+
+ return T_NUMERIC_LITERAL;
+}
+
+bool Lexer::scanRegExp(RegExpBodyPrefix prefix)
+{
+ _tokenText.resize(0);
+ _validTokenText = true;
+ _patternFlags = 0;
+
+ if (prefix == EqualPrefix)
+ _tokenText += QLatin1Char('=');
+
+ while (true) {
+ switch (_char.unicode()) {
+ case 0: // eof
+ case '\n': case '\r': // line terminator
+ _errorMessage = QCoreApplication::translate("QmlParser", "Unterminated regular expression literal");
+ return false;
+
+ case '/':
+ scanChar();
+
+ // scan the flags
+ _patternFlags = 0;
+ while (isIdentLetter(_char)) {
+ int flag = regExpFlagFromChar(_char);
+ if (flag == 0) {
+ _errorMessage = QCoreApplication::translate("QmlParser", "Invalid regular expression flag '%0'")
+ .arg(QChar(_char));
+ return false;
+ }
+ _patternFlags |= flag;
+ scanChar();
+ }
+
+ _tokenLength = _codePtr - _tokenStartPtr - 1;
+ return true;
+
+ case '\\':
+ // regular expression backslash sequence
+ _tokenText += _char;
+ scanChar();
+
+ if (_char.isNull() || isLineTerminator()) {
+ _errorMessage = QCoreApplication::translate("QmlParser", "Unterminated regular expression backslash sequence");
+ return false;
+ }
+
+ _tokenText += _char;
+ scanChar();
+ break;
+
+ case '[':
+ // regular expression class
+ _tokenText += _char;
+ scanChar();
+
+ while (! _char.isNull() && ! isLineTerminator()) {
+ if (_char == QLatin1Char(']'))
+ break;
+ else if (_char == QLatin1Char('\\')) {
+ // regular expression backslash sequence
+ _tokenText += _char;
+ scanChar();
+
+ if (_char.isNull() || isLineTerminator()) {
+ _errorMessage = QCoreApplication::translate("QmlParser", "Unterminated regular expression backslash sequence");
+ return false;
+ }
+
+ _tokenText += _char;
+ scanChar();
+ } else {
+ _tokenText += _char;
+ scanChar();
+ }
+ }
+
+ if (_char != QLatin1Char(']')) {
+ _errorMessage = QCoreApplication::translate("QmlParser", "Unterminated regular expression class");
+ return false;
+ }
+
+ _tokenText += _char;
+ scanChar(); // skip ]
+ break;
+
+ default:
+ _tokenText += _char;
+ scanChar();
+ } // switch
+ } // while
+
+ return false;
+}
+
+bool Lexer::isLineTerminator() const
+{
+ return (_char == QLatin1Char('\n') || _char == QLatin1Char('\r'));
+}
+
+bool Lexer::isIdentLetter(QChar ch)
+{
+ // ASCII-biased, since all reserved words are ASCII, aand hence the
+ // bulk of content to be parsed.
+ if ((ch >= QLatin1Char('a') && ch <= QLatin1Char('z'))
+ || (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z'))
+ || ch == QLatin1Char('$')
+ || ch == QLatin1Char('_'))
+ return true;
+ if (ch.unicode() < 128)
+ return false;
+ return ch.isLetterOrNumber();
+}
+
+bool Lexer::isDecimalDigit(ushort c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+bool Lexer::isHexDigit(QChar c)
+{
+ return ((c >= QLatin1Char('0') && c <= QLatin1Char('9'))
+ || (c >= QLatin1Char('a') && c <= QLatin1Char('f'))
+ || (c >= QLatin1Char('A') && c <= QLatin1Char('F')));
+}
+
+bool Lexer::isOctalDigit(ushort c)
+{
+ return (c >= '0' && c <= '7');
+}
+
+int Lexer::tokenEndLine() const
+{
+ return _currentLineNumber;
+}
+
+int Lexer::tokenEndColumn() const
+{
+ return _codePtr - _lastLinePtr;
+}
+
+QString Lexer::tokenText() const
+{
+ if (_validTokenText)
+ return _tokenText;
+
+ if (_tokenKind == T_STRING_LITERAL)
+ return QString(_tokenStartPtr + 1, _tokenLength - 2);
+
+ return QString(_tokenStartPtr, _tokenLength);
+}
+
+Lexer::Error Lexer::errorCode() const
+{
+ return _errorCode;
+}
+
+QString Lexer::errorMessage() const
+{
+ return _errorMessage;
+}
+
+void Lexer::syncProhibitAutomaticSemicolon()
+{
+ if (_parenthesesState == BalancedParentheses) {
+ // we have seen something like "if (foo)", which means we should
+ // never insert an automatic semicolon at this point, since it would
+ // then be expanded into an empty statement (ECMA-262 7.9.1)
+ _prohibitAutomaticSemicolon = true;
+ _parenthesesState = IgnoreParentheses;
+ } else {
+ _prohibitAutomaticSemicolon = false;
+ }
+}
+
+bool Lexer::prevTerminator() const
+{
+ return _terminator;
+}
+
+bool Lexer::followsClosingBrace() const
+{
+ return _followsClosingBrace;
+}
+
+bool Lexer::canInsertAutomaticSemicolon(int token) const
+{
+ return token == T_RBRACE
+ || token == EOF_SYMBOL
+ || _terminator
+ || _followsClosingBrace;
+}
+
+bool Lexer::scanDirectives(Directives *directives)
+{
+ if (_qmlMode) {
+ // the directives are a Javascript-only extension.
+ return false;
+ }
+
+ lex(); // fetch the first token
+
+ if (_tokenKind != T_DOT)
+ return true;
+
+ do {
+ lex(); // skip T_DOT
+
+ const int lineNumber = tokenStartLine();
+
+ if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD))
+ return false; // expected a valid QML/JS directive
+
+ const QString directiveName = tokenText();
+
+ if (! (directiveName == QLatin1String("pragma") ||
+ directiveName == QLatin1String("import")))
+ return false; // not a valid directive name
+
+ // it must be a pragma or an import directive.
+ if (directiveName == QLatin1String("pragma")) {
+ // .pragma library
+ if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library")))
+ return false; // expected `library
+
+ // we found a .pragma library directive
+ directives->pragmaLibrary();
+
+ } else {
+ Q_ASSERT(directiveName == QLatin1String("import"));
+ lex(); // skip .import
+
+ QString pathOrUri;
+ QString version;
+ bool fileImport = false; // file or uri import
+
+ if (_tokenKind == T_STRING_LITERAL) {
+ // .import T_STRING_LITERAL as T_IDENTIFIER
+
+ fileImport = true;
+ pathOrUri = tokenText();
+
+ } else if (_tokenKind == T_IDENTIFIER) {
+ // .import T_IDENTIFIER (. T_IDENTIFIER)* T_NUMERIC_LITERAL as T_IDENTIFIER
+
+ pathOrUri = tokenText();
+
+ lex(); // skip the first T_IDENTIFIER
+ for (; _tokenKind == T_DOT; lex()) {
+ if (lex() != T_IDENTIFIER)
+ return false;
+
+ pathOrUri += QLatin1Char('.');
+ pathOrUri += tokenText();
+ }
+
+ if (_tokenKind != T_NUMERIC_LITERAL)
+ return false; // expected the module version number
+
+ version = tokenText();
+ }
+
+ //
+ // recognize the mandatory `as' followed by the module name
+ //
+ if (! (lex() == T_RESERVED_WORD && tokenText() == QLatin1String("as")))
+ return false; // expected `as'
+
+ if (lex() != T_IDENTIFIER)
+ return false; // expected module name
+
+ const QString module = tokenText();
+
+ if (fileImport)
+ directives->importFile(pathOrUri, module);
+ else
+ directives->importModule(pathOrUri, version, module);
+ }
+
+ if (tokenStartLine() != lineNumber)
+ return false; // the directives cannot span over multiple lines
+
+ // fetch the first token after the .pragma/.import directive
+ lex();
+ } while (_tokenKind == T_DOT);
+
+ return true;
+}
+
+} // namespace QbsQmlJS
+
+#include "qmljskeywords_p.h"
diff --git a/src/lib/corelib/parser/qmljslexer_p.h b/src/lib/corelib/parser/qmljslexer_p.h
new file mode 100644
index 000000000..692659637
--- /dev/null
+++ b/src/lib/corelib/parser/qmljslexer_p.h
@@ -0,0 +1,233 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QMLJSLEXER_P_H
+#define QMLJSLEXER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmljsglobal_p.h"
+#include "qmljsgrammar_p.h"
+#include <QtCore/QString>
+
+namespace QbsQmlJS {
+
+class Engine;
+
+class QML_PARSER_EXPORT Directives {
+public:
+ virtual ~Directives() {}
+
+ virtual void pragmaLibrary()
+ {
+ }
+
+ virtual void importFile(const QString &jsfile, const QString &module)
+ {
+ Q_UNUSED(jsfile);
+ Q_UNUSED(module);
+ }
+
+ virtual void importModule(const QString &uri, const QString &version, const QString &module)
+ {
+ Q_UNUSED(uri);
+ Q_UNUSED(version);
+ Q_UNUSED(module);
+ }
+};
+
+class QML_PARSER_EXPORT Lexer: public QmlJSGrammar
+{
+public:
+ enum {
+ T_ABSTRACT = T_RESERVED_WORD,
+ T_BOOLEAN = T_RESERVED_WORD,
+ T_BYTE = T_RESERVED_WORD,
+ T_CHAR = T_RESERVED_WORD,
+ T_CLASS = T_RESERVED_WORD,
+ T_DOUBLE = T_RESERVED_WORD,
+ T_ENUM = T_RESERVED_WORD,
+ T_EXPORT = T_RESERVED_WORD,
+ T_EXTENDS = T_RESERVED_WORD,
+ T_FINAL = T_RESERVED_WORD,
+ T_FLOAT = T_RESERVED_WORD,
+ T_GOTO = T_RESERVED_WORD,
+ T_IMPLEMENTS = T_RESERVED_WORD,
+ T_INT = T_RESERVED_WORD,
+ T_INTERFACE = T_RESERVED_WORD,
+ T_LET = T_RESERVED_WORD,
+ T_LONG = T_RESERVED_WORD,
+ T_NATIVE = T_RESERVED_WORD,
+ T_PACKAGE = T_RESERVED_WORD,
+ T_PRIVATE = T_RESERVED_WORD,
+ T_PROTECTED = T_RESERVED_WORD,
+ T_SHORT = T_RESERVED_WORD,
+ T_STATIC = T_RESERVED_WORD,
+ T_SUPER = T_RESERVED_WORD,
+ T_SYNCHRONIZED = T_RESERVED_WORD,
+ T_THROWS = T_RESERVED_WORD,
+ T_TRANSIENT = T_RESERVED_WORD,
+ T_VOLATILE = T_RESERVED_WORD,
+ T_YIELD = T_RESERVED_WORD
+ };
+
+ enum Error {
+ NoError,
+ IllegalCharacter,
+ UnclosedStringLiteral,
+ IllegalEscapeSequence,
+ IllegalUnicodeEscapeSequence,
+ UnclosedComment,
+ IllegalExponentIndicator,
+ IllegalIdentifier
+ };
+
+ enum RegExpBodyPrefix {
+ NoPrefix,
+ EqualPrefix
+ };
+
+ enum RegExpFlag {
+ RegExp_Global = 0x01,
+ RegExp_IgnoreCase = 0x02,
+ RegExp_Multiline = 0x04
+ };
+
+public:
+ Lexer(Engine *engine);
+
+ bool qmlMode() const;
+
+ QString code() const;
+ void setCode(const QString &code, int lineno, bool qmlMode = true);
+
+ int lex();
+
+ bool scanRegExp(RegExpBodyPrefix prefix = NoPrefix);
+ bool scanDirectives(Directives *directives);
+
+ int regExpFlags() const { return _patternFlags; }
+ QString regExpPattern() const { return _tokenText; }
+
+ int tokenKind() const { return _tokenKind; }
+ int tokenOffset() const { return _tokenStartPtr - _code.unicode(); }
+ int tokenLength() const { return _tokenLength; }
+
+ int tokenStartLine() const { return _tokenLine; }
+ int tokenStartColumn() const { return _tokenStartPtr - _tokenLinePtr + 1; }
+
+ int tokenEndLine() const;
+ int tokenEndColumn() const;
+
+ inline QStringRef tokenSpell() const { return _tokenSpell; }
+ double tokenValue() const { return _tokenValue; }
+ QString tokenText() const;
+
+ Error errorCode() const;
+ QString errorMessage() const;
+
+ bool prevTerminator() const;
+ bool followsClosingBrace() const;
+ bool canInsertAutomaticSemicolon(int token) const;
+
+ enum ParenthesesState {
+ IgnoreParentheses,
+ CountParentheses,
+ BalancedParentheses
+ };
+
+protected:
+ int classify(const QChar *s, int n, bool qmlMode);
+
+private:
+ inline void scanChar();
+ int scanToken();
+ int scanNumber(QChar ch);
+
+ bool isLineTerminator() const;
+ static bool isIdentLetter(QChar c);
+ static bool isDecimalDigit(ushort c);
+ static bool isHexDigit(QChar c);
+ static bool isOctalDigit(ushort c);
+ static bool isUnicodeEscapeSequence(const QChar *chars);
+
+ void syncProhibitAutomaticSemicolon();
+ QChar decodeUnicodeEscapeCharacter(bool *ok);
+
+private:
+ Engine *_engine;
+
+ QString _code;
+ QString _tokenText;
+ QString _errorMessage;
+ QStringRef _tokenSpell;
+
+ const QChar *_codePtr;
+ const QChar *_lastLinePtr;
+ const QChar *_tokenLinePtr;
+ const QChar *_tokenStartPtr;
+
+ QChar _char;
+ Error _errorCode;
+
+ int _currentLineNumber;
+ double _tokenValue;
+
+ // parentheses state
+ ParenthesesState _parenthesesState;
+ int _parenthesesCount;
+
+ int _stackToken;
+
+ int _patternFlags;
+ int _tokenKind;
+ int _tokenLength;
+ int _tokenLine;
+
+ bool _validTokenText;
+ bool _prohibitAutomaticSemicolon;
+ bool _restrictedKeyword;
+ bool _terminator;
+ bool _followsClosingBrace;
+ bool _delimited;
+ bool _qmlMode;
+};
+
+} // namespace QbsQmlJS
+
+#endif // LEXER_H
diff --git a/src/lib/corelib/parser/qmljsmemorypool_p.h b/src/lib/corelib/parser/qmljsmemorypool_p.h
new file mode 100644
index 000000000..f644cd59d
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsmemorypool_p.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QMLJSMEMORYPOOL_P_H
+#define QMLJSMEMORYPOOL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qmljsglobal_p.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qdebug.h>
+
+#include <cstring>
+
+namespace QbsQmlJS {
+
+class QML_PARSER_EXPORT MemoryPool : public QSharedData
+{
+ MemoryPool(const MemoryPool &other);
+ void operator =(const MemoryPool &other);
+
+public:
+ MemoryPool()
+ : _blocks(0),
+ _allocatedBlocks(0),
+ _blockCount(-1),
+ _ptr(0),
+ _end(0)
+ { }
+
+ ~MemoryPool()
+ {
+ if (_blocks) {
+ for (int i = 0; i < _allocatedBlocks; ++i) {
+ if (char *b = _blocks[i])
+ free(b);
+ }
+
+ free(_blocks);
+ }
+ }
+
+ inline void *allocate(size_t size)
+ {
+ size = (size + 7) & ~7;
+ if (_ptr && (_ptr + size < _end)) {
+ void *addr = _ptr;
+ _ptr += size;
+ return addr;
+ }
+ return allocate_helper(size);
+ }
+
+ void reset()
+ {
+ _blockCount = -1;
+ _ptr = _end = 0;
+ }
+
+private:
+ void *allocate_helper(size_t size)
+ {
+ Q_ASSERT(size < BLOCK_SIZE);
+
+ if (++_blockCount == _allocatedBlocks) {
+ if (! _allocatedBlocks)
+ _allocatedBlocks = DEFAULT_BLOCK_COUNT;
+ else
+ _allocatedBlocks *= 2;
+
+ _blocks = (char **) realloc(_blocks, sizeof(char *) * _allocatedBlocks);
+
+ for (int index = _blockCount; index < _allocatedBlocks; ++index)
+ _blocks[index] = 0;
+ }
+
+ char *&block = _blocks[_blockCount];
+
+ if (! block)
+ block = (char *) malloc(BLOCK_SIZE);
+
+ _ptr = block;
+ _end = _ptr + BLOCK_SIZE;
+
+ void *addr = _ptr;
+ _ptr += size;
+ return addr;
+ }
+
+private:
+ char **_blocks;
+ int _allocatedBlocks;
+ int _blockCount;
+ char *_ptr;
+ char *_end;
+
+ enum
+ {
+ BLOCK_SIZE = 8 * 1024,
+ DEFAULT_BLOCK_COUNT = 8
+ };
+};
+
+class QML_PARSER_EXPORT Managed
+{
+ Managed(const Managed &other);
+ void operator = (const Managed &other);
+
+public:
+ Managed() {}
+ ~Managed() {}
+
+ void *operator new(size_t size, MemoryPool *pool) { return pool->allocate(size); }
+ void operator delete(void *) {}
+ void operator delete(void *, MemoryPool *) {}
+};
+
+} // namespace QbsQmlJS
+
+#endif
diff --git a/src/lib/corelib/parser/qmljsparser.cpp b/src/lib/corelib/parser/qmljsparser.cpp
new file mode 100644
index 000000000..afd9beeee
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsparser.cpp
@@ -0,0 +1,1810 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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 <QtCore/QDebug>
+#include <QtCore/QCoreApplication>
+
+#include <string.h>
+
+#include "qmljsengine_p.h"
+#include "qmljslexer_p.h"
+#include "qmljsast_p.h"
+#include "qmljsmemorypool_p.h"
+
+
+
+#include "qmljsparser_p.h"
+#include <QVarLengthArray>
+
+//
+// This file is automatically generated from qmljs.g.
+// Changes will be lost.
+//
+
+namespace QbsQmlJS {
+
+void Parser::reallocateStack()
+{
+ if (! stack_size)
+ stack_size = 128;
+ else
+ stack_size <<= 1;
+
+ sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value)));
+ state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int)));
+ location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation)));
+ string_stack = reinterpret_cast<QStringRef*> (realloc(string_stack, stack_size * sizeof(QStringRef)));
+}
+
+Parser::Parser(Engine *engine):
+ driver(engine),
+ pool(engine->pool()),
+ tos(0),
+ stack_size(0),
+ sym_stack(0),
+ state_stack(0),
+ location_stack(0),
+ string_stack(0),
+ program(0),
+ first_token(0),
+ last_token(0)
+{
+}
+
+Parser::~Parser()
+{
+ if (stack_size) {
+ free(sym_stack);
+ free(state_stack);
+ free(location_stack);
+ free(string_stack);
+ }
+}
+
+static inline AST::SourceLocation location(Lexer *lexer)
+{
+ AST::SourceLocation loc;
+ loc.offset = lexer->tokenOffset();
+ loc.length = lexer->tokenLength();
+ loc.startLine = lexer->tokenStartLine();
+ loc.startColumn = lexer->tokenStartColumn();
+ return loc;
+}
+
+AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr)
+{
+ QVarLengthArray<QStringRef, 4> nameIds;
+ QVarLengthArray<AST::SourceLocation, 4> locations;
+
+ AST::ExpressionNode *it = expr;
+ while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) {
+ nameIds.append(m->name);
+ locations.append(m->identifierToken);
+ it = m->base;
+ }
+
+ if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) {
+ AST::UiQualifiedId *q = new (pool) AST::UiQualifiedId(idExpr->name);
+ q->identifierToken = idExpr->identifierToken;
+
+ AST::UiQualifiedId *currentId = q;
+ for (int i = nameIds.size() - 1; i != -1; --i) {
+ currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]);
+ currentId->identifierToken = locations[i];
+ }
+
+ return currentId->finish();
+ }
+
+ return 0;
+}
+
+bool Parser::parse(int startToken)
+{
+ Lexer *lexer = driver->lexer();
+ bool hadErrors = false;
+ int yytoken = -1;
+ int action = 0;
+
+ token_buffer[0].token = startToken;
+ first_token = &token_buffer[0];
+ if (startToken == T_FEED_JS_PROGRAM) {
+ Directives ignoreDirectives;
+ Directives *directives = driver->directives();
+ if (!directives)
+ directives = &ignoreDirectives;
+ lexer->scanDirectives(directives);
+ token_buffer[1].token = lexer->tokenKind();
+ token_buffer[1].dval = lexer->tokenValue();
+ token_buffer[1].loc = location(lexer);
+ token_buffer[1].spell = lexer->tokenSpell();
+ last_token = &token_buffer[2];
+ } else {
+ last_token = &token_buffer[1];
+ }
+
+ tos = -1;
+ program = 0;
+
+ do {
+ if (++tos == stack_size)
+ reallocateStack();
+
+ state_stack[tos] = action;
+
+ _Lcheck_token:
+ if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) {
+ yyprevlloc = yylloc;
+
+ if (first_token == last_token) {
+ yytoken = lexer->lex();
+ yylval = lexer->tokenValue();
+ yytokenspell = lexer->tokenSpell();
+ yylloc = location(lexer);
+ } else {
+ yytoken = first_token->token;
+ yylval = first_token->dval;
+ yytokenspell = first_token->spell;
+ yylloc = first_token->loc;
+ ++first_token;
+ }
+ }
+
+ action = t_action(action, yytoken);
+ if (action > 0) {
+ if (action != ACCEPT_STATE) {
+ yytoken = -1;
+ sym(1).dval = yylval;
+ stringRef(1) = yytokenspell;
+ loc(1) = yylloc;
+ } else {
+ --tos;
+ return ! hadErrors;
+ }
+ } else if (action < 0) {
+ const int r = -action - 1;
+ tos -= rhs[r];
+
+ switch (r) {
+
+case 0: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+
+case 1: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+
+case 2: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+
+case 3: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+
+case 4: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+
+case 5: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+} break;
+
+case 6: {
+ sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiImportList,
+ sym(2).UiObjectMemberList->finish());
+} break;
+
+case 8: {
+ sym(1).Node = sym(1).UiImportList->finish();
+} break;
+
+case 9: {
+ sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImport);
+} break;
+
+case 10: {
+ sym(1).Node = new (pool) AST::UiImportList(sym(1).UiImportList, sym(2).UiImport);
+} break;
+
+case 13: {
+ sym(1).UiImport->semicolonToken = loc(2);
+} break;
+
+case 15: {
+ sym(1).UiImport->versionToken = loc(2);
+ sym(1).UiImport->semicolonToken = loc(3);
+} break;
+
+case 17: {
+ sym(1).UiImport->versionToken = loc(2);
+ sym(1).UiImport->asToken = loc(3);
+ sym(1).UiImport->importIdToken = loc(4);
+ sym(1).UiImport->importId = stringRef(4);
+ sym(1).UiImport->semicolonToken = loc(5);
+} break;
+
+case 19: {
+ sym(1).UiImport->asToken = loc(2);
+ sym(1).UiImport->importIdToken = loc(3);
+ sym(1).UiImport->importId = stringRef(3);
+ sym(1).UiImport->semicolonToken = loc(4);
+} break;
+
+case 20: {
+ AST::UiImport *node = 0;
+
+ if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) {
+ node = new (pool) AST::UiImport(importIdLiteral->value);
+ node->fileNameToken = loc(2);
+ } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) {
+ node = new (pool) AST::UiImport(qualifiedId);
+ node->fileNameToken = loc(2);
+ }
+
+ sym(1).Node = node;
+
+ if (node) {
+ node->importToken = loc(1);
+ } else {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ QLatin1String("Expected a qualified name id or a string literal")));
+
+ return false; // ### remove me
+ }
+} break;
+
+case 21: {
+ sym(1).Node = 0;
+} break;
+
+case 22: {
+ sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
+} break;
+
+case 23: {
+ sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
+} break;
+
+case 24: {
+ AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList(
+ sym(1).UiObjectMemberList, sym(2).UiObjectMember);
+ sym(1).Node = node;
+} break;
+
+case 25: {
+ sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember);
+} break;
+
+case 26: {
+ AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList(
+ sym(1).UiArrayMemberList, sym(3).UiObjectMember);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 27: {
+ AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 28: {
+ AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 29: {
+ AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId,
+ sym(2).UiObjectInitializer);
+ sym(1).Node = node;
+} break;
+
+case 31: {
+ AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding(
+ sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish());
+ node->colonToken = loc(2);
+ node->lbracketToken = loc(3);
+ node->rbracketToken = loc(5);
+ sym(1).Node = node;
+} break;
+
+case 32: {
+ AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
+ sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 33: {
+ AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
+ sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer);
+ node->colonToken = loc(2);
+ node->hasOnToken = true;
+ sym(1).Node = node;
+} break;
+
+case 41:
+{
+ AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(
+ sym(1).UiQualifiedId, sym(3).Statement);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 45: {
+ sym(1).Node = 0;
+} break;
+
+case 46: {
+ sym(1).Node = sym(1).UiParameterList->finish ();
+} break;
+
+case 47: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(stringRef(1), stringRef(2));
+ node->propertyTypeToken = loc(1);
+ node->identifierToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 48: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, stringRef(3), stringRef(4));
+ node->commaToken = loc(2);
+ node->identifierToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 50: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2));
+ node->type = AST::UiPublicMember::Signal;
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(2);
+ node->parameters = sym(4).UiParameterList;
+ node->semicolonToken = loc(6);
+ sym(1).Node = node;
+} break;
+
+case 52: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(QStringRef(), stringRef(2));
+ node->type = AST::UiPublicMember::Signal;
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 54: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6));
+ node->typeModifier = stringRef(2);
+ node->propertyToken = loc(1);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(6);
+ node->semicolonToken = loc(7);
+ sym(1).Node = node;
+} break;
+
+case 56: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3));
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->semicolonToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 58: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4));
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->semicolonToken = loc(5);
+ sym(1).Node = node;
+} break;
+
+case 59: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3),
+ sym(5).Statement);
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 60: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4),
+ sym(6).Statement);
+ node->isReadonlyMember = true;
+ node->readonlyToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->colonToken = loc(5);
+ sym(1).Node = node;
+} break;
+
+case 61: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(3), stringRef(4),
+ sym(6).Statement);
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->colonToken = loc(5);
+ sym(1).Node = node;
+} break;
+
+case 62: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(4), stringRef(6));
+ node->typeModifier = stringRef(2);
+ node->propertyToken = loc(1);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(6);
+ node->semicolonToken = loc(7); // insert a fake ';' before ':'
+
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
+ propertyName->identifierToken = loc(6);
+ propertyName->next = 0;
+
+ AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(
+ propertyName, sym(9).UiArrayMemberList->finish());
+ binding->colonToken = loc(7);
+ binding->lbracketToken = loc(8);
+ binding->rbracketToken = loc(10);
+
+ node->binding = binding;
+
+ sym(1).Node = node;
+} break;
+
+case 63: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(stringRef(2), stringRef(3));
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->semicolonToken = loc(4); // insert a fake ';' before ':'
+
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
+ propertyName->identifierToken = loc(3);
+ propertyName->next = 0;
+
+ AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
+ propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer);
+ binding->colonToken = loc(4);
+
+ node->binding = binding;
+
+ sym(1).Node = node;
+} break;
+
+case 64: {
+ sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
+} break;
+
+case 65: {
+ sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
+} break;
+
+case 71: {
+ AST::ThisExpression *node = new (pool) AST::ThisExpression();
+ node->thisToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 72: {
+ AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 73: {
+ AST::NullExpression *node = new (pool) AST::NullExpression();
+ node->nullToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 74: {
+ AST::TrueLiteral *node = new (pool) AST::TrueLiteral();
+ node->trueToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 75: {
+ AST::FalseLiteral *node = new (pool) AST::FalseLiteral();
+ node->falseToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 76: {
+ AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval);
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+case 77:
+case 78: {
+ AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1));
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 79: {
+ bool rx = lexer->scanRegExp(Lexer::NoPrefix);
+ if (!rx) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
+ return false; // ### remove me
+ }
+
+ loc(1).length = lexer->tokenLength();
+ yylloc = loc(1); // adjust the location of the current token
+
+ AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
+ driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 80: {
+ bool rx = lexer->scanRegExp(Lexer::EqualPrefix);
+ if (!rx) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
+ return false;
+ }
+
+ loc(1).length = lexer->tokenLength();
+ yylloc = loc(1); // adjust the location of the current token
+
+ AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
+ driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 81: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0);
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 82: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish());
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 83: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ());
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 84: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
+ (AST::Elision *) 0);
+ node->lbracketToken = loc(1);
+ node->commaToken = loc(3);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 85: {
+ AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
+ sym(4).Elision->finish());
+ node->lbracketToken = loc(1);
+ node->commaToken = loc(3);
+ node->rbracketToken = loc(5);
+ sym(1).Node = node;
+} break;
+
+case 86: {
+ AST::ObjectLiteral *node = 0;
+ if (sym(2).Node)
+ node = new (pool) AST::ObjectLiteral(
+ sym(2).PropertyNameAndValueList->finish ());
+ else
+ node = new (pool) AST::ObjectLiteral();
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 87: {
+ AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral(
+ sym(2).PropertyNameAndValueList->finish ());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 88: {
+ AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression);
+ node->lparenToken = loc(1);
+ node->rparenToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 89: {
+ if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken,
+ QLatin1String("Ignored annotation")));
+
+ sym(1).Expression = mem->base;
+ }
+
+ if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) {
+ sym(1).UiQualifiedId = qualifiedId;
+ } else {
+ sym(1).UiQualifiedId = 0;
+
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ QLatin1String("Expected a qualified name id")));
+
+ return false; // ### recover
+ }
+} break;
+
+case 90: {
+ sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression);
+} break;
+
+case 91: {
+ sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression);
+} break;
+
+case 92: {
+ AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList,
+ (AST::Elision *) 0, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 93: {
+ AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(),
+ sym(4).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 94: {
+ AST::Elision *node = new (pool) AST::Elision();
+ node->commaToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 95: {
+ AST::Elision *node = new (pool) AST::Elision(sym(1).Elision);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 96: {
+ AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList(
+ sym(1).PropertyName, sym(3).Expression);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 97: {
+ AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList(
+ sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression);
+ node->commaToken = loc(2);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 98: {
+ AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+case 99:
+case 100: {
+ AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 101: {
+ AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 102: {
+ AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval);
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 103: {
+ AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 139: {
+ AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
+ node->lbracketToken = loc(2);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 140: {
+ AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
+ node->dotToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 141: {
+ AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList);
+ node->newToken = loc(1);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ sym(1).Node = node;
+} break;
+
+case 143: {
+ AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression);
+ node->newToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 144: {
+ AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 145: {
+ AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 146: {
+ AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
+ node->lbracketToken = loc(2);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 147: {
+ AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
+ node->dotToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 148: {
+ sym(1).Node = 0;
+} break;
+
+case 149: {
+ sym(1).Node = sym(1).ArgumentList->finish();
+} break;
+
+case 150: {
+ sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression);
+} break;
+
+case 151: {
+ AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 155: {
+ AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression);
+ node->incrementToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 156: {
+ AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression);
+ node->decrementToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 158: {
+ AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression);
+ node->deleteToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 159: {
+ AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression);
+ node->voidToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 160: {
+ AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression);
+ node->typeofToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 161: {
+ AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression);
+ node->incrementToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 162: {
+ AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression);
+ node->decrementToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 163: {
+ AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression);
+ node->plusToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 164: {
+ AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression);
+ node->minusToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 165: {
+ AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression);
+ node->tildeToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 166: {
+ AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression);
+ node->notToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 168: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Mul, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 169: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Div, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 170: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Mod, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 172: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Add, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 173: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Sub, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 175: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::LShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 176: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::RShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 177: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::URShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 179: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Lt, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 180: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Gt, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 181: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Le, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 182: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Ge, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 183: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::InstanceOf, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 184: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::In, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 186: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Lt, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 187: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Gt, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 188: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Le, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 189: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Ge, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 190: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::InstanceOf, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 192: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Equal, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 193: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::NotEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 194: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::StrictEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 195: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::StrictNotEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 197: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Equal, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 198: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::NotEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 199: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::StrictEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 200: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::StrictNotEqual, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 202: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitAnd, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 204: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitAnd, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 206: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitXor, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 208: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitXor, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 210: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitOr, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 212: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::BitOr, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 214: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::And, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 216: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::And, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 218: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Or, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 220: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ QSOperator::Or, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 222: {
+ AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
+ sym(3).Expression, sym(5).Expression);
+ node->questionToken = loc(2);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 224: {
+ AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
+ sym(3).Expression, sym(5).Expression);
+ node->questionToken = loc(2);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 226: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 228: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
+ sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 229: {
+ sym(1).ival = QSOperator::Assign;
+} break;
+
+case 230: {
+ sym(1).ival = QSOperator::InplaceMul;
+} break;
+
+case 231: {
+ sym(1).ival = QSOperator::InplaceDiv;
+} break;
+
+case 232: {
+ sym(1).ival = QSOperator::InplaceMod;
+} break;
+
+case 233: {
+ sym(1).ival = QSOperator::InplaceAdd;
+} break;
+
+case 234: {
+ sym(1).ival = QSOperator::InplaceSub;
+} break;
+
+case 235: {
+ sym(1).ival = QSOperator::InplaceLeftShift;
+} break;
+
+case 236: {
+ sym(1).ival = QSOperator::InplaceRightShift;
+} break;
+
+case 237: {
+ sym(1).ival = QSOperator::InplaceURightShift;
+} break;
+
+case 238: {
+ sym(1).ival = QSOperator::InplaceAnd;
+} break;
+
+case 239: {
+ sym(1).ival = QSOperator::InplaceXor;
+} break;
+
+case 240: {
+ sym(1).ival = QSOperator::InplaceOr;
+} break;
+
+case 242: {
+ AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 243: {
+ sym(1).Node = 0;
+} break;
+
+case 246: {
+ AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 247: {
+ sym(1).Node = 0;
+} break;
+
+case 264: {
+ AST::Block *node = new (pool) AST::Block(sym(2).StatementList);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 265: {
+ sym(1).Node = new (pool) AST::StatementList(sym(1).Statement);
+} break;
+
+case 266: {
+ sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement);
+} break;
+
+case 267: {
+ sym(1).Node = 0;
+} break;
+
+case 268: {
+ sym(1).Node = sym(1).StatementList->finish ();
+} break;
+
+case 270: {
+ AST::VariableStatement *node = new (pool) AST::VariableStatement(
+ sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST));
+ node->declarationKindToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 271: {
+ sym(1).ival = T_CONST;
+} break;
+
+case 272: {
+ sym(1).ival = T_VAR;
+} break;
+
+case 273: {
+ sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
+} break;
+
+case 274: {
+ AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList(
+ sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 275: {
+ sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
+} break;
+
+case 276: {
+ sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
+} break;
+
+case 277: {
+ AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression);
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 278: {
+ AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression);
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 279: {
+ // ### TODO: AST for initializer
+ sym(1) = sym(2);
+} break;
+
+case 280: {
+ sym(1).Node = 0;
+} break;
+
+case 282: {
+ // ### TODO: AST for initializer
+ sym(1) = sym(2);
+} break;
+
+case 283: {
+ sym(1).Node = 0;
+} break;
+
+case 285: {
+ AST::EmptyStatement *node = new (pool) AST::EmptyStatement();
+ node->semicolonToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 287: {
+ AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 288: {
+ AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement);
+ node->ifToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ node->elseToken = loc(6);
+ sym(1).Node = node;
+} break;
+
+case 289: {
+ AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement);
+ node->ifToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 291: {
+ AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression);
+ node->doToken = loc(1);
+ node->whileToken = loc(3);
+ node->lparenToken = loc(4);
+ node->rparenToken = loc(6);
+ node->semicolonToken = loc(7);
+ sym(1).Node = node;
+} break;
+
+case 292: {
+ AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement);
+ node->whileToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 293: {
+ AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression,
+ sym(5).Expression, sym(7).Expression, sym(9).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->firstSemicolonToken = loc(4);
+ node->secondSemicolonToken = loc(6);
+ node->rparenToken = loc(8);
+ sym(1).Node = node;
+} break;
+
+case 294: {
+ AST::LocalForStatement *node = new (pool) AST::LocalForStatement(
+ sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression,
+ sym(8).Expression, sym(10).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->varToken = loc(3);
+ node->firstSemicolonToken = loc(5);
+ node->secondSemicolonToken = loc(7);
+ node->rparenToken = loc(9);
+ sym(1).Node = node;
+} break;
+
+case 295: {
+ AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression,
+ sym(5).Expression, sym(7).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->inToken = loc(4);
+ node->rparenToken = loc(6);
+ sym(1).Node = node;
+} break;
+
+case 296: {
+ AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement(
+ sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->varToken = loc(3);
+ node->inToken = loc(5);
+ node->rparenToken = loc(7);
+ sym(1).Node = node;
+} break;
+
+case 298: {
+ AST::ContinueStatement *node = new (pool) AST::ContinueStatement();
+ node->continueToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 300: {
+ AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2));
+ node->continueToken = loc(1);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 302: {
+ AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef());
+ node->breakToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 304: {
+ AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2));
+ node->breakToken = loc(1);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 306: {
+ AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression);
+ node->returnToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 307: {
+ AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement);
+ node->withToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 308: {
+ AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock);
+ node->switchToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 309: {
+ AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 310: {
+ AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(5);
+ sym(1).Node = node;
+} break;
+
+case 311: {
+ sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause);
+} break;
+
+case 312: {
+ sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause);
+} break;
+
+case 313: {
+ sym(1).Node = 0;
+} break;
+
+case 314: {
+ sym(1).Node = sym(1).CaseClauses->finish ();
+} break;
+
+case 315: {
+ AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList);
+ node->caseToken = loc(1);
+ node->colonToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 316: {
+ AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList);
+ node->defaultToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+case 317:
+case 318: {
+ AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
+ node->identifierToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 319: {
+ AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
+ node->identifierToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 321: {
+ AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression);
+ node->throwToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 322: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 323: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 324: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 325: {
+ AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block);
+ node->catchToken = loc(1);
+ node->lparenToken = loc(2);
+ node->identifierToken = loc(3);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+} break;
+
+case 326: {
+ AST::Finally *node = new (pool) AST::Finally(sym(2).Block);
+ node->finallyToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 328: {
+ AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement();
+ node->debuggerToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+} break;
+
+case 329: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ sym(1).Node = node;
+} break;
+
+case 330: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
+ node->functionToken = loc(1);
+ if (! stringRef(2).isNull())
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ sym(1).Node = node;
+} break;
+
+case 331: {
+ AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+} break;
+
+case 332: {
+ AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3));
+ node->commaToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+} break;
+
+case 333: {
+ sym(1).Node = 0;
+} break;
+
+case 334: {
+ sym(1).Node = sym(1).FormalParameterList->finish ();
+} break;
+
+case 335: {
+ sym(1).Node = 0;
+} break;
+
+case 337: {
+ sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ());
+} break;
+
+case 339: {
+ sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ());
+} break;
+
+case 340: {
+ sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement);
+} break;
+
+case 341: {
+ sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement);
+} break;
+
+case 342: {
+ sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement);
+} break;
+
+case 343: {
+ sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration);
+} break;
+
+case 344: {
+ stringRef(1) = QStringRef();
+} break;
+
+case 346: {
+ sym(1).Node = 0;
+} break;
+
+ } // switch
+ action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT);
+ } // if
+ } while (action != 0);
+
+ if (first_token == last_token) {
+ const int errorState = state_stack[tos];
+
+ // automatic insertion of `;'
+ if (yytoken != -1 && t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) {
+ SavedToken &tk = token_buffer[0];
+ tk.token = yytoken;
+ tk.dval = yylval;
+ tk.spell = yytokenspell;
+ tk.loc = yylloc;
+
+ yylloc = yyprevlloc;
+ yylloc.offset += yylloc.length;
+ yylloc.startColumn += yylloc.length;
+ yylloc.length = 0;
+
+ //const QString msg = qApp->translate("QmlParser", "Missing `;'");
+ //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg));
+
+ first_token = &token_buffer[0];
+ last_token = &token_buffer[1];
+
+ yytoken = T_SEMICOLON;
+ yylval = 0;
+
+ action = errorState;
+
+ goto _Lcheck_token;
+ }
+
+ hadErrors = true;
+
+ token_buffer[0].token = yytoken;
+ token_buffer[0].dval = yylval;
+ token_buffer[0].spell = yytokenspell;
+ token_buffer[0].loc = yylloc;
+
+ token_buffer[1].token = yytoken = lexer->lex();
+ token_buffer[1].dval = yylval = lexer->tokenValue();
+ token_buffer[1].spell = yytokenspell = lexer->tokenSpell();
+ token_buffer[1].loc = yylloc = location(lexer);
+
+ if (t_action(errorState, yytoken)) {
+ QString msg;
+ int token = token_buffer[0].token;
+ if (token < 0 || token >= TERMINAL_COUNT)
+ msg = qApp->translate("QmlParser", "Syntax error");
+ else
+ msg = qApp->translate("QmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token]));
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+
+ action = errorState;
+ goto _Lcheck_token;
+ }
+
+ static int tokens[] = {
+ T_PLUS,
+ T_EQ,
+
+ T_COMMA,
+ T_COLON,
+ T_SEMICOLON,
+
+ T_RPAREN, T_RBRACKET, T_RBRACE,
+
+ T_NUMERIC_LITERAL,
+ T_IDENTIFIER,
+
+ T_LPAREN, T_LBRACKET, T_LBRACE,
+
+ EOF_SYMBOL
+ };
+
+ for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) {
+ int a = t_action(errorState, *tk);
+ if (a > 0 && t_action(a, yytoken)) {
+ const QString msg = qApp->translate("QmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk]));
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+
+ yytoken = *tk;
+ yylval = 0;
+ yylloc = token_buffer[0].loc;
+ yylloc.length = 0;
+
+ first_token = &token_buffer[0];
+ last_token = &token_buffer[2];
+
+ action = errorState;
+ goto _Lcheck_token;
+ }
+ }
+
+ for (int tk = 1; tk < TERMINAL_COUNT; ++tk) {
+ if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM ||
+ tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION ||
+ tk == T_FEED_JS_SOURCE_ELEMENT)
+ continue;
+
+ int a = t_action(errorState, tk);
+ if (a > 0 && t_action(a, yytoken)) {
+ const QString msg = qApp->translate("QmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk]));
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+
+ yytoken = tk;
+ yylval = 0;
+ yylloc = token_buffer[0].loc;
+ yylloc.length = 0;
+
+ action = errorState;
+ goto _Lcheck_token;
+ }
+ }
+
+ const QString msg = qApp->translate("QmlParser", "Syntax error");
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
+ }
+
+ return false;
+}
+
+} // namespace QbsQmlJS
diff --git a/src/lib/corelib/parser/qmljsparser_p.h b/src/lib/corelib/parser/qmljsparser_p.h
new file mode 100644
index 000000000..090369fae
--- /dev/null
+++ b/src/lib/corelib/parser/qmljsparser_p.h
@@ -0,0 +1,230 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** 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.
+**
+****************************************************************************/
+
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+//
+// This file is automatically generated from qmljs.g.
+// Changes will be lost.
+//
+
+#ifndef QMLJSPARSER_P_H
+#define QMLJSPARSER_P_H
+
+#include "qmljsglobal_p.h"
+#include "qmljsgrammar_p.h"
+#include "qmljsast_p.h"
+#include "qmljsengine_p.h"
+
+#include <QtCore/QList>
+#include <QtCore/QString>
+
+namespace QbsQmlJS {
+
+class Engine;
+
+class QML_PARSER_EXPORT Parser: protected QmlJSGrammar
+{
+public:
+ union Value {
+ int ival;
+ double dval;
+ AST::ArgumentList *ArgumentList;
+ AST::CaseBlock *CaseBlock;
+ AST::CaseClause *CaseClause;
+ AST::CaseClauses *CaseClauses;
+ AST::Catch *Catch;
+ AST::DefaultClause *DefaultClause;
+ AST::ElementList *ElementList;
+ AST::Elision *Elision;
+ AST::ExpressionNode *Expression;
+ AST::Finally *Finally;
+ AST::FormalParameterList *FormalParameterList;
+ AST::FunctionBody *FunctionBody;
+ AST::FunctionDeclaration *FunctionDeclaration;
+ AST::Node *Node;
+ AST::PropertyName *PropertyName;
+ AST::PropertyNameAndValueList *PropertyNameAndValueList;
+ AST::SourceElement *SourceElement;
+ AST::SourceElements *SourceElements;
+ AST::Statement *Statement;
+ AST::StatementList *StatementList;
+ AST::Block *Block;
+ AST::VariableDeclaration *VariableDeclaration;
+ AST::VariableDeclarationList *VariableDeclarationList;
+
+ AST::UiProgram *UiProgram;
+ AST::UiImportList *UiImportList;
+ AST::UiImport *UiImport;
+ AST::UiParameterList *UiParameterList;
+ AST::UiPublicMember *UiPublicMember;
+ AST::UiObjectDefinition *UiObjectDefinition;
+ AST::UiObjectInitializer *UiObjectInitializer;
+ AST::UiObjectBinding *UiObjectBinding;
+ AST::UiScriptBinding *UiScriptBinding;
+ AST::UiArrayBinding *UiArrayBinding;
+ AST::UiObjectMember *UiObjectMember;
+ AST::UiObjectMemberList *UiObjectMemberList;
+ AST::UiArrayMemberList *UiArrayMemberList;
+ AST::UiQualifiedId *UiQualifiedId;
+ };
+
+public:
+ Parser(Engine *engine);
+ ~Parser();
+
+ // parse a UI program
+ bool parse() { return parse(T_FEED_UI_PROGRAM); }
+ bool parseStatement() { return parse(T_FEED_JS_STATEMENT); }
+ bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); }
+ bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); }
+ bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); }
+ bool parseProgram() { return parse(T_FEED_JS_PROGRAM); }
+
+ AST::UiProgram *ast() const
+ { return AST::cast<AST::UiProgram *>(program); }
+
+ AST::Statement *statement() const
+ {
+ if (! program)
+ return 0;
+
+ return program->statementCast();
+ }
+
+ AST::ExpressionNode *expression() const
+ {
+ if (! program)
+ return 0;
+
+ return program->expressionCast();
+ }
+
+ AST::UiObjectMember *uiObjectMember() const
+ {
+ if (! program)
+ return 0;
+
+ return program->uiObjectMemberCast();
+ }
+
+ AST::Node *rootNode() const
+ { return program; }
+
+ QList<DiagnosticMessage> diagnosticMessages() const
+ { return diagnostic_messages; }
+
+ inline DiagnosticMessage diagnosticMessage() const
+ {
+ foreach (const DiagnosticMessage &d, diagnostic_messages) {
+ if (d.kind != DiagnosticMessage::Warning)
+ return d;
+ }
+
+ return DiagnosticMessage();
+ }
+
+ inline QString errorMessage() const
+ { return diagnosticMessage().message; }
+
+ inline int errorLineNumber() const
+ { return diagnosticMessage().loc.startLine; }
+
+ inline int errorColumnNumber() const
+ { return diagnosticMessage().loc.startColumn; }
+
+protected:
+ bool parse(int startToken);
+
+ void reallocateStack();
+
+ inline Value &sym(int index)
+ { return sym_stack [tos + index - 1]; }
+
+ inline QStringRef &stringRef(int index)
+ { return string_stack [tos + index - 1]; }
+
+ inline AST::SourceLocation &loc(int index)
+ { return location_stack [tos + index - 1]; }
+
+ AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr);
+
+protected:
+ Engine *driver;
+ MemoryPool *pool;
+ int tos;
+ int stack_size;
+ Value *sym_stack;
+ int *state_stack;
+ AST::SourceLocation *location_stack;
+ QStringRef *string_stack;
+
+ AST::Node *program;
+
+ // error recovery
+ enum { TOKEN_BUFFER_SIZE = 3 };
+
+ struct SavedToken {
+ int token;
+ double dval;
+ AST::SourceLocation loc;
+ QStringRef spell;
+ };
+
+ double yylval;
+ QStringRef yytokenspell;
+ AST::SourceLocation yylloc;
+ AST::SourceLocation yyprevlloc;
+
+ SavedToken token_buffer[TOKEN_BUFFER_SIZE];
+ SavedToken *first_token;
+ SavedToken *last_token;
+
+ QList<DiagnosticMessage> diagnostic_messages;
+};
+
+} // end of namespace QbsQmlJS
+
+
+
+#define J_SCRIPT_REGEXPLITERAL_RULE1 79
+
+#define J_SCRIPT_REGEXPLITERAL_RULE2 80
+
+#endif // QMLJSPARSER_P_H
diff --git a/src/lib/corelib/qbs.h b/src/lib/corelib/qbs.h
new file mode 100644
index 000000000..56ae00990
--- /dev/null
+++ b/src/lib/corelib/qbs.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_H
+#define QBS_H
+
+#include "api/jobs.h"
+#include "api/project.h"
+#include "api/projectdata.h"
+#include "logging/ilogsink.h"
+#include "tools/buildoptions.h"
+#include "tools/cleanoptions.h"
+#include "tools/error.h"
+#include "tools/installoptions.h"
+#include "tools/preferences.h"
+#include "tools/processresult.h"
+#include "tools/settings.h"
+#include "tools/setupprojectparameters.h"
+
+#endif // QBS_H
diff --git a/src/lib/corelib/tools/buildoptions.cpp b/src/lib/corelib/tools/buildoptions.cpp
new file mode 100644
index 000000000..2261124d8
--- /dev/null
+++ b/src/lib/corelib/tools/buildoptions.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** 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 "buildoptions.h"
+
+#include <QSharedData>
+#include <QThread>
+
+namespace qbs {
+namespace Internal {
+
+class BuildOptionsPrivate : public QSharedData
+{
+public:
+ BuildOptionsPrivate() : maxJobCount(0), dryRun(false), keepGoing(false), logElapsedTime(false)
+ {
+ }
+
+ QStringList changedFiles;
+ QStringList activeFileTags;
+ int maxJobCount;
+ bool dryRun;
+ bool keepGoing;
+ bool forceTimestampCheck;
+ bool logElapsedTime;
+};
+
+} // namespace Internal
+
+/*!
+ * \class BuildOptions
+ * \brief The \c BuildOptions class comprises parameters that influence the behavior of
+ * build and clean operations.
+ */
+
+/*!
+ * \brief Creates a \c BuildOptions object and initializes its members to sensible default values.
+ */
+BuildOptions::BuildOptions() : d(new Internal::BuildOptionsPrivate)
+{
+}
+
+BuildOptions::BuildOptions(const BuildOptions &other) : d(other.d)
+{
+}
+
+BuildOptions &BuildOptions::operator=(const BuildOptions &other)
+{
+ d = other.d;
+ return *this;
+}
+
+BuildOptions::~BuildOptions()
+{
+}
+
+/*!
+ * \brief If non-empty, qbs pretends that only these files have changed.
+ * By default, this list is empty.
+ */
+QStringList BuildOptions::changedFiles() const
+{
+ return d->changedFiles;
+}
+
+/*!
+ * \brief If the given list is empty, qbs will pretend only the listed files are changed.
+ * \note The list elements must be absolute file paths.
+ */
+void BuildOptions::setChangedFiles(const QStringList &changedFiles)
+{
+ d->changedFiles = changedFiles;
+}
+
+/*!
+ * \brief The list of active file tags.
+ * \sa setActiveFileTags
+ */
+QStringList BuildOptions::activeFileTags() const
+{
+ return d->activeFileTags;
+}
+
+/*!
+ * \brief Set the list of active file tags.
+ * If this list is non-empty, then every transformer with non-matching output file tags is skipped.
+ * E.g. set changed files to "foo.cpp" and activeFileTags to ["obj"] to run the compiler
+ * on foo.cpp without further processing like linking.
+ * \sa activeFileTags
+ */
+void BuildOptions::setActiveFileTags(const QStringList &fileTags)
+{
+ d->activeFileTags = fileTags;
+}
+
+/*!
+ * \brief Returns the default value for \c maxJobCount.
+ * This value will be used when \c maxJobCount has not been set explicitly.
+ */
+int BuildOptions::defaultMaxJobCount()
+{
+ return QThread::idealThreadCount();
+}
+
+/*!
+ * \brief Returns the maximum number of build commands to run concurrently.
+ * If the value is not valid (i.e. <= 0), a sensible one will be derived at build time
+ * from the number of available processor cores at build time.
+ * The default is 0.
+ * \sa BuildOptions::defaultMaxJobCount
+ */
+int BuildOptions::maxJobCount() const
+{
+ return d->maxJobCount;
+}
+
+/*!
+ * \brief Controls how many build commands can be run in parallel.
+ * A value <= 0 leaves the decision to qbs.
+ */
+void BuildOptions::setMaxJobCount(int jobCount)
+{
+ d->maxJobCount = jobCount;
+}
+
+/*!
+ * \brief Returns true iff qbs will not actually execute any commands, but just show what
+ * would happen.
+ * The default is false.
+ */
+bool BuildOptions::dryRun() const
+{
+ return d->dryRun;
+}
+
+/*!
+ * \brief Controls whether qbs will actually build something.
+ * If the argument is true, qbs will just emit information about what it would do. Otherwise,
+ * the build is actually done.
+ * \note After you build with this setting enabled, the next call to \c build() on the same
+ * \c Project object will do nothing, since the internal state needs to be updated the same way
+ * as if an actual build had happened. You'll need to create a new \c Project object to do
+ * a real build afterwards.
+ */
+void BuildOptions::setDryRun(bool dryRun)
+{
+ d->dryRun = dryRun;
+}
+
+/*!
+ * \brief Returns true iff a build will continue after an error.
+ * E.g. a failed compile command will result in a warning message being printed, instead of
+ * stopping the build process right away. However, there might still be fatal errors after which the
+ * build process cannot continue.
+ * The default is \c false.
+ */
+bool BuildOptions::keepGoing() const
+{
+ return d->keepGoing;
+}
+
+/*!
+ * \brief Controls whether a qbs will try to continue building after an error has occurred.
+ */
+void BuildOptions::setKeepGoing(bool keepGoing)
+{
+ d->keepGoing = keepGoing;
+}
+
+/*!
+ * \brief Returns true if qbs is to use physical timestamps instead of the timestamps stored in the
+ * build graph.
+ * The default is \c false.
+ */
+bool BuildOptions::forceTimestampCheck() const
+{
+ return d->forceTimestampCheck;
+}
+
+/*!
+ * \brief Controls whether qbs should use physical timestamps for up-to-date checks.
+ */
+void BuildOptions::setForceTimestampCheck(bool enabled)
+{
+ d->forceTimestampCheck = enabled;
+}
+
+/*!
+ * \brief Returns true iff the time the operation takes will be logged.
+ * The default is \c false.
+ */
+bool BuildOptions::logElapsedTime() const
+{
+ return d->logElapsedTime;
+}
+
+/*!
+ * \brief Controls whether the build time will be measured and logged.
+ */
+void BuildOptions::setLogElapsedTime(bool log)
+{
+ d->logElapsedTime = log;
+}
+
+
+bool operator==(const BuildOptions &bo1, const BuildOptions &bo2)
+{
+ return bo1.changedFiles() == bo2.changedFiles()
+ && bo1.dryRun() == bo2.dryRun()
+ && bo1.keepGoing() == bo2.keepGoing()
+ && bo1.logElapsedTime() == bo2.logElapsedTime()
+ && bo1.maxJobCount() == bo2.maxJobCount();
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/buildoptions.h b/src/lib/corelib/tools/buildoptions.h
new file mode 100644
index 000000000..2c277fc3b
--- /dev/null
+++ b/src/lib/corelib/tools/buildoptions.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_BUILDOPTIONS_H
+#define QBS_BUILDOPTIONS_H
+
+#include "qbs_export.h"
+
+#include <QSharedDataPointer>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal { class BuildOptionsPrivate; }
+
+class QBS_EXPORT BuildOptions
+{
+public:
+ BuildOptions();
+ BuildOptions(const BuildOptions &other);
+ BuildOptions &operator=(const BuildOptions &other);
+ ~BuildOptions();
+
+ QStringList changedFiles() const;
+ void setChangedFiles(const QStringList &changedFiles);
+
+ QStringList activeFileTags() const;
+ void setActiveFileTags(const QStringList &fileTags);
+
+ static int defaultMaxJobCount();
+ int maxJobCount() const;
+ void setMaxJobCount(int jobCount);
+
+ bool dryRun() const;
+ void setDryRun(bool dryRun);
+
+ bool keepGoing() const;
+ void setKeepGoing(bool keepGoing);
+
+ bool forceTimestampCheck() const;
+ void setForceTimestampCheck(bool enabled);
+
+ bool logElapsedTime() const;
+ void setLogElapsedTime(bool log);
+
+private:
+ QSharedDataPointer<Internal::BuildOptionsPrivate> d;
+};
+
+bool operator==(const BuildOptions &bo1, const BuildOptions &bo2);
+inline bool operator!=(const BuildOptions &bo1, const BuildOptions &bo2) { return !(bo1 == bo2); }
+
+} // namespace qbs
+
+#endif // QBS_BUILDOPTIONS_H
diff --git a/src/lib/corelib/tools/cleanoptions.cpp b/src/lib/corelib/tools/cleanoptions.cpp
new file mode 100644
index 000000000..359fd47dc
--- /dev/null
+++ b/src/lib/corelib/tools/cleanoptions.cpp
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** 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 "cleanoptions.h"
+
+#include <QSharedData>
+
+namespace qbs {
+namespace Internal {
+
+class CleanOptionsPrivate : public QSharedData
+{
+public:
+ CleanOptionsPrivate()
+ : cleanType(CleanOptions::CleanupAll), dryRun(false),
+ keepGoing(false), logElapsedTime(false)
+ { }
+
+ CleanOptions::CleanType cleanType;
+ bool dryRun;
+ bool keepGoing;
+ bool logElapsedTime;
+};
+
+}
+
+/*!
+ * \class CleanOptions
+ * \brief The \c CleanOptions class comprises parameters that influence the behavior of
+ * cleaning operations.
+ */
+
+/*!
+ * \enum CleanOptions::CleanType
+ * This enum type specifies which kind of build artifacts to remove.
+ * \value CleanupAll Indicates that all files created by the build process should be removed.
+ * \value CleanupTemporaries Indicates that only intermediate build artifacts should be removed.
+ * If, for example, the product to clean up for is a Linux shared library, the .so file
+ * would be left on the disk, but the .o files would be removed.
+ */
+
+CleanOptions::CleanOptions() : d(new Internal::CleanOptionsPrivate)
+{
+}
+
+CleanOptions::CleanOptions(const CleanOptions &other) : d(other.d)
+{
+}
+
+CleanOptions &CleanOptions::operator=(const CleanOptions &other)
+{
+ d = other.d;
+ return *this;
+}
+
+CleanOptions::~CleanOptions()
+{
+}
+
+/*!
+ * \brief Returns information about which type of artifacts will be removed.
+ */
+CleanOptions::CleanType CleanOptions::cleanType() const
+{
+ return d->cleanType;
+}
+
+/*!
+ * \brief Controls which kind of artifacts to remove.
+ * \sa CleanOptions::CleanType
+ */
+void CleanOptions::setCleanType(CleanOptions::CleanType cleanType)
+{
+ d->cleanType = cleanType;
+}
+
+/*!
+ * \brief Returns true iff qbs will not actually remove any files, but just show what would happen.
+ * The default is false.
+ */
+bool CleanOptions::dryRun() const
+{
+ return d->dryRun;
+}
+
+/*!
+ * \brief Controls whether clean-up will actually take place.
+ * If the argument is true, then qbs will emit information about which files would be removed
+ * instead of actually doing it.
+ */
+void CleanOptions::setDryRun(bool dryRun)
+{
+ d->dryRun = dryRun;
+}
+
+/*!
+ * Returns true iff clean-up will continue if an error occurs.
+ * The default is false.
+ */
+bool CleanOptions::keepGoing() const
+{
+ return d->keepGoing;
+}
+
+/*!
+ * \brief Controls whether to abort on errors.
+ * If the argument is true, then if a file cannot be removed e.g. due to a permission problem,
+ * a warning will be printed and the clean-up will continue. If the argument is false,
+ * then the clean-up will abort immediately in case of an error.
+ */
+void CleanOptions::setKeepGoing(bool keepGoing)
+{
+ d->keepGoing = keepGoing;
+}
+
+/*!
+ * \brief Returns true iff the time the operation takes will be logged.
+ * The default is false.
+ */
+bool CleanOptions::logElapsedTime() const
+{
+ return d->logElapsedTime;
+}
+
+/*!
+ * \brief Controls whether the clean-up time will be measured and logged.
+ */
+void CleanOptions::setLogElapsedTime(bool log)
+{
+ d->logElapsedTime = log;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/cleanoptions.h b/src/lib/corelib/tools/cleanoptions.h
new file mode 100644
index 000000000..0e3a0a564
--- /dev/null
+++ b/src/lib/corelib/tools/cleanoptions.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_CLEANOPTIONS_H
+#define QBS_CLEANOPTIONS_H
+
+#include "qbs_export.h"
+
+#include <QSharedDataPointer>
+#include <QString>
+
+namespace qbs {
+namespace Internal { class CleanOptionsPrivate; }
+
+class QBS_EXPORT CleanOptions
+{
+public:
+ CleanOptions();
+ CleanOptions(const CleanOptions &other);
+ CleanOptions &operator=(const CleanOptions &other);
+ ~CleanOptions();
+
+ enum CleanType { CleanupAll, CleanupTemporaries };
+ CleanType cleanType() const;
+ void setCleanType(CleanType cleanType);
+
+ bool dryRun() const;
+ void setDryRun(bool dryRun);
+
+ bool keepGoing() const;
+ void setKeepGoing(bool keepGoing);
+
+ bool logElapsedTime() const;
+ void setLogElapsedTime(bool log);
+
+private:
+ QSharedDataPointer<Internal::CleanOptionsPrivate> d;
+};
+
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/tools/codelocation.cpp b/src/lib/corelib/tools/codelocation.cpp
new file mode 100644
index 000000000..70a1814cd
--- /dev/null
+++ b/src/lib/corelib/tools/codelocation.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** 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 "codelocation.h"
+
+#include <QDataStream>
+#include <QDir>
+#include <QRegExp>
+#include <QSharedData>
+#include <QString>
+
+namespace qbs {
+
+class CodeLocation::CodeLocationPrivate : public QSharedData
+{
+public:
+ QString fileName;
+ int line;
+ int column;
+};
+
+CodeLocation::CodeLocation() : d(new CodeLocationPrivate)
+{
+ d->line = d->column = -1;
+}
+
+CodeLocation::CodeLocation(const QString &aFileName, int aLine, int aColumn)
+ : d(new CodeLocationPrivate)
+{
+ d->fileName = aFileName;
+ d->line = aLine;
+ d->column = aColumn;
+}
+
+CodeLocation::CodeLocation(const CodeLocation &other) : d(other.d)
+{
+}
+
+CodeLocation &CodeLocation::operator=(const CodeLocation &other)
+{
+ d = other.d;
+ return *this;
+}
+
+CodeLocation::~CodeLocation()
+{
+}
+
+QString CodeLocation::fileName() const
+{
+ return d->fileName;
+}
+
+int CodeLocation::line() const
+{
+ return d->line;
+}
+
+int CodeLocation::column() const
+{
+ return d->column;
+}
+
+bool CodeLocation::isValid() const
+{
+ return !fileName().isEmpty();
+}
+
+QString CodeLocation::toString() const
+{
+ QString str;
+ if (isValid()) {
+ str = QDir::toNativeSeparators(fileName());
+ QString lineAndColumn;
+ if (line() > 0 && !str.contains(QRegExp(QLatin1String(":[0-9]+$"))))
+ lineAndColumn += QLatin1Char(':') + QString::number(line());
+ if (column() > 0 && !str.contains(QRegExp(QLatin1String(":[0-9]+:[0-9]+$"))))
+ lineAndColumn += QLatin1Char(':') + QString::number(column());
+ str += lineAndColumn;
+ }
+ return str;
+}
+
+bool operator==(const CodeLocation &cl1, const CodeLocation &cl2)
+{
+ return cl1.fileName() == cl2.fileName() && cl1.line() == cl2.line()
+ && cl1.column() == cl2.column();
+}
+
+bool operator!=(const CodeLocation &cl1, const CodeLocation &cl2)
+{
+ return !(cl1 == cl2);
+}
+
+QDataStream &operator<<(QDataStream &s, const CodeLocation &o)
+{
+ s << o.fileName();
+ s << o.line();
+ s << o.column();
+ return s;
+}
+
+QDataStream &operator>>(QDataStream &s, CodeLocation &o)
+{
+ QString fileName;
+ int line;
+ int column;
+ s >> fileName;
+ s >> line;
+ s >> column;
+ o = CodeLocation(fileName, line, column);
+ return s;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/codelocation.h b/src/lib/corelib/tools/codelocation.h
new file mode 100644
index 000000000..204192b5a
--- /dev/null
+++ b/src/lib/corelib/tools/codelocation.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_SOURCELOCATION_H
+#define QBS_SOURCELOCATION_H
+
+#include "qbs_export.h"
+
+#include <QExplicitlySharedDataPointer>
+
+QT_BEGIN_NAMESPACE
+class QDataStream;
+class QString;
+QT_END_NAMESPACE
+
+namespace qbs {
+
+class QBS_EXPORT CodeLocation
+{
+public:
+ CodeLocation();
+ CodeLocation(const QString &aFileName, int aLine = -1, int aColumn = -1);
+ CodeLocation(const CodeLocation &other);
+ CodeLocation &operator=(const CodeLocation &other);
+ ~CodeLocation();
+
+ QString fileName() const;
+ int line() const;
+ int column() const;
+
+ bool isValid() const;
+ QString toString() const;
+private:
+ class CodeLocationPrivate;
+ QExplicitlySharedDataPointer<CodeLocationPrivate> d;
+};
+
+QBS_EXPORT bool operator==(const CodeLocation &cl1, const CodeLocation &cl2);
+QBS_EXPORT bool operator!=(const CodeLocation &cl1, const CodeLocation &cl2);
+
+QDataStream &operator<<(QDataStream &s, const CodeLocation &o);
+QDataStream &operator>>(QDataStream &s, CodeLocation &o);
+
+} // namespace qbs
+
+#endif // QBS_SOURCELOCATION_H
diff --git a/src/lib/corelib/tools/error.cpp b/src/lib/corelib/tools/error.cpp
new file mode 100644
index 000000000..4bcdc620c
--- /dev/null
+++ b/src/lib/corelib/tools/error.cpp
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** 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 "error.h"
+
+#include <QSharedData>
+#include <QStringList>
+
+namespace qbs {
+
+class ErrorItem::ErrorItemPrivate : public QSharedData
+{
+public:
+ QString description;
+ CodeLocation codeLocation;
+};
+
+/*!
+ * \class ErrorData
+ * \brief The \c ErrorData class describes (part of) an error resulting from a qbs operation.
+ * It is always delivered as part of an \c Error.
+ * \sa Error
+ */
+
+ErrorItem::ErrorItem() : d(new ErrorItemPrivate)
+{
+}
+
+ErrorItem::ErrorItem(const QString &description, const CodeLocation &codeLocation)
+ : d(new ErrorItemPrivate)
+{
+ d->description = description;
+ d->codeLocation = codeLocation;
+}
+
+ErrorItem::ErrorItem(const ErrorItem &rhs) : d(rhs.d)
+{
+}
+
+ErrorItem &ErrorItem::operator=(const ErrorItem &other)
+{
+ d = other.d;
+ return *this;
+}
+
+ErrorItem::~ErrorItem()
+{
+}
+
+QString ErrorItem::description() const
+{
+ return d->description;
+}
+
+CodeLocation ErrorItem::codeLocation() const
+{
+ return d->codeLocation;
+}
+
+/*!
+ * \fn const QString &ErrorData::description() const
+ * \brief A general description of the error.
+ */
+
+ /*!
+ * \fn const QString &ErrorData::codeLocation() const
+ * \brief The location at which file in which the error occurred.
+ * \note This information might not be applicable, in which case location().isValid() returns false
+ */
+
+/*!
+ * \brief A full textual description of the error using all available information.
+ */
+QString ErrorItem::toString() const
+{
+ QString str = codeLocation().toString();
+ if (!str.isEmpty())
+ str += QLatin1Char(' ');
+ return str += description();
+}
+
+
+class ErrorInfo::ErrorInfoPrivate : public QSharedData
+{
+public:
+ ErrorInfoPrivate() : internalError(false) { }
+
+ QList<ErrorItem> items;
+ bool internalError;
+};
+
+/*!
+ * \class Error
+ * \brief Represents an error resulting from a qbs operation.
+ * It is made up of one or more \c ErrorData objects.
+ * \sa ErrorData
+ */
+
+ErrorInfo::ErrorInfo() : d(new ErrorInfoPrivate)
+{
+}
+
+ErrorInfo::ErrorInfo(const ErrorInfo &rhs) : d(rhs.d)
+{
+}
+
+ErrorInfo::ErrorInfo(const QString &description, const CodeLocation &location, bool internalError)
+ : d(new ErrorInfoPrivate)
+{
+ append(description, location);
+ d->internalError = internalError;
+}
+
+ErrorInfo &ErrorInfo::operator =(const ErrorInfo &other)
+{
+ d = other.d;
+ return *this;
+}
+
+ErrorInfo::~ErrorInfo()
+{
+}
+
+void ErrorInfo::append(const QString &description, const CodeLocation &location)
+{
+ d->items.append(ErrorItem(description, location));
+}
+
+void ErrorInfo::prepend(const QString &description, const CodeLocation &location)
+{
+ d->items.prepend(ErrorItem(description, location));
+}
+
+/*!
+ * \brief A list of concrete error description.
+ * Most often, there will be one element in this list, but there can be more e.g. to illustrate
+ * how an error condition propagates through several source files.
+ */
+QList<ErrorItem> ErrorInfo::items() const
+{
+ return d->items;
+}
+
+void ErrorInfo::clear()
+{
+ d->items.clear();
+}
+
+/*!
+ * \brief A complete textual description of the error.
+ * All "sub-errors" will be represented.
+ * \sa Error::entries()
+ */
+QString ErrorInfo::toString() const
+{
+ QStringList lines;
+ foreach (const ErrorItem &e, d->items)
+ lines.append(e.toString());
+ return lines.join(QLatin1String("\n"));
+}
+
+/*!
+ * \brief Returns true if this error represents a bug in qbs, false otherwise.
+ */
+bool ErrorInfo::isInternalError() const
+{
+ return d->internalError;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/error.h b/src/lib/corelib/tools/error.h
new file mode 100644
index 000000000..3aae9e239
--- /dev/null
+++ b/src/lib/corelib/tools/error.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_ERROR
+#define QBS_ERROR
+
+#include "codelocation.h"
+
+#include <QExplicitlySharedDataPointer>
+#include <QList>
+#include <QMetaType>
+#include <QSharedDataPointer>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+namespace qbs {
+class CodeLocation;
+
+class QBS_EXPORT ErrorItem
+{
+ friend class ErrorInfo;
+public:
+ ErrorItem();
+ ErrorItem(const ErrorItem &rhs);
+ ErrorItem &operator=(const ErrorItem &other);
+ ~ErrorItem();
+
+ QString description() const;
+ CodeLocation codeLocation() const;
+ QString toString() const;
+
+private:
+ ErrorItem(const QString &description, const CodeLocation &codeLocation);
+
+ class ErrorItemPrivate;
+ QExplicitlySharedDataPointer<ErrorItemPrivate> d;
+};
+
+class QBS_EXPORT ErrorInfo
+{
+public:
+ ErrorInfo();
+ ErrorInfo(const ErrorInfo &rhs);
+ ErrorInfo(const QString &description, const CodeLocation &location = CodeLocation(),
+ bool internalError = false);
+ ErrorInfo &operator=(const ErrorInfo &other);
+ ~ErrorInfo();
+
+ void append(const QString &description, const CodeLocation &location = CodeLocation());
+ void prepend(const QString &description, const CodeLocation &location = CodeLocation());
+ QList<ErrorItem> items() const;
+ bool hasError() const { return !items().isEmpty(); }
+ void clear();
+ QString toString() const;
+ bool isInternalError() const;
+
+private:
+ class ErrorInfoPrivate;
+ QSharedDataPointer<ErrorInfoPrivate> d;
+};
+
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::ErrorInfo)
+
+#endif // QBS_ERROR
diff --git a/src/lib/corelib/tools/fileinfo.cpp b/src/lib/corelib/tools/fileinfo.cpp
new file mode 100644
index 000000000..9eca8c2ca
--- /dev/null
+++ b/src/lib/corelib/tools/fileinfo.cpp
@@ -0,0 +1,477 @@
+/****************************************************************************
+**
+** 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 "fileinfo.h"
+
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+#include <tools/qbsassert.h>
+
+#include <QCoreApplication>
+#include <QDateTime>
+#include <QDir>
+#include <QFileInfo>
+
+#if defined(Q_OS_UNIX)
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#elif defined(Q_OS_WIN)
+#include <qt_windows.h>
+#endif
+
+namespace qbs {
+namespace Internal {
+
+QString FileInfo::fileName(const QString &fp)
+{
+ int last = fp.lastIndexOf(QLatin1Char('/'));
+ if (last < 0)
+ return fp;
+ return fp.mid(last + 1);
+}
+
+QString FileInfo::baseName(const QString &fp)
+{
+ QString fn = fileName(fp);
+ int dot = fn.indexOf(QLatin1Char('.'));
+ if (dot < 0)
+ return fp;
+ return fn.mid(0, dot);
+}
+
+QString FileInfo::completeBaseName(const QString &fp)
+{
+ QString fn = fileName(fp);
+ int dot = fn.lastIndexOf(QLatin1Char('.'));
+ if (dot < 0)
+ return fp;
+ return fn.mid(0, dot);
+}
+
+QString FileInfo::path(const QString &fp)
+{
+ if (fp.isEmpty())
+ return QString();
+ if (fp.at(fp.size() - 1) == QLatin1Char('/'))
+ return fp;
+ int last = fp.lastIndexOf(QLatin1Char('/'));
+ if (last < 0)
+ return QLatin1String(".");
+ return QDir::cleanPath(fp.mid(0, last));
+}
+
+void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QString *dirPath, QString *fileName)
+{
+ int idx = filePath.lastIndexOf(QLatin1Char('/'));
+ if (idx < 0) {
+ dirPath->clear();
+ *fileName = filePath;
+ return;
+ }
+ *dirPath = filePath.left(idx);
+ *fileName = filePath.mid(idx + 1);
+}
+
+void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QStringRef *dirPath, QStringRef *fileName)
+{
+ int idx = filePath.lastIndexOf(QLatin1Char('/'));
+ if (idx < 0) {
+ dirPath->clear();
+ *fileName = QStringRef(&filePath);
+ return;
+ }
+ *dirPath = filePath.leftRef(idx);
+ *fileName = filePath.midRef(idx + 1);
+}
+
+bool FileInfo::exists(const QString &fp)
+{
+ return FileInfo(fp).exists();
+}
+
+// from creator/src/shared/proparser/ioutils.cpp
+bool FileInfo::isAbsolute(const QString &path)
+{
+ const int n = path.size();
+ if (n == 0)
+ return false;
+ const QChar at0 = path.at(0);
+ if (at0 == QLatin1Char('/'))
+ return true;
+ if (HostOsInfo::isWindowsHost()) {
+ if (at0 == QLatin1Char('\\'))
+ return true;
+ // Unlike QFileInfo, this won't accept a relative path with a drive letter.
+ // Such paths result in a royal mess anyway ...
+ if (n >= 3 && path.at(1) == QLatin1Char(':') && at0.isLetter()
+ && (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\')))
+ return true;
+ }
+ return false;
+}
+
+bool FileInfo::isPattern(const QString &str)
+{
+ return isPattern(QStringRef(&str));
+}
+
+bool FileInfo::isPattern(const QStringRef &str)
+{
+ for (int i = 0; i < str.size(); ++i) {
+ const QChar ch = str.at(i);
+ if (ch == QLatin1Char('*') || ch == QLatin1Char('?')
+ || ch == QLatin1Char(']') || ch == QLatin1Char('[')) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Concatenates the paths \a base and \a rel.
+ * Base must be an absolute path.
+ * Double dots at the start of \a rel are handled.
+ * This function assumes that both paths are clean, that is they don't contain
+ * double slashes or redundant dot parts.
+ */
+QString FileInfo::resolvePath(const QString &base, const QString &rel)
+{
+ QBS_ASSERT(isAbsolute(base), return QString());
+ if (isAbsolute(rel))
+ return rel;
+ if (rel.size() == 1 && rel.at(0) == QLatin1Char('.'))
+ return base;
+ if (rel.size() == 1 && rel.at(0) == QLatin1Char('~'))
+ return QDir::homePath();
+ if (rel.startsWith(QLatin1String("~/")))
+ return QDir::homePath() + rel.mid(1);
+
+ QString r = base;
+ if (r.endsWith(QLatin1Char('/')))
+ r.chop(1);
+
+ QString s = rel;
+ while (s.startsWith(QLatin1String("../"))) {
+ s.remove(0, 3);
+ int idx = r.lastIndexOf(QLatin1Char('/'));
+ if (idx >= 0)
+ r.truncate(idx);
+ }
+
+ r.reserve(r.length() + 1 + s.length());
+ r += QLatin1Char('/');
+ r += s;
+ return r;
+}
+
+bool FileInfo::globMatches(const QRegExp &regexp, const QString &fileName)
+{
+ const QString pattern = regexp.pattern();
+ // May be it's simple wildcard, i.e. "*.cpp"?
+ if (pattern.startsWith(QLatin1Char('*')) && !isPattern(pattern.midRef(1))) {
+ // Yes, it's rather simple to just check the extension
+ return fileName.endsWith(pattern.midRef(1));
+ }
+ return regexp.exactMatch(fileName);
+}
+
+bool FileInfo::isFileCaseCorrect(const QString &filePath)
+{
+#if defined(Q_OS_WIN)
+ // QFileInfo::canonicalFilePath() does not return the real case of the file path on Windows.
+ QFileInfo fi(filePath);
+ const QString absolute = fi.absoluteFilePath();
+ WIN32_FIND_DATA fd;
+ HANDLE hFindFile = ::FindFirstFile((wchar_t*)absolute.utf16(), &fd);
+ if (hFindFile == INVALID_HANDLE_VALUE)
+ return false;
+ const QString actualFileName = QString::fromWCharArray(fd.cFileName);
+ FindClose(hFindFile);
+ return actualFileName == fi.fileName();
+#elif defined(Q_OS_DARWIN)
+ QFileInfo fi(filePath);
+ return fi.absoluteFilePath() == fi.canonicalFilePath();
+#else
+ Q_UNUSED(filePath)
+ return true;
+#endif
+}
+
+#if defined(Q_OS_WIN)
+
+#define z(x) reinterpret_cast<WIN32_FILE_ATTRIBUTE_DATA*>(const_cast<FileInfo::InternalStatType*>(&x))
+
+template<bool> struct CompileTimeAssert;
+template<> struct CompileTimeAssert<true> {};
+
+FileInfo::FileInfo(const QString &fileName)
+{
+ static CompileTimeAssert<
+ sizeof(FileInfo::InternalStatType) == sizeof(WIN32_FILE_ATTRIBUTE_DATA)
+ > internal_type_has_wrong_size;
+ Q_UNUSED(internal_type_has_wrong_size);
+ if (!GetFileAttributesEx(reinterpret_cast<const WCHAR*>(fileName.utf16()),
+ GetFileExInfoStandard, &m_stat))
+ {
+ ZeroMemory(z(m_stat), sizeof(WIN32_FILE_ATTRIBUTE_DATA));
+ z(m_stat)->dwFileAttributes = INVALID_FILE_ATTRIBUTES;
+ }
+}
+
+bool FileInfo::exists() const
+{
+ return z(m_stat)->dwFileAttributes != INVALID_FILE_ATTRIBUTES;
+}
+
+FileTime FileInfo::lastModified() const
+{
+ const FileTime::InternalType* ft_it;
+ ft_it = reinterpret_cast<const FileTime::InternalType*>(&z(m_stat)->ftLastWriteTime);
+ return FileTime(*ft_it);
+}
+
+FileTime FileInfo::lastStatusChange() const
+{
+ return lastModified();
+}
+
+bool FileInfo::isDir() const
+{
+ return z(m_stat)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
+}
+
+static QString resolveSymlinks(const QString &fileName)
+{
+ QFileInfo fi(fileName);
+ while (fi.isSymLink())
+ fi.setFile(fi.symLinkTarget());
+ return fi.absoluteFilePath();
+}
+
+QString applicationDirPath()
+{
+ static const QString appDirPath = FileInfo::path(resolveSymlinks(QCoreApplication::applicationFilePath()));
+ return appDirPath;
+}
+
+#elif defined(Q_OS_UNIX)
+
+FileInfo::FileInfo(const QString &fileName)
+{
+ if (stat(fileName.toLocal8Bit(), &m_stat) == -1)
+ m_stat.st_mtime = 0;
+}
+
+bool FileInfo::exists() const
+{
+ return m_stat.st_mtime != 0;
+}
+
+FileTime FileInfo::lastModified() const
+{
+ return m_stat.st_mtime;
+}
+
+FileTime FileInfo::lastStatusChange() const
+{
+ return m_stat.st_ctime;
+}
+
+bool FileInfo::isDir() const
+{
+ return S_ISDIR(m_stat.st_mode);
+}
+
+#endif
+
+// adapted from qtc/plugins/vcsbase/cleandialog.cpp
+bool removeFileRecursion(const QFileInfo &f, QString *errorMessage)
+{
+ if (!f.exists())
+ return true;
+ if (f.isDir()) {
+ const QDir dir(f.absoluteFilePath());
+ foreach(const QFileInfo &fi, dir.entryInfoList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::Hidden))
+ removeFileRecursion(fi, errorMessage);
+ QDir parent = f.absoluteDir();
+ if (!parent.rmdir(f.fileName())) {
+ errorMessage->append(Tr::tr("The directory %1 could not be deleted.").
+ arg(QDir::toNativeSeparators(f.absoluteFilePath())));
+ return false;
+ }
+ } else {
+ QFile file(f.absoluteFilePath());
+ file.setPermissions(f.permissions() | QFile::WriteUser);
+ if (!file.remove()) {
+ if (!errorMessage->isEmpty())
+ errorMessage->append(QLatin1Char('\n'));
+ errorMessage->append(Tr::tr("The file %1 could not be deleted.").
+ arg(QDir::toNativeSeparators(f.absoluteFilePath())));
+ return false;
+ }
+ }
+ return true;
+}
+
+bool removeDirectoryWithContents(const QString &path, QString *errorMessage)
+{
+ QFileInfo f(path);
+ if (f.exists() && !f.isDir()) {
+ *errorMessage = Tr::tr("%1 is not a directory.").arg(QDir::toNativeSeparators(path));
+ return false;
+ }
+ return removeFileRecursion(f, errorMessage);
+}
+
+/*!
+ * Returns the stored link target of the symbolic link \a{filePath}.
+ * Unlike QFileInfo::symLinkTarget, this will not make the link target an absolute path.
+ */
+static QByteArray storedLinkTarget(const QString &filePath)
+{
+ QByteArray result;
+
+#ifdef Q_OS_UNIX
+ const QByteArray nativeFilePath = QFile::encodeName(filePath);
+ ssize_t len;
+ while (true) {
+ struct stat sb;
+ if (lstat(nativeFilePath.constData(), &sb)) {
+ qWarning("storedLinkTarget: lstat for %s failed with error code %d",
+ nativeFilePath.constData(), errno);
+ return QByteArray();
+ }
+
+ result.resize(sb.st_size);
+ len = readlink(nativeFilePath.constData(), result.data(), sb.st_size + 1);
+ if (len < 0) {
+ qWarning("storedLinkTarget: readlink for %s failed with error code %d",
+ nativeFilePath.constData(), errno);
+ return QByteArray();
+ }
+
+ if (len < sb.st_size) {
+ result.resize(len);
+ break;
+ }
+ if (len == sb.st_size)
+ break;
+ }
+#else
+ Q_UNUSED(filePath);
+#endif // Q_OS_UNIX
+
+ return result;
+}
+
+static bool createSymLink(const QByteArray &path1, const QString &path2)
+{
+#ifdef Q_OS_UNIX
+ const QByteArray newPath = QFile::encodeName(path2);
+ unlink(newPath.constData());
+ return symlink(path1.constData(), newPath.constData()) == 0;
+#else
+ Q_UNUSED(path1);
+ Q_UNUSED(path2);
+ return false;
+#endif // Q_OS_UNIX
+}
+
+/*!
+ Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath.
+ \a tgtFilePath will contain the target directory, which will be created. Example usage:
+
+ \code
+ QString error;
+ book ok = Utils::FileUtils::copyRecursively("/foo/bar", "/foo/baz", &error);
+ if (!ok)
+ qDebug() << error;
+ \endcode
+
+ This will copy the contents of /foo/bar into to the baz directory under /foo,
+ which will be created in the process.
+
+ \return Whether the operation succeeded.
+ \note Function was adapted from qtc/src/libs/fileutils.cpp
+*/
+
+bool copyFileRecursion(const QString &srcFilePath, const QString &tgtFilePath,
+ bool preserveSymLinks, QString *errorMessage)
+{
+ QFileInfo srcFileInfo(srcFilePath);
+ QFileInfo tgtFileInfo(tgtFilePath);
+ const QString targetDirPath = tgtFileInfo.absoluteDir().path();
+ if (!QDir::root().mkpath(targetDirPath)) {
+ *errorMessage = Tr::tr("The directory '%1' could not be created.")
+ .arg(QDir::toNativeSeparators(targetDirPath));
+ return false;
+ }
+ if (HostOsInfo::isAnyUnixHost() && preserveSymLinks && srcFileInfo.isSymLink()) {
+ // For now, disable symlink preserving copying on Windows.
+ // MS did a good job to prevent people from using symlinks - even if they are supported.
+ if (!createSymLink(storedLinkTarget(srcFilePath), tgtFilePath)) {
+ *errorMessage = Tr::tr("The symlink '%1' could not be created.")
+ .arg(tgtFilePath);
+ return false;
+ }
+ } else if (srcFileInfo.isDir()) {
+ QDir sourceDir(srcFilePath);
+ QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot
+ | QDir::Hidden | QDir::System);
+ foreach (const QString &fileName, fileNames) {
+ const QString newSrcFilePath = srcFilePath + QLatin1Char('/') + fileName;
+ const QString newTgtFilePath = tgtFilePath + QLatin1Char('/') + fileName;
+ if (!copyFileRecursion(newSrcFilePath, newTgtFilePath, preserveSymLinks, errorMessage))
+ return false;
+ }
+ } else {
+ if (tgtFileInfo.exists() && srcFileInfo.lastModified() <= tgtFileInfo.lastModified())
+ return true;
+ QFile file(srcFilePath);
+ QFile targetFile(tgtFilePath);
+ if (targetFile.exists()) {
+ targetFile.setPermissions(targetFile.permissions() | QFile::WriteUser);
+ if (!targetFile.remove()) {
+ *errorMessage = Tr::tr("Could not remove file '%1'. %2")
+ .arg(QDir::toNativeSeparators(tgtFilePath), targetFile.errorString());
+ }
+ }
+ if (!file.copy(tgtFilePath)) {
+ *errorMessage = Tr::tr("Could not copy file '%1' to '%2'. %3")
+ .arg(QDir::toNativeSeparators(srcFilePath), QDir::toNativeSeparators(tgtFilePath),
+ file.errorString());
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/fileinfo.h b/src/lib/corelib/tools/fileinfo.h
new file mode 100644
index 000000000..b5731cedd
--- /dev/null
+++ b/src/lib/corelib/tools/fileinfo.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_FILEINFO_H
+#define QBS_FILEINFO_H
+
+#include "filetime.h"
+#include "qbs_export.h"
+
+#if defined(Q_OS_UNIX)
+#include <sys/stat.h>
+#endif
+
+#include <QString>
+
+QT_FORWARD_DECLARE_CLASS(QFileInfo)
+
+namespace qbs {
+namespace Internal {
+
+class FileInfo
+{
+public:
+ FileInfo(const QString &fileName);
+
+ bool exists() const;
+ FileTime lastModified() const;
+ FileTime lastStatusChange() const;
+ bool isDir() const;
+
+ static QString fileName(const QString &fp);
+ static QString baseName(const QString &fp);
+ static QString completeBaseName(const QString &fp);
+ static QString path(const QString &fp);
+ static void splitIntoDirectoryAndFileName(const QString &filePath, QString *dirPath, QString *fileName);
+ static void splitIntoDirectoryAndFileName(const QString &filePath, QStringRef *dirPath, QStringRef *fileName);
+ static bool exists(const QString &fp);
+ static bool isAbsolute(const QString &fp);
+ static bool isPattern(const QStringRef &str);
+ static bool isPattern(const QString &str);
+ static QString resolvePath(const QString &base, const QString &rel);
+ static bool globMatches(const QRegExp &pattern, const QString &subject);
+ static bool isFileCaseCorrect(const QString &filePath);
+
+private:
+#if defined(Q_OS_WIN)
+ struct InternalStatType
+ {
+ quint8 z[36];
+ };
+#elif defined(Q_OS_UNIX)
+ typedef struct stat InternalStatType;
+#else
+# error unknown platform
+#endif
+ InternalStatType m_stat;
+};
+
+bool removeFileRecursion(const QFileInfo &f, QString *errorMessage);
+
+// FIXME: Used by tests.
+bool QBS_EXPORT removeDirectoryWithContents(const QString &path, QString *errorMessage);
+bool QBS_EXPORT copyFileRecursion(const QString &sourcePath, const QString &targetPath,
+ bool preserveSymLinks, QString *errorMessage);
+
+} // namespace Internal
+} // namespace qbs
+
+#endif
diff --git a/src/lib/corelib/tools/filetime.h b/src/lib/corelib/tools/filetime.h
new file mode 100644
index 000000000..0dc0524df
--- /dev/null
+++ b/src/lib/corelib/tools/filetime.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_FILETIME_H
+#define QBS_FILETIME_H
+
+#include <QDataStream>
+#include <QDebug>
+
+#if defined(Q_OS_UNIX)
+#include <time.h>
+#endif
+
+namespace qbs {
+namespace Internal {
+
+class FileTime
+{
+public:
+#if defined(Q_OS_UNIX)
+ typedef time_t InternalType;
+#elif defined(Q_OS_WIN)
+ typedef quint64 InternalType;
+#else
+# error unknown platform
+#endif
+
+ FileTime();
+ FileTime(const InternalType &ft)
+ : m_fileTime(ft)
+ { }
+
+ bool operator < (const FileTime &rhs) const;
+ bool operator > (const FileTime &rhs) const;
+ bool operator <= (const FileTime &rhs) const;
+ bool operator >= (const FileTime &rhs) const;
+ bool operator == (const FileTime &rhs) const;
+ bool operator != (const FileTime &rhs) const;
+
+ void clear();
+ bool isValid() const;
+ QString toString() const;
+
+ static FileTime currentTime();
+
+ friend class FileInfo;
+ InternalType m_fileTime;
+};
+
+inline bool FileTime::operator > (const FileTime &rhs) const
+{
+ return rhs < *this;
+}
+
+inline bool FileTime::operator <= (const FileTime &rhs) const
+{
+ return operator < (rhs) || operator == (rhs);
+}
+
+inline bool FileTime::operator >= (const FileTime &rhs) const
+{
+ return operator > (rhs) || operator == (rhs);
+}
+
+inline bool FileTime::operator == (const FileTime &rhs) const
+{
+ return m_fileTime == rhs.m_fileTime;
+}
+
+inline bool FileTime::operator != (const FileTime &rhs) const
+{
+ return !operator==(rhs);
+}
+
+} // namespace Internal
+} // namespace qbs
+
+QT_BEGIN_NAMESPACE
+
+inline QDataStream& operator>>(QDataStream &stream, qbs::Internal::FileTime &ft)
+{
+ quint64 u;
+ stream >> u;
+ ft.m_fileTime = u;
+ return stream;
+}
+
+inline QDataStream& operator<<(QDataStream &stream, const qbs::Internal::FileTime &ft)
+{
+ stream << (quint64)ft.m_fileTime;
+ return stream;
+}
+
+inline QDebug operator<<(QDebug dbg, const qbs::Internal::FileTime &t)
+{
+ dbg.nospace() << t.toString();
+ return dbg.space();
+}
+
+QT_END_NAMESPACE
+
+#endif // QBS_FILETIME_H
diff --git a/src/lib/corelib/tools/filetime_unix.cpp b/src/lib/corelib/tools/filetime_unix.cpp
new file mode 100644
index 000000000..945be8f44
--- /dev/null
+++ b/src/lib/corelib/tools/filetime_unix.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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 "filetime.h"
+
+#include <QDateTime>
+#include <QString>
+
+#include <time.h>
+
+namespace qbs {
+namespace Internal {
+
+FileTime::FileTime()
+ : m_fileTime(0)
+{
+}
+
+bool FileTime::operator < (const FileTime &rhs) const
+{
+ return m_fileTime < rhs.m_fileTime;
+}
+
+void FileTime::clear()
+{
+ m_fileTime = 0;
+}
+
+bool FileTime::isValid() const
+{
+ return m_fileTime != 0;
+}
+
+FileTime FileTime::currentTime()
+{
+ return time(0);
+}
+
+QString FileTime::toString() const
+{
+ QDateTime dt;
+ dt.setTime_t(m_fileTime);
+ return dt.toString();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/filetime_win.cpp b/src/lib/corelib/tools/filetime_win.cpp
new file mode 100644
index 000000000..b3a7fab67
--- /dev/null
+++ b/src/lib/corelib/tools/filetime_win.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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 "filetime.h"
+
+#include <QString>
+#include <qt_windows.h>
+#ifdef Q_CC_MSVC
+#include <strsafe.h>
+#endif // Q_CC_MSVC
+
+namespace qbs {
+namespace Internal {
+
+template<bool> struct CompileTimeAssert;
+template<> struct CompileTimeAssert<true> {};
+
+FileTime::FileTime()
+ : m_fileTime(0)
+{
+ static CompileTimeAssert<sizeof(FileTime::InternalType) == sizeof(FILETIME)> internal_type_has_wrong_size;
+ Q_UNUSED(internal_type_has_wrong_size);
+}
+
+bool FileTime::operator < (const FileTime &rhs) const
+{
+ const FILETIME *const t1 = reinterpret_cast<const FILETIME *>(&m_fileTime);
+ const FILETIME *const t2 = reinterpret_cast<const FILETIME *>(&rhs.m_fileTime);
+ return CompareFileTime(t1, t2) < 0;
+}
+
+void FileTime::clear()
+{
+ m_fileTime = 0;
+}
+
+bool FileTime::isValid() const
+{
+ return m_fileTime != 0;
+}
+
+FileTime FileTime::currentTime()
+{
+ FileTime result;
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ FILETIME *const ft = reinterpret_cast<FILETIME *>(&result.m_fileTime);
+ SystemTimeToFileTime(&st, ft);
+ return result;
+}
+
+QString FileTime::toString() const
+{
+ const FILETIME *const ft = reinterpret_cast<const FILETIME *>(&m_fileTime);
+ SYSTEMTIME stUTC, stLocal;
+ FileTimeToSystemTime(ft, &stUTC);
+ SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal);
+#ifdef Q_CC_MSVC
+ WCHAR szString[512];
+ HRESULT hr = StringCchPrintf(szString, sizeof(szString) / sizeof(WCHAR),
+ L"%02d.%02d.%d %02d:%02d:%02d:%02d",
+ stLocal.wDay, stLocal.wMonth, stLocal.wYear,
+ stLocal.wHour, stLocal.wMinute, stLocal.wSecond,
+ stLocal.wMilliseconds);
+ return SUCCEEDED(hr) ? QString::fromWCharArray(szString) : QString();
+#else // Q_CC_MSVC
+ const QString result = QString("%1.%2.%3 %4:%5:%6")
+ .arg(stLocal.wDay, 2, 10, QLatin1Char('0')).arg(stLocal.wMonth, 2, 10, QLatin1Char('0')).arg(stLocal.wYear)
+ .arg(stLocal.wHour, 2, 10, QLatin1Char('0')).arg(stLocal.wMinute, 2, 10, QLatin1Char('0')).arg(stLocal.wSecond, 2, 10, QLatin1Char('0'));
+ return result;
+#endif // Q_CC_MSVC
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/hostosinfo.h b/src/lib/corelib/tools/hostosinfo.h
new file mode 100644
index 000000000..0bf05cbfd
--- /dev/null
+++ b/src/lib/corelib/tools/hostosinfo.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_HOSTOSINFO_H
+#define QBS_HOSTOSINFO_H
+
+#include "qbs_export.h"
+
+#include <QtGlobal>
+#include <QMap>
+#include <QString>
+#include <QStringList>
+
+#if defined(Q_OS_WIN)
+#define QTC_HOST_EXE_SUFFIX ".exe"
+#define QTC_HOST_DYNAMICLIB_PREFIX ""
+#define QTC_HOST_DYNAMICLIB_SUFFIX ".dll"
+#define QTC_HOST_OBJECT_SUFFIX ".obj"
+#elif defined(Q_OS_DARWIN)
+#define QTC_HOST_EXE_SUFFIX ""
+#define QTC_HOST_DYNAMICLIB_PREFIX "lib"
+#define QTC_HOST_DYNAMICLIB_SUFFIX ".dylib"
+#define QTC_HOST_OBJECT_SUFFIX ".o"
+#else
+#define QTC_HOST_EXE_SUFFIX ""
+#define QTC_HOST_DYNAMICLIB_PREFIX "lib"
+#define QTC_HOST_DYNAMICLIB_SUFFIX ".so"
+#define QTC_HOST_OBJECT_SUFFIX ".o"
+#endif // Q_OS_WIN
+
+namespace qbs {
+namespace Internal {
+
+class QBS_EXPORT HostOsInfo // Exported for use by command-line tools.
+{
+public:
+ // Add more as needed.
+ enum HostOs { HostOsWindows, HostOsLinux, HostOsOsx, HostOsOtherUnix, HostOsOther };
+
+ static inline HostOs hostOs();
+
+ static bool isWindowsHost() { return hostOs() == HostOsWindows; }
+ static bool isLinuxHost() { return hostOs() == HostOsLinux; }
+ static bool isOsxHost() { return hostOs() == HostOsOsx; }
+ static inline bool isAnyUnixHost();
+ static inline QString canonicalArchitecture(const QString &architecture);
+ static inline QString defaultEndianness(const QString &architecture);
+
+ static QString appendExecutableSuffix(const QString &executable)
+ {
+ QString finalName = executable;
+ if (isWindowsHost())
+ finalName += QLatin1String(QTC_HOST_EXE_SUFFIX);
+ return finalName;
+ }
+
+ static QString dynamicLibraryName(const QString &libraryBaseName)
+ {
+ return QLatin1String(QTC_HOST_DYNAMICLIB_PREFIX) + libraryBaseName
+ + QLatin1String(QTC_HOST_DYNAMICLIB_SUFFIX);
+ }
+
+ static QString objectName(const QString &baseName)
+ {
+ return baseName + QLatin1String(QTC_HOST_OBJECT_SUFFIX);
+ }
+
+ static Qt::CaseSensitivity fileNameCaseSensitivity()
+ {
+ return isWindowsHost() ? Qt::CaseInsensitive: Qt::CaseSensitive;
+ }
+
+ static QChar pathListSeparator()
+ {
+ return isWindowsHost() ? QLatin1Char(';') : QLatin1Char(':');
+ }
+
+ static Qt::KeyboardModifier controlModifier()
+ {
+ return isOsxHost() ? Qt::MetaModifier : Qt::ControlModifier;
+ }
+};
+
+HostOsInfo::HostOs HostOsInfo::hostOs()
+{
+#if defined(Q_OS_WIN)
+ return HostOsWindows;
+#elif defined(Q_OS_LINUX)
+ return HostOsLinux;
+#elif defined(Q_OS_DARWIN)
+ return HostOsOsx;
+#elif defined(Q_OS_UNIX)
+ return HostOsOtherUnix;
+#else
+ return HostOsOther;
+#endif
+}
+
+bool HostOsInfo::isAnyUnixHost()
+{
+#ifdef Q_OS_UNIX
+ return true;
+#else
+ return false;
+#endif
+}
+
+QString HostOsInfo::canonicalArchitecture(const QString &architecture)
+{
+ QMap<QString, QStringList> archMap;
+ archMap.insert(QLatin1String("x86"), QStringList()
+ << QLatin1String("i386")
+ << QLatin1String("i486")
+ << QLatin1String("i586")
+ << QLatin1String("i686")
+ << QLatin1String("ia32")
+ << QLatin1String("ia-32")
+ << QLatin1String("x86_32")
+ << QLatin1String("x86-32")
+ << QLatin1String("intel32"));
+
+ archMap.insert(QLatin1String("x86_64"), QStringList()
+ << QLatin1String("x86-64")
+ << QLatin1String("x64")
+ << QLatin1String("amd64")
+ << QLatin1String("ia32e")
+ << QLatin1String("em64t")
+ << QLatin1String("intel64"));
+
+ archMap.insert(QLatin1String("ia64"), QStringList()
+ << QLatin1String("ia-64")
+ << QLatin1String("itanium"));
+
+ QMapIterator<QString, QStringList> i(archMap);
+ while (i.hasNext()) {
+ i.next();
+ if (i.value().contains(architecture.toLower()))
+ return i.key();
+ }
+
+ return architecture;
+}
+
+QString HostOsInfo::defaultEndianness(const QString &architecture)
+{
+ const QString canonicalArch = canonicalArchitecture(architecture);
+
+ QStringList little = QStringList()
+ << QLatin1String("x86")
+ << QLatin1String("x86_64")
+ << QLatin1String("arm")
+ << QLatin1String("arm64");
+
+ if (little.contains(canonicalArch))
+ return QLatin1String("little");
+
+ QStringList big = QStringList()
+ << QLatin1String("ppc")
+ << QLatin1String("ppc64");
+
+ if (big.contains(canonicalArch))
+ return QLatin1String("big");
+
+ return QString();
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_HOSTOSINFO_H
diff --git a/src/lib/corelib/tools/id.cpp b/src/lib/corelib/tools/id.cpp
new file mode 100644
index 000000000..a9dc07cbc
--- /dev/null
+++ b/src/lib/corelib/tools/id.cpp
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** 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 "id.h"
+#include "qbsassert.h"
+
+#include <QByteArray>
+#include <QHash>
+#include <QVector>
+
+namespace qbs {
+namespace Internal {
+
+/*!
+ \class qbs::Internal::Id
+
+ \brief The class Id encapsulates an identifier that is unique
+ within a specific running process, using the qbs library.
+
+ \c{Id} is used as facility to identify objects of interest
+ in a more typesafe and faster manner than a plain \c QString or
+ \c QByteArray would provide.
+
+ An id is internally represented as a 32 bit integer (its \c UID)
+ and associated with a plain 7-bit-clean ASCII name used
+ for display and persistency.
+
+ This class is copied from Qt Creator.
+*/
+
+class StringHolder
+{
+public:
+ StringHolder()
+ : n(0), str(0)
+ {}
+
+ StringHolder(const char *s, int length)
+ : n(length), str(s)
+ {
+ if (!n)
+ length = n = qstrlen(s);
+ h = 0;
+ while (length--) {
+ h = (h << 4) + *s++;
+ h ^= (h & 0xf0000000) >> 23;
+ h &= 0x0fffffff;
+ }
+ }
+ int n;
+ const char *str;
+ uint h;
+};
+
+static bool operator==(const StringHolder &sh1, const StringHolder &sh2)
+{
+ // sh.n is unlikely to discriminate better than the hash.
+ return sh1.h == sh2.h && sh1.str && sh2.str && strcmp(sh1.str, sh2.str) == 0;
+}
+
+
+static uint qHash(const StringHolder &sh)
+{
+ return sh.h;
+}
+
+struct IdCache : public QHash<StringHolder, int>
+{
+#ifndef QBS_ALLOW_STATIC_LEAKS
+ ~IdCache()
+ {
+ for (IdCache::iterator it = begin(); it != end(); ++it)
+ delete[](const_cast<char *>(it.key().str));
+ }
+#endif
+};
+
+
+static int firstUnusedId = Id::IdsPerPlugin * Id::ReservedPlugins;
+
+static QHash<int, StringHolder> stringFromId;
+static IdCache idFromString;
+
+static int theId(const char *str, int n = 0)
+{
+ QBS_ASSERT(str && *str, return 0);
+ StringHolder sh(str, n);
+ int res = idFromString.value(sh, 0);
+ if (res == 0) {
+ res = firstUnusedId++;
+ sh.str = qstrdup(sh.str);
+ idFromString[sh] = res;
+ stringFromId[res] = sh;
+ }
+ return res;
+}
+
+static int theId(const QByteArray &ba)
+{
+ return theId(ba.constData(), ba.size());
+}
+
+/*!
+ \fn qbs::Internal::Id(int uid)
+
+ \brief Constructs an id given a UID.
+
+ The UID is an integer value that is unique within the running
+ process.
+
+ It is the callers responsibility to ensure the uniqueness of
+ the passed integer. The recommended approach is to use
+ \c{registerId()} with an value taken from the plugin's
+ private range.
+
+ \sa registerId()
+
+*/
+
+/*!
+ Constructs an id given its associated name. The internal
+ representation will be unspecified, but consistent within a
+ process.
+
+*/
+Id::Id(const char *name)
+ : m_id(theId(name, 0))
+{}
+
+/*!
+ \overload
+
+*/
+Id::Id(const QByteArray &name)
+ : m_id(theId(name))
+{}
+
+///*!
+// \overload
+// \deprecated
+//*/
+//Id::Id(const QString &name)
+// : m_id(theId(name.toUtf8()))
+//{}
+
+/*!
+ Returns an internal representation of the id.
+*/
+
+QByteArray Id::name() const
+{
+ return stringFromId.value(m_id).str;
+}
+
+/*!
+ Returns a string representation of the id suitable
+ for UI display.
+
+ This should not be used to create a persistent version
+ of the Id, use \c{toSetting()} instead.
+
+ \sa fromString(), toSetting()
+*/
+
+QString Id::toString() const
+{
+ return QString::fromUtf8(stringFromId.value(m_id).str);
+}
+
+/*!
+ Creates an id from a string representation.
+
+ This should not be used to handle a persistent version
+ of the Id, use \c{fromSetting()} instead.
+
+ \deprecated
+
+ \sa toString(), fromSetting()
+*/
+
+Id Id::fromString(const QString &name)
+{
+ return Id(theId(name.toUtf8()));
+}
+
+/*!
+ Creates an id from a string representation.
+
+ This should not be used to handle a persistent version
+ of the Id, use \c{fromSetting()} instead.
+
+ \deprecated
+
+ \sa toString(), fromSetting()
+*/
+
+Id Id::fromName(const QByteArray &name)
+{
+ return Id(theId(name));
+}
+
+/*!
+ Returns a persistent value representing the id which is
+ suitable to be stored in QSettings.
+
+ \sa fromSetting()
+*/
+
+QVariant Id::toSetting() const
+{
+ return QVariant(QString::fromUtf8(stringFromId.value(m_id).str));
+}
+
+/*!
+ Reconstructs an id from a persistent value.
+
+ \sa toSetting()
+*/
+
+Id Id::fromSetting(const QVariant &variant)
+{
+ const QByteArray ba = variant.toString().toUtf8();
+ if (ba.isEmpty())
+ return Id();
+ return Id(theId(ba));
+}
+
+/*!
+ Constructs a derived id.
+
+ This can be used to construct groups of ids logically
+ belonging together. The associated internal name
+ will be generated by appending \c{suffix}.
+*/
+
+Id Id::withSuffix(int suffix) const
+{
+ const QByteArray ba = name() + QByteArray::number(suffix);
+ return Id(ba.constData());
+}
+
+/*!
+ \overload
+*/
+
+Id Id::withSuffix(const char *suffix) const
+{
+ const QByteArray ba = name() + suffix;
+ return Id(ba.constData());
+}
+
+/*!
+ Constructs a derived id.
+
+ This can be used to construct groups of ids logically
+ belonging together. The associated internal name
+ will be generated by prepending \c{prefix}.
+*/
+
+Id Id::withPrefix(const char *prefix) const
+{
+ const QByteArray ba = prefix + name();
+ return Id(ba.constData());
+}
+
+
+/*!
+ Associates a id with its uid and its string
+ representation.
+
+ The uid should be taken from the plugin's private range.
+
+ \sa fromSetting()
+*/
+
+void Id::registerId(int uid, const char *name)
+{
+ StringHolder sh(name, 0);
+ idFromString[sh] = uid;
+ stringFromId[uid] = sh;
+}
+
+bool Id::operator==(const char *name) const
+{
+ const char *string = stringFromId.value(m_id).str;
+ if (string && name)
+ return strcmp(string, name) == 0;
+ else
+ return false;
+}
+
+bool Id::alphabeticallyBefore(Id other) const
+{
+ return toString().compare(other.toString(), Qt::CaseInsensitive) < 0;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/id.h b/src/lib/corelib/tools/id.h
new file mode 100644
index 000000000..097a2d89b
--- /dev/null
+++ b/src/lib/corelib/tools/id.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_TOOLS_ID_H
+#define QBS_TOOLS_ID_H
+
+#include <QMetaType>
+#include <QString>
+#include <QVariant>
+
+namespace qbs {
+namespace Internal {
+
+class Id
+{
+public:
+ enum { IdsPerPlugin = 10000, ReservedPlugins = 1000 };
+
+ Id() : m_id(0) {}
+ Id(int uid) : m_id(uid) {}
+ Id(const char *name);
+// explicit Id(const QString &name);
+ explicit Id(const QByteArray &name);
+
+ Id withSuffix(int suffix) const;
+ Id withSuffix(const char *name) const;
+ Id withPrefix(const char *name) const;
+
+ QByteArray name() const;
+ QString toString() const; // Avoid.
+ QVariant toSetting() const; // Good to use.
+ bool isValid() const { return m_id; }
+ bool operator==(Id id) const { return m_id == id.m_id; }
+ bool operator==(const char *name) const;
+ bool operator!=(Id id) const { return m_id != id.m_id; }
+ bool operator!=(const char *name) const { return !operator==(name); }
+ bool operator<(Id id) const { return m_id < id.m_id; }
+ bool operator>(Id id) const { return m_id > id.m_id; }
+ bool alphabeticallyBefore(Id other) const;
+ int uniqueIdentifier() const { return m_id; }
+ static Id fromUniqueIdentifier(int uid) { return Id(uid); }
+ static Id fromString(const QString &str); // FIXME: avoid.
+ static Id fromName(const QByteArray &ba); // FIXME: avoid.
+ static Id fromSetting(const QVariant &variant); // Good to use.
+ static void registerId(int uid, const char *name);
+
+private:
+ // Intentionally unimplemented
+ Id(const QLatin1String &);
+ int m_id;
+};
+
+inline uint qHash(const Id &id) { return id.uniqueIdentifier(); }
+
+} // namespace Internal
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::Internal::Id)
+Q_DECLARE_METATYPE(QList<qbs::Internal::Id>)
+
+#endif // QBS_TOOLS_ID_H
diff --git a/src/lib/corelib/tools/installoptions.cpp b/src/lib/corelib/tools/installoptions.cpp
new file mode 100644
index 000000000..66e82fd23
--- /dev/null
+++ b/src/lib/corelib/tools/installoptions.cpp
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** 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 "installoptions.h"
+
+#include <QDir>
+#include <QSharedData>
+
+namespace qbs {
+namespace Internal {
+
+class InstallOptionsPrivate : public QSharedData
+{
+public:
+ InstallOptionsPrivate()
+ : useSysroot(false), removeExisting(false), dryRun(false),
+ keepGoing(false), logElapsedTime(false)
+ {}
+
+ QString installRoot;
+ bool useSysroot;
+ bool removeExisting;
+ bool dryRun;
+ bool keepGoing;
+ bool logElapsedTime;
+};
+
+} // namespace Internal
+
+/*!
+ * \class InstallOptions
+ * \brief The \c InstallOptions class comprises parameters that influence the behavior of
+ * install operations.
+ */
+
+InstallOptions::InstallOptions() : d(new Internal::InstallOptionsPrivate)
+{
+}
+
+InstallOptions::InstallOptions(const InstallOptions &other) : d(other.d)
+{
+}
+
+InstallOptions &InstallOptions::operator=(const InstallOptions &other)
+{
+ d = other.d;
+ return *this;
+}
+
+InstallOptions::~InstallOptions()
+{
+}
+
+/*!
+ * \brief The default install root, relative to the build directory.
+ */
+QString InstallOptions::defaultInstallRoot()
+{
+ return QLatin1String("install-root");
+}
+
+/*!
+ * Returns the base directory for the installation.
+ * The \c qbs.installPrefix path is relative to this root. If the string is empty, either the value of
+ * qbs.sysroot or "<build dir>/install-root" will be used, depending on what \c installIntoSysroot()
+ * returns.
+ * The default is empty.
+ */
+QString InstallOptions::installRoot() const
+{
+ return d->installRoot;
+}
+
+/*!
+ * \brief Sets the base directory for the installation.
+ * \note The argument must either be an empty string or an absolute path to a directory
+ * (which might not yet exist, in which case it will be created).
+ */
+void InstallOptions::setInstallRoot(const QString &installRoot)
+{
+ d->installRoot = installRoot;
+ if (!QDir(installRoot).isRoot()) {
+ while (d->installRoot.endsWith(QLatin1Char('/')))
+ d->installRoot.chop(1);
+ }
+}
+
+/*!
+ * Returns whether to use the sysroot as the default install root.
+ * The default is false.
+ */
+bool InstallOptions::installIntoSysroot() const
+{
+ return d->useSysroot;
+}
+
+void InstallOptions::setInstallIntoSysroot(bool useSysroot)
+{
+ d->useSysroot = useSysroot;
+}
+
+/*!
+ * \brief Returns true iff an existing installation will be removed prior to installing.
+ * The default is false.
+ */
+bool InstallOptions::removeExistingInstallation() const
+{
+ return d->removeExisting;
+}
+
+/*!
+ * Controls whether to remove an existing installation before installing.
+ * \note qbs may do some safety checks and refuse to remove certain directories such as
+ * a user's home directory. You should still be careful with this option, since it
+ * deletes recursively.
+ */
+void InstallOptions::setRemoveExistingInstallation(bool removeExisting)
+{
+ d->removeExisting = removeExisting;
+}
+
+/*!
+ * \brief Returns true iff qbs will not actually copy any files, but just show what would happen.
+ * The default is false.
+ */
+bool InstallOptions::dryRun() const
+{
+ return d->dryRun;
+}
+
+/*!
+ * \brief Controls whether installation will actually take place.
+ * If the argument is true, then qbs will emit information about which files would be copied
+ * instead of actually doing it.
+ */
+void InstallOptions::setDryRun(bool dryRun)
+{
+ d->dryRun = dryRun;
+}
+
+/*!
+ * Returns true iff installation will continue if an error occurs.
+ * The default is false.
+ */
+bool InstallOptions::keepGoing() const
+{
+ return d->keepGoing;
+}
+
+/*!
+ * \brief Controls whether to abort on errors.
+ * If the argument is true, then if a file cannot be copied e.g. due to a permission problem,
+ * a warning will be printed and the installation will continue. If the argument is false,
+ * then the installation will abort immediately in case of an error.
+ */
+void InstallOptions::setKeepGoing(bool keepGoing)
+{
+ d->keepGoing = keepGoing;
+}
+
+/*!
+ * \brief Returns true iff the time the operation takes will be logged.
+ * The default is false.
+ */
+bool InstallOptions::logElapsedTime() const
+{
+ return d->logElapsedTime;
+}
+
+/*!
+ * \brief Controls whether the installation time will be measured and logged.
+ */
+void InstallOptions::setLogElapsedTime(bool logElapsedTime)
+{
+ d->logElapsedTime = logElapsedTime;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/installoptions.h b/src/lib/corelib/tools/installoptions.h
new file mode 100644
index 000000000..2b6194fb0
--- /dev/null
+++ b/src/lib/corelib/tools/installoptions.h
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_INSTALLOPTIONS_H
+#define QBS_INSTALLOPTIONS_H
+
+#include "qbs_export.h"
+
+#include <QSharedDataPointer>
+#include <QString>
+
+namespace qbs {
+namespace Internal { class InstallOptionsPrivate; }
+
+class QBS_EXPORT InstallOptions
+{
+public:
+ InstallOptions();
+ InstallOptions(const InstallOptions &other);
+ InstallOptions &operator=(const InstallOptions &other);
+ ~InstallOptions();
+
+ static QString defaultInstallRoot();
+ QString installRoot() const;
+ void setInstallRoot(const QString &installRoot);
+
+ bool installIntoSysroot() const;
+ void setInstallIntoSysroot(bool useSysroot);
+
+ bool removeExistingInstallation() const;
+ void setRemoveExistingInstallation(bool removeExisting);
+
+ bool dryRun() const;
+ void setDryRun(bool dryRun);
+
+ bool keepGoing() const;
+ void setKeepGoing(bool keepGoing);
+
+ bool logElapsedTime() const;
+ void setLogElapsedTime(bool logElapsedTime);
+
+private:
+ QSharedDataPointer<Internal::InstallOptionsPrivate> d;
+};
+
+} // namespace qbs
+
+#endif // QBS_INSTALLOPTIONS_H
diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp
new file mode 100644
index 000000000..aa519cab2
--- /dev/null
+++ b/src/lib/corelib/tools/persistence.cpp
@@ -0,0 +1,217 @@
+/****************************************************************************
+**
+** 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 "persistence.h"
+
+#include "fileinfo.h"
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+
+#include <QDir>
+#include <QScopedPointer>
+
+namespace qbs {
+namespace Internal {
+
+static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-60";
+
+PersistentPool::PersistentPool(const Logger &logger) : m_logger(logger)
+{
+ m_stream.setVersion(QDataStream::Qt_4_8);
+}
+
+PersistentPool::~PersistentPool()
+{
+ closeStream();
+}
+
+void PersistentPool::load(const QString &filePath)
+{
+ QScopedPointer<QFile> file(new QFile(filePath));
+ if (!file->exists())
+ throw ErrorInfo(Tr::tr("No build graph exists yet for this configuration."));
+ if (!file->open(QFile::ReadOnly)) {
+ throw ErrorInfo(Tr::tr("Could not open open build graph file '%1': %2")
+ .arg(filePath, file->errorString()));
+ }
+
+ m_stream.setDevice(file.data());
+ QByteArray magic;
+ m_stream >> magic;
+ if (magic != QBS_PERSISTENCE_MAGIC) {
+ file->close();
+ file->remove();
+ m_stream.setDevice(0);
+ throw ErrorInfo(Tr::tr("Cannot use stored build graph at '%1': Incompatible file format. "
+ "Expected magic token '%2', got '%3'.")
+ .arg(filePath, QString::fromLatin1(QBS_PERSISTENCE_MAGIC),
+ QString::fromLatin1(magic)));
+ }
+
+ m_stream >> m_headData.projectConfig;
+ file.take();
+ m_loadedRaw.clear();
+ m_loaded.clear();
+ m_storageIndices.clear();
+ m_stringStorage.clear();
+ m_inverseStringStorage.clear();
+}
+
+void PersistentPool::setupWriteStream(const QString &filePath)
+{
+ QString dirPath = FileInfo::path(filePath);
+ if (!FileInfo::exists(dirPath) && !QDir().mkpath(dirPath)) {
+ throw ErrorInfo(Tr::tr("Failure storing build graph: Cannot create directory '%1'.")
+ .arg(dirPath));
+ }
+
+ if (QFile::exists(filePath) && !QFile::remove(filePath)) {
+ throw ErrorInfo(Tr::tr("Failure storing build graph: Cannot remove old file '%1'")
+ .arg(filePath));
+ }
+ QBS_CHECK(!QFile::exists(filePath));
+ QScopedPointer<QFile> file(new QFile(filePath));
+ if (!file->open(QFile::WriteOnly)) {
+ throw ErrorInfo(Tr::tr("Failure storing build graph: "
+ "Cannot open file '%1' for writing: %2").arg(filePath, file->errorString()));
+ }
+
+ m_stream.setDevice(file.take());
+ m_stream << QByteArray(QBS_PERSISTENCE_MAGIC) << m_headData.projectConfig;
+ m_lastStoredObjectId = 0;
+ m_lastStoredStringId = 0;
+}
+
+void PersistentPool::closeStream()
+{
+ delete m_stream.device();
+ m_stream.setDevice(0);
+}
+
+void PersistentPool::store(const PersistentObject *object)
+{
+ if (!object) {
+ m_stream << -1;
+ return;
+ }
+ PersistentObjectId id = m_storageIndices.value(object, -1);
+ if (id < 0) {
+ id = m_lastStoredObjectId++;
+ m_storageIndices.insert(object, id);
+ m_stream << id;
+ object->store(*this);
+ } else {
+ m_stream << id;
+ }
+}
+
+void PersistentPool::clear()
+{
+ m_loaded.clear();
+ m_storageIndices.clear();
+ m_stringStorage.clear();
+ m_inverseStringStorage.clear();
+}
+
+QDataStream &PersistentPool::stream()
+{
+ return m_stream;
+}
+
+void PersistentPool::storeString(const QString &t)
+{
+ int id = m_inverseStringStorage.value(t, -1);
+ if (id < 0) {
+ id = m_lastStoredStringId++;
+ m_inverseStringStorage.insert(t, id);
+ m_stream << id << t;
+ } else {
+ m_stream << id;
+ }
+}
+
+QString PersistentPool::loadString(int id)
+{
+ QBS_CHECK(id >= 0);
+
+ if (id >= m_stringStorage.count()) {
+ QString s;
+ m_stream >> s;
+ m_stringStorage.resize(id + 1);
+ m_stringStorage[id] = s;
+ return s;
+ }
+
+ return m_stringStorage.at(id);
+}
+
+QString PersistentPool::idLoadString()
+{
+ int id;
+ m_stream >> id;
+ return loadString(id);
+}
+
+void PersistentPool::storeStringSet(const QSet<QString> &t)
+{
+ m_stream << t.count();
+ foreach (const QString &s, t)
+ storeString(s);
+}
+
+QSet<QString> PersistentPool::idLoadStringSet()
+{
+ int i;
+ m_stream >> i;
+ QSet<QString> result;
+ for (; --i >= 0;)
+ result += idLoadString();
+ return result;
+}
+
+void PersistentPool::storeStringList(const QStringList &t)
+{
+ m_stream << t.count();
+ foreach (const QString &s, t)
+ storeString(s);
+}
+
+QStringList PersistentPool::idLoadStringList()
+{
+ int i;
+ m_stream >> i;
+ QStringList result;
+ for (; --i >= 0;)
+ result += idLoadString();
+ return result;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/persistence.h b/src/lib/corelib/tools/persistence.h
new file mode 100644
index 000000000..1afea6d80
--- /dev/null
+++ b/src/lib/corelib/tools/persistence.h
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_PERSISTENCE
+#define QBS_PERSISTENCE
+
+#include "persistentobject.h"
+#include <logging/logger.h>
+
+#include <QDataStream>
+#include <QSharedPointer>
+#include <QString>
+#include <QVariantMap>
+#include <QVector>
+
+namespace qbs {
+namespace Internal {
+
+class PersistentPool
+{
+public:
+ PersistentPool(const Logger &logger);
+ ~PersistentPool();
+
+ class HeadData
+ {
+ public:
+ QVariantMap projectConfig;
+ };
+
+ void load(const QString &filePath);
+ void setupWriteStream(const QString &filePath);
+ void closeStream();
+ void clear();
+ QDataStream &stream();
+
+ template <typename T> T *idLoad();
+ template <typename T> void loadContainer(T &container);
+ template <class T> QSharedPointer<T> idLoadS();
+ template <typename T> void loadContainerS(T &container);
+
+ void store(const QSharedPointer<const PersistentObject> &ptr) { store(ptr.data()); }
+ void store(const PersistentObject *object);
+ template <typename T> void storeContainer(T &container);
+
+ void storeString(const QString &t);
+ QString loadString(int id);
+ QString idLoadString();
+
+ void storeStringSet(const QSet<QString> &t);
+ QSet<QString> loadStringSet(const QList<int> &id);
+ QSet<QString> idLoadStringSet();
+
+ void storeStringList(const QStringList &t);
+ QStringList loadStringList(const QList<int> &ids);
+ QStringList idLoadStringList();
+
+ const HeadData &headData() const { return m_headData; }
+ void setHeadData(const HeadData &hd) { m_headData = hd; }
+
+private:
+ typedef int PersistentObjectId;
+
+ template<typename T> struct RemovePointer { typedef T Type; };
+ template<typename T> struct RemovePointer<T*> { typedef T Type; };
+ template <class T> struct RemoveConst { typedef T Type; };
+ template <class T> struct RemoveConst<const T> { typedef T Type; };
+
+ template <class T> T *loadRaw(PersistentObjectId id);
+ template <class T> QSharedPointer<T> load(PersistentObjectId id);
+
+ QDataStream m_stream;
+ HeadData m_headData;
+ QVector<PersistentObject *> m_loadedRaw;
+ QVector<QSharedPointer<PersistentObject> > m_loaded;
+ QHash<const PersistentObject *, int> m_storageIndices;
+ PersistentObjectId m_lastStoredObjectId;
+
+ QVector<QString> m_stringStorage;
+ QHash<QString, int> m_inverseStringStorage;
+ PersistentObjectId m_lastStoredStringId;
+ Logger m_logger;
+};
+
+template <typename T> inline T *PersistentPool::idLoad()
+{
+ PersistentObjectId id;
+ stream() >> id;
+ return loadRaw<T>(id);
+}
+
+template <typename T> inline void PersistentPool::loadContainer(T &container)
+{
+ int count;
+ stream() >> count;
+ container.clear();
+ container.reserve(count);
+ for (int i = count; --i >= 0;)
+ container += idLoad<typename RemovePointer<typename T::value_type>::Type>();
+}
+
+template <class T> inline QSharedPointer<T> PersistentPool::idLoadS()
+{
+ PersistentObjectId id;
+ m_stream >> id;
+ return load<T>(id);
+}
+
+template <typename T> inline void PersistentPool::loadContainerS(T &container)
+{
+ int count;
+ stream() >> count;
+ container.clear();
+ container.reserve(count);
+ for (int i = count; --i >= 0;)
+ container += idLoadS<typename RemoveConst<typename T::value_type::value_type>::Type>();
+}
+
+template <typename T> inline void PersistentPool::storeContainer(T &container)
+{
+ stream() << container.count();
+ typename T::const_iterator it = container.constBegin();
+ const typename T::const_iterator itEnd = container.constEnd();
+ for (; it != itEnd; ++it)
+ store(*it);
+}
+
+template <class T> inline T *PersistentPool::loadRaw(PersistentObjectId id)
+{
+ if (id < 0)
+ return 0;
+
+ if (id < m_loadedRaw.count()) {
+ PersistentObject *obj = m_loadedRaw.value(id);
+ return dynamic_cast<T*>(obj);
+ }
+
+ int i = m_loadedRaw.count();
+ m_loadedRaw.resize(id + 1);
+ for (; i < m_loadedRaw.count(); ++i)
+ m_loadedRaw[i] = 0;
+
+ T * const t = new T;
+ PersistentObject * const po = t;
+ m_loadedRaw[id] = po;
+ po->load(*this);
+ return t;
+}
+
+template <class T> inline QSharedPointer<T> PersistentPool::load(PersistentObjectId id)
+{
+ if (id < 0)
+ return QSharedPointer<T>();
+
+ if (id < m_loaded.count()) {
+ QSharedPointer<PersistentObject> obj = m_loaded.value(id);
+ return obj.dynamicCast<T>();
+ }
+
+ m_loaded.resize(id + 1);
+ const QSharedPointer<T> t = T::create();
+ m_loaded[id] = t;
+ PersistentObject * const po = t.data();
+ po->load(*this);
+ return t;
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif
diff --git a/src/lib/corelib/tools/persistentobject.h b/src/lib/corelib/tools/persistentobject.h
new file mode 100644
index 000000000..0d9b588c0
--- /dev/null
+++ b/src/lib/corelib/tools/persistentobject.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PERSISTENTOBJECT_H
+#define QBS_PERSISTENTOBJECT_H
+
+namespace qbs {
+namespace Internal {
+
+class PersistentPool;
+
+class PersistentObject
+{
+public:
+ virtual ~PersistentObject() {}
+ virtual void load(PersistentPool &) = 0;
+ virtual void store(PersistentPool &) const = 0;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PERSISTENTOBJECT_H
diff --git a/src/lib/corelib/tools/preferences.cpp b/src/lib/corelib/tools/preferences.cpp
new file mode 100644
index 000000000..bdff7c67b
--- /dev/null
+++ b/src/lib/corelib/tools/preferences.cpp
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** 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 "preferences.h"
+
+#include "buildoptions.h"
+#include "hostosinfo.h"
+#include "profile.h"
+#include "settings.h"
+
+namespace qbs {
+
+/*!
+ * \class Preferences
+ * \brief The \c Preferences class gives access to all general qbs preferences.
+ * If a non-empty \c profileName is given, the profile's preferences take precedence over global
+ * ones. Otherwise, the global preferences are used.
+ */
+Preferences::Preferences(Settings *settings, const QString &profileName)
+ : m_settings(settings), m_profile(profileName)
+{
+}
+
+
+/*!
+ * \brief Returns true <=> colored output should be used for printing messages.
+ * This is only relevant for command-line frontends.
+ */
+bool Preferences::useColoredOutput() const
+{
+ return getPreference(QLatin1String("useColoredOutput"), true).toBool();
+}
+
+/*!
+ * \brief Returns the number of parallel jobs to use for building.
+ * Uses a sensible default value if there is no such setting.
+ */
+int Preferences::jobs() const
+{
+ return getPreference(QLatin1String("jobs"), BuildOptions::defaultMaxJobCount()).toInt();
+}
+
+/*!
+ * \brief Returns the shell to use for the "qbs shell" command.
+ * This is only relevant for command-line frontends.
+ */
+QString Preferences::shell() const
+{
+ return getPreference(QLatin1String("shell")).toString();
+}
+
+/*!
+ * \brief Returns the default build directory used by Qbs if none is specified.
+ */
+QString Preferences::defaultBuildDirectory() const
+{
+ return getPreference(QLatin1String("defaultBuildDirectory")).toString();
+}
+
+/*!
+ * \brief Returns the list of paths where qbs looks for module definitions and such.
+ * If there is no such setting, \c qbsRootPath will be used to look up a fallback location.
+ */
+QStringList Preferences::searchPaths(const QString &qbsRootPath) const
+{
+ const QStringList searchPaths = pathList(QLatin1String("qbsSearchPaths"),
+ qbsRootPath + QLatin1String("/share/qbs"));
+
+ // TODO: Remove in 1.2.
+ const QStringList deprecatedSearchPaths = getPreference(QLatin1String("qbsPath")).toString()
+ .split(Internal::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+ if (!deprecatedSearchPaths.isEmpty()) {
+ qDebug("Warning: preferences.qbsPath is deprecated, "
+ "use preferences.qbsSearchPaths instead.");
+ }
+ return deprecatedSearchPaths + searchPaths;
+}
+
+/*!
+ * \brief Returns the list of paths where qbs looks for plugins.
+ * If there is no such setting, \c qbsRootPath will be used to look up a fallback location.
+ */
+QStringList Preferences::pluginPaths(const QString &qbsRootPath) const
+{
+ return pathList(QLatin1String("pluginsPath"), qbsRootPath + QLatin1String("/lib/qbs/plugins"));
+}
+
+QVariant Preferences::getPreference(const QString &key, const QVariant &defaultValue) const
+{
+ const QString fullKey = QLatin1String("preferences.") + key;
+ if (!m_profile.isEmpty()) {
+ const QVariant value = Profile(m_profile, m_settings).value(fullKey);
+ if (value.isValid())
+ return value;
+ }
+
+ return m_settings->value(fullKey, defaultValue);
+}
+
+QStringList Preferences::pathList(const QString &key, const QString &defaultValue) const
+{
+ QStringList paths = getPreference(key).toString().split(
+ Internal::HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+ paths << defaultValue;
+ return paths;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/preferences.h b/src/lib/corelib/tools/preferences.h
new file mode 100644
index 000000000..e5e6c9bb6
--- /dev/null
+++ b/src/lib/corelib/tools/preferences.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PREFERENCES_H
+#define QBS_PREFERENCES_H
+
+#include "qbs_export.h"
+
+#include <QStringList>
+#include <QVariant>
+
+namespace qbs {
+class Settings;
+
+class QBS_EXPORT Preferences
+{
+public:
+ explicit Preferences(Settings *settings, const QString &profileName = QString());
+
+ bool useColoredOutput() const;
+ int jobs() const;
+ QString shell() const;
+ QString defaultBuildDirectory() const;
+ QStringList searchPaths(const QString &qbsRootPath = QString()) const;
+ QStringList pluginPaths(const QString &qbsRootPath = QString()) const;
+
+private:
+ QVariant getPreference(const QString &key, const QVariant &defaultValue = QVariant()) const;
+ QStringList pathList(const QString &key, const QString &defaultValue) const;
+
+ Settings *m_settings;
+ QString m_profile;
+};
+
+} // namespace qbs
+
+
+#endif // Header guard
diff --git a/src/lib/corelib/tools/processresult.cpp b/src/lib/corelib/tools/processresult.cpp
new file mode 100644
index 000000000..885d7ffee
--- /dev/null
+++ b/src/lib/corelib/tools/processresult.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** 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 "processresult.h"
+#include "processresult_p.h"
+
+/*!
+ * \class SetupProjectParameters
+ * \brief The \c ProcessResult class describes a finished qbs process command.
+ */
+
+namespace qbs {
+
+ProcessResult::ProcessResult() : d(new Internal::ProcessResultPrivate)
+{
+}
+
+ProcessResult::ProcessResult(const ProcessResult &other) : d(other.d)
+{
+}
+
+ProcessResult &ProcessResult::operator=(const ProcessResult &other)
+{
+ d = other.d;
+ return *this;
+}
+
+ProcessResult::~ProcessResult()
+{
+}
+
+/*!
+ * \brief Returns true iff the command finished successfully.
+ */
+bool ProcessResult::success() const
+{
+ return d->success;
+}
+
+/*!
+ * \brief Returns the file path of the executable that was run.
+ */
+QString ProcessResult::executableFilePath() const
+{
+ return d->executableFilePath;
+}
+
+/*!
+ * \brief Returns the command-line arguments with which the command was invoked.
+ */
+QStringList ProcessResult::arguments() const
+{
+ return d->arguments;
+}
+
+/*!
+ * \brief Returns the working directory of the invoked command.
+ */
+QString ProcessResult::workingDirectory() const
+{
+ return d->workingDirectory;
+}
+
+/*!
+ * \brief Returns the exit status of the command.
+ */
+QProcess::ExitStatus ProcessResult::exitStatus() const
+{
+ return d->exitStatus;
+}
+
+/*!
+ * \brief Returns the exit code of the command.
+ */
+int ProcessResult::exitCode() const
+{
+ return d->exitCode;
+}
+
+/*!
+ * \brief Returns the data the command wrote to the standard output channel.
+ */
+QStringList ProcessResult::stdOut() const
+{
+ return d->stdOut;
+}
+
+/*!
+ * \brief Returns the data the command wrote to the standard error channel.
+ */
+QStringList ProcessResult::stdErr() const
+{
+ return d->stdErr;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/processresult.h b/src/lib/corelib/tools/processresult.h
new file mode 100644
index 000000000..b8de9ddd2
--- /dev/null
+++ b/src/lib/corelib/tools/processresult.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROCESSRESULT_H
+#define QBS_PROCESSRESULT_H
+
+#include "qbs_export.h"
+
+#include <QExplicitlySharedDataPointer>
+#include <QMetaType>
+#include <QProcess>
+#include <QString>
+#include <QStringList>
+
+namespace qbs {
+namespace Internal {
+class ProcessCommandExecutor;
+class ProcessResultPrivate;
+}
+
+class QBS_EXPORT ProcessResult
+{
+ friend class qbs::Internal::ProcessCommandExecutor;
+public:
+ ProcessResult();
+ ProcessResult(const ProcessResult &other);
+ ProcessResult &operator=(const ProcessResult &other);
+ ~ProcessResult();
+
+ bool success() const;
+ QString executableFilePath() const;
+ QStringList arguments() const;
+ QString workingDirectory() const;
+ QProcess::ExitStatus exitStatus() const;
+ int exitCode() const;
+ QStringList stdOut() const;
+ QStringList stdErr() const;
+
+private:
+ QExplicitlySharedDataPointer<Internal::ProcessResultPrivate> d;
+};
+
+} // namespace qbs
+
+Q_DECLARE_METATYPE(qbs::ProcessResult)
+
+#endif // QBS_PROCESSRESULT_H
diff --git a/src/lib/corelib/tools/processresult_p.h b/src/lib/corelib/tools/processresult_p.h
new file mode 100644
index 000000000..db2475b44
--- /dev/null
+++ b/src/lib/corelib/tools/processresult_p.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROCESSRESULT_P_H
+#define QBS_PROCESSRESULT_P_H
+
+#include <QSharedData>
+#include <QStringList>
+#include <QProcess>
+
+namespace qbs {
+namespace Internal {
+class ProcessResultPrivate : public QSharedData
+{
+public:
+ bool success;
+
+ QString executableFilePath;
+ QStringList arguments;
+ QString workingDirectory;
+
+ QProcess::ExitStatus exitStatus;
+ int exitCode;
+ QStringList stdOut;
+ QStringList stdErr;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/tools/profile.cpp b/src/lib/corelib/tools/profile.cpp
new file mode 100644
index 000000000..cb3186ae4
--- /dev/null
+++ b/src/lib/corelib/tools/profile.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** 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 "profile.h"
+#include "qbsassert.h"
+#include "settings.h"
+
+#include <logging/translator.h>
+#include <tools/error.h>
+
+namespace qbs {
+
+/*!
+ * \class Profile
+ * \brief The \c Profile class gives access to the settings of a given profile.
+ */
+
+ /*!
+ * \enum Profile::KeySelection
+ * This enum type specifies whether to enumerate keys recursively.
+ * \value KeySelectionRecursive Indicates that key enumeration should happen recursively, i.e.
+ * it should go up the base profile chain.
+ * \value KeySelectionNonRecursive Indicates that only keys directly attached to a profile
+ * should be listed.
+ */
+
+/*!
+ * \brief Creates an object giving access to the settings for profile \c name.
+ */
+Profile::Profile(const QString &name, Settings *settings) : m_name(name), m_settings(settings)
+{
+ QBS_ASSERT(name == cleanName(name), return);
+}
+
+bool Profile::exists() const
+{
+ return !m_settings->allKeysWithPrefix(profileKey()).isEmpty();
+}
+
+/*!
+ * \brief Returns the value for property \c key in this profile.
+ */
+QVariant Profile::value(const QString &key, const QVariant &defaultValue) const
+{
+ return possiblyInheritedValue(key, defaultValue, QStringList());
+}
+
+/*!
+ * \brief Gives value \c value to the property \c key in this profile.
+ */
+void Profile::setValue(const QString &key, const QVariant &value)
+{
+ m_settings->setValue(fullyQualifiedKey(key), value);
+
+ if (key == baseProfileKey()) {
+ QBS_ASSERT(value.toString() == cleanName(value.toString()), return);
+ }
+}
+
+/*!
+ * \brief Removes a key and the associated value from this profile.
+ */
+void Profile::remove(const QString &key)
+{
+ m_settings->remove(fullyQualifiedKey(key));
+}
+
+/*!
+ * \brief Returns the name of this profile.
+ */
+QString Profile::name() const
+{
+ return m_name;
+}
+
+/*!
+ * \brief Returns all property keys in this profile.
+ * If and only if selection is Profile::KeySelectionRecursive, this will also list keys defined
+ * in base profiles.
+ */
+QStringList Profile::allKeys(KeySelection selection) const
+{
+ return allKeysInternal(selection, QStringList());
+}
+
+/*!
+ * \brief Returns the name of this profile's base profile.
+ * The returned value is empty if the profile does not have a base profile.
+ */
+QString Profile::baseProfile() const
+{
+ return localValue(baseProfileKey()).toString();
+}
+
+/*!
+ * \brief Sets a new base profile for this profile.
+ */
+void Profile::setBaseProfile(const QString &baseProfile)
+{
+ setValue(baseProfileKey(), baseProfile);
+}
+
+/*!
+ * \brief Removes this profile's base profile setting.
+ */
+void Profile::removeBaseProfile()
+{
+ remove(baseProfileKey());
+}
+
+/*!
+ * \brief Removes this profile from the settings.
+ */
+void Profile::removeProfile()
+{
+ m_settings->remove(profileKey());
+}
+
+/*!
+ * \brief Returns a string suitiable as a profile name.
+ * Removes all dots and replaces them with hyphens.
+ */
+QString Profile::cleanName(const QString &name)
+{
+ QString newName = name;
+ return newName.replace(QLatin1Char('.'), QLatin1Char('-'));
+}
+
+QString Profile::profileKey() const
+{
+ return QLatin1String("profiles.") + m_name;
+}
+
+QString Profile::baseProfileKey()
+{
+ return QLatin1String("baseProfile");
+}
+
+void Profile::checkBaseProfileExistence(const Profile &baseProfile) const
+{
+ if (!baseProfile.exists())
+ throw ErrorInfo(Internal::Tr::tr("Profile \"%1\" has a non-existent base profile \"%2\".").arg(
+ name(), baseProfile.name()));
+}
+
+QVariant Profile::localValue(const QString &key) const
+{
+ return m_settings->value(fullyQualifiedKey(key));
+}
+
+QString Profile::fullyQualifiedKey(const QString &key) const
+{
+ return profileKey() + QLatin1Char('.') + key;
+}
+
+QVariant Profile::possiblyInheritedValue(const QString &key, const QVariant &defaultValue,
+ QStringList profileChain) const
+{
+ extendAndCheckProfileChain(profileChain);
+ const QVariant v = localValue(key);
+ if (v.isValid())
+ return v;
+ const QString baseProfileName = baseProfile();
+ if (baseProfileName.isEmpty())
+ return defaultValue;
+ Profile parentProfile(baseProfileName, m_settings);
+ checkBaseProfileExistence(parentProfile);
+ return parentProfile.possiblyInheritedValue(key, defaultValue, profileChain);
+}
+
+QStringList Profile::allKeysInternal(Profile::KeySelection selection,
+ QStringList profileChain) const
+{
+ extendAndCheckProfileChain(profileChain);
+ QStringList keys = m_settings->allKeysWithPrefix(profileKey());
+ if (selection == KeySelectionNonRecursive)
+ return keys;
+ const QString baseProfileName = baseProfile();
+ if (baseProfileName.isEmpty())
+ return keys;
+ Profile parentProfile(baseProfileName, m_settings);
+ checkBaseProfileExistence(parentProfile);
+ keys += parentProfile.allKeysInternal(KeySelectionRecursive, profileChain);
+ keys.removeDuplicates();
+ keys.removeOne(baseProfileKey());
+ keys.sort();
+ return keys;
+}
+
+void Profile::extendAndCheckProfileChain(QStringList &chain) const
+{
+ chain << m_name;
+ if (Q_UNLIKELY(chain.count(m_name) > 1)) {
+ throw ErrorInfo(Internal::Tr::tr("Circular profile inheritance. Cycle is '%1'.")
+ .arg(chain.join(QLatin1String(" -> "))));
+ }
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/profile.h b/src/lib/corelib/tools/profile.h
new file mode 100644
index 000000000..c3b0bbfe8
--- /dev/null
+++ b/src/lib/corelib/tools/profile.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROFILE_H
+#define QBS_PROFILE_H
+
+#include "qbs_export.h"
+
+#include <QString>
+#include <QStringList>
+#include <QVariant>
+
+namespace qbs {
+class Settings;
+
+class QBS_EXPORT Profile
+{
+public:
+ explicit Profile(const QString &name, Settings *settings);
+
+ bool exists() const;
+ QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+ void setValue(const QString &key, const QVariant &value);
+ void remove(const QString &key);
+
+ QString name() const;
+
+ QString baseProfile() const;
+ void setBaseProfile(const QString &baseProfile);
+ void removeBaseProfile();
+
+ void removeProfile();
+
+ enum KeySelection { KeySelectionRecursive, KeySelectionNonRecursive };
+ QStringList allKeys(KeySelection selection) const;
+
+ static QString cleanName(const QString &name);
+
+private:
+ static QString baseProfileKey();
+ void checkBaseProfileExistence(const Profile &baseProfile) const;
+ QString profileKey() const;
+ QVariant localValue(const QString &key) const;
+ QString fullyQualifiedKey(const QString &key) const;
+ QVariant possiblyInheritedValue(const QString &key, const QVariant &defaultValue,
+ QStringList profileChain) const;
+ QStringList allKeysInternal(KeySelection selection, QStringList profileChain) const;
+ void extendAndCheckProfileChain(QStringList &chain) const;
+
+ QString m_name;
+ Settings *m_settings;
+};
+
+} // namespace qbs
+
+#endif // Header guard
diff --git a/src/lib/corelib/tools/progressobserver.cpp b/src/lib/corelib/tools/progressobserver.cpp
new file mode 100644
index 000000000..f18aefcfb
--- /dev/null
+++ b/src/lib/corelib/tools/progressobserver.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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 "progressobserver.h"
+
+namespace qbs {
+namespace Internal {
+
+/*!
+ * \class ProgressObserver
+ * The \c ProgressObserver class is used in long running qbs operations. It serves two purposes:
+ * Firstly, it allows operations to indicate progress to a client. Secondly, a client can
+ * signal to an operation that is should exit prematurely.
+ * Clients of the qbs library are supposed to subclass this class and implement the virtual
+ * functions in a way that lets users know about the current operation and its progress.
+ */
+
+/*!
+ * \fn virtual void initialize(const QString &task, int maximum) = 0
+ * \brief Indicates that a new operation is starting.
+ * Library code calls this function to indicate that it is starting a new task.
+ * The \a task parameter is a textual description of that task suitable for presentation to a user.
+ * The \a maximum parameter is an estimate of the maximum effort the operation is going to take.
+ * This is helpful if the client wants to set up some sort of progress bar showing the
+ * percentage of the work already done.
+ */
+
+/*!
+ * \fn virtual void setProgressValue(int value) = 0
+ * \brief Sets the new progress value.
+ * Library code calls this function to indicate that the current operation has progressed.
+ * It will try hard to ensure that \a value will not exceed \c maximum().
+ * \sa ProgressObserver::maximum().
+ */
+
+/*!
+ * \fn virtual int progressValue() = 0
+ * \brief The current progress value.
+ * Will typically reflect the \a value from the last call to \c setProgressValue() and should not
+ * exceed \c maximum().
+ * \sa setProgressvalue()
+ * \sa maximum()
+ */
+
+void ProgressObserver::incrementProgressValue(int increment)
+{
+ setProgressValue(progressValue() + increment);
+}
+
+/*!
+ * \fn virtual bool canceled() const = 0
+ * \brief Indicates whether the current operation should be canceled.
+ * Library code will periodically call this function and abort the current operation
+ * if it returns true.
+ */
+
+/*!
+ * \fn virtual int maximum() const = 0
+ * \brief The expected maximum progress value.
+ * This will typically be the value of \c maximum passed to \c initialize().
+ * \sa ProgressObserver::initialize()
+ */
+
+void ProgressObserver::setFinished()
+{
+ setProgressValue(maximum());
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/progressobserver.h b/src/lib/corelib/tools/progressobserver.h
new file mode 100644
index 000000000..66d552a02
--- /dev/null
+++ b/src/lib/corelib/tools/progressobserver.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROGRESSOBSERVER_H
+#define QBS_PROGRESSOBSERVER_H
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+class ProgressObserver
+{
+public:
+ virtual ~ProgressObserver() { }
+
+ virtual void initialize(const QString &task, int maximum) = 0;
+ virtual void setProgressValue(int value) = 0;
+ virtual int progressValue() = 0;
+ virtual bool canceled() const = 0;
+ virtual void setMaximum(int maximum) = 0;
+ virtual int maximum() const = 0;
+
+ void incrementProgressValue(int increment = 1);
+
+ // Call this to ensure that the progress bar always goes to 100%.
+ void setFinished();
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_PROGRESSOBSERVER_H
diff --git a/src/lib/corelib/tools/propertyfinder.cpp b/src/lib/corelib/tools/propertyfinder.cpp
new file mode 100644
index 000000000..96c081f01
--- /dev/null
+++ b/src/lib/corelib/tools/propertyfinder.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** 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 "propertyfinder.h"
+
+#include "qbsassert.h"
+
+namespace qbs {
+namespace Internal {
+
+QVariantList PropertyFinder::propertyValues(const QVariantMap &properties,
+ const QString &moduleName, const QString &key, MergeType mergeType)
+{
+ m_moduleName = moduleName;
+ m_key = key;
+ m_values.clear();
+ findModuleValues(properties, true);
+ if (mergeType == DoMergeLists)
+ mergeLists(&m_values);
+ return m_values;
+}
+
+QVariant PropertyFinder::propertyValue(const QVariantMap &properties, const QString &moduleName,
+ const QString &key)
+{
+ m_moduleName = moduleName;
+ m_key = key;
+ m_values.clear();
+ findModuleValues(properties, false);
+
+ QBS_ASSERT(m_values.count() <= 1, return QVariant());
+ return m_values.isEmpty() ? QVariant() : m_values.first();
+}
+
+void PropertyFinder::findModuleValues(const QVariantMap &properties, bool searchRecursively)
+{
+ QVariantMap moduleProperties = properties.value(QLatin1String("modules")).toMap();
+
+ // Direct hits come first.
+ const QVariantMap::Iterator modIt = moduleProperties.find(m_moduleName);
+ if (modIt != moduleProperties.end()) {
+ const QVariantMap moduleMap = modIt->toMap();
+ const QVariant property = moduleMap.value(m_key);
+ addToList(property);
+ moduleProperties.erase(modIt);
+ }
+
+ if (!searchRecursively)
+ return;
+
+ // These are the non-matching modules.
+ for (QVariantMap::ConstIterator it = moduleProperties.constBegin();
+ it != moduleProperties.constEnd(); ++it) {
+ findModuleValues(it->toMap(), true);
+ }
+}
+
+void PropertyFinder::addToList(const QVariant &value)
+{
+ // Note: This means that manually setting a property to "null" will not lead to a "hit".
+ if (!value.isNull() && !m_values.contains(value))
+ m_values << value;
+}
+
+void PropertyFinder::mergeLists(QVariantList *values)
+{
+ QVariantList::iterator it = values->begin();
+ while (it != values->end()) {
+ if (it->canConvert<QVariantList>()) {
+ QVariantList sublist = it->toList();
+ mergeLists(&sublist);
+ it = values->erase(it);
+ for (int k = sublist.count(); --k >= 0;)
+ it = values->insert(it, sublist.at(k));
+ } else {
+ ++it;
+ }
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/propertyfinder.h b/src/lib/corelib/tools/propertyfinder.h
new file mode 100644
index 000000000..0db0ba39f
--- /dev/null
+++ b/src/lib/corelib/tools/propertyfinder.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_PROPERTY_FINDER_H
+#define QBS_PROPERTY_FINDER_H
+
+#include <QVariantList>
+#include <QVariantMap>
+
+namespace qbs {
+namespace Internal {
+
+class PropertyFinder
+{
+public:
+ enum MergeType { DoMergeLists, DoNotMergeLists };
+ QVariantList propertyValues(const QVariantMap &properties, const QString &moduleName,
+ const QString &key, MergeType mergeType = DoMergeLists);
+
+ // Note that this can still be a list if the property type itself is one.
+ QVariant propertyValue(const QVariantMap &properties, const QString &moduleName,
+ const QString &key);
+
+private:
+ void findModuleValues(const QVariantMap &properties, bool searchRecursively);
+ void addToList(const QVariant &value);
+ static void mergeLists(QVariantList *values);
+
+ QString m_moduleName;
+ QString m_key;
+ QVariantList m_values;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/tools/qbs_export.h b/src/lib/corelib/tools/qbs_export.h
new file mode 100644
index 000000000..da6779088
--- /dev/null
+++ b/src/lib/corelib/tools/qbs_export.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_EXPORT_H
+#define QBS_EXPORT_H
+
+#include <qglobal.h>
+
+#ifdef QBS_STATIC_LIB
+# define QBS_EXPORT
+#else
+# ifdef QBS_LIBRARY
+# define QBS_EXPORT Q_DECL_EXPORT
+# else
+# define QBS_EXPORT Q_DECL_IMPORT
+# endif
+#endif
+
+#endif // Include guard.
diff --git a/src/lib/corelib/tools/qbsassert.cpp b/src/lib/corelib/tools/qbsassert.cpp
new file mode 100644
index 000000000..46ea58b83
--- /dev/null
+++ b/src/lib/corelib/tools/qbsassert.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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 "qbsassert.h"
+#include "error.h"
+
+#include <QString>
+
+namespace qbs {
+namespace Internal {
+
+void writeAssertLocation(const char *condition, const char *file, int line)
+{
+ qDebug("SOFT ASSERT: %s in %s:%d", condition, file, line);
+}
+
+void throwAssertLocation(const char *condition, const char *file, int line)
+{
+ throw ErrorInfo(QString(QLatin1String("ASSERT: %1")).arg(condition),
+ CodeLocation(QString::fromLocal8Bit(file), line), true);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/qbsassert.h b/src/lib/corelib/tools/qbsassert.h
new file mode 100644
index 000000000..25c00ae1a
--- /dev/null
+++ b/src/lib/corelib/tools/qbsassert.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_QBSASSERT_H
+#define QBS_QBSASSERT_H
+
+#include "qbs_export.h"
+
+namespace qbs {
+namespace Internal {
+
+void QBS_EXPORT writeAssertLocation(const char *condition, const char *file, int line);
+void QBS_EXPORT throwAssertLocation(const char *condition, const char *file, int line);
+
+} // namespace Internal
+} // namespace qbs
+
+#define QBS_ASSERT(cond, action)\
+ if (Q_LIKELY(cond)) {} else {\
+ ::qbs::Internal::writeAssertLocation(#cond, __FILE__, __LINE__); action;\
+ } do {} while (0)
+
+// The do {} while (0) is here to enforce the use of a semicolon after QBS_ASSERT.
+// action can also be continue or break. Copied from qtcassert.h in Qt Creator.
+
+#define QBS_CHECK(cond)\
+ do {\
+ if (Q_LIKELY(cond)) {} else {\
+ ::qbs::Internal::throwAssertLocation(#cond, __FILE__, __LINE__);\
+ }\
+ } while (0)
+
+#endif // QBS_QBSASSERT_H
diff --git a/src/lib/corelib/tools/qttools.cpp b/src/lib/corelib/tools/qttools.cpp
new file mode 100644
index 000000000..21fef9b8f
--- /dev/null
+++ b/src/lib/corelib/tools/qttools.cpp
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** 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 "qttools.h"
+
+QT_BEGIN_NAMESPACE
+uint qHash(const QStringList &list)
+{
+ uint s = 0;
+ foreach (const QString &n, list)
+ s ^= qHash(n) + 0x9e3779b9 + (s << 6) + (s >> 2);
+ return s;
+}
+QT_END_NAMESPACE
diff --git a/src/lib/corelib/tools/qttools.h b/src/lib/corelib/tools/qttools.h
new file mode 100644
index 000000000..bef545c0e
--- /dev/null
+++ b/src/lib/corelib/tools/qttools.h
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBSQTTOOLS_H
+#define QBSQTTOOLS_H
+
+#include <QHash>
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+uint qHash(const QStringList &list);
+QT_END_NAMESPACE
+
+#endif // QBSQTTOOLS_H
diff --git a/src/lib/corelib/tools/scannerpluginmanager.cpp b/src/lib/corelib/tools/scannerpluginmanager.cpp
new file mode 100644
index 000000000..3c5dad702
--- /dev/null
+++ b/src/lib/corelib/tools/scannerpluginmanager.cpp
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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 "scannerpluginmanager.h"
+
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+
+#include <QCoreApplication>
+#include <QDirIterator>
+#include <QLibrary>
+
+namespace qbs {
+namespace Internal {
+
+ScannerPluginManager::~ScannerPluginManager()
+{
+ foreach (QLibrary * const lib, m_libs) {
+ lib->unload();
+ delete lib;
+ }
+}
+
+ScannerPluginManager *ScannerPluginManager::instance()
+{
+ static ScannerPluginManager scannerPlugin;
+ return &scannerPlugin;
+}
+
+ScannerPluginManager::ScannerPluginManager()
+{
+}
+
+QList<ScannerPlugin *> ScannerPluginManager::scannersForFileTag(const FileTag &fileTag)
+{
+ return instance()->m_scannerPlugins.value(fileTag);
+}
+
+void ScannerPluginManager::loadPlugins(const QStringList &pluginPaths, const Logger &logger)
+{
+ QStringList filters;
+
+ if (HostOsInfo::isWindowsHost())
+ filters << "*.dll";
+ else if (HostOsInfo::isOsxHost())
+ filters << "*.dylib";
+ else
+ filters << "*.so";
+
+ foreach (const QString &pluginPath, pluginPaths) {
+ logger.qbsTrace() << QString::fromLocal8Bit("pluginmanager: loading plugins from '%1'.")
+ .arg(QDir::toNativeSeparators(pluginPath));
+ QDirIterator it(pluginPath, filters, QDir::Files);
+ while (it.hasNext()) {
+ const QString fileName = it.next();
+ QScopedPointer<QLibrary> lib(new QLibrary(fileName));
+ if (!lib->load()) {
+ logger.qbsWarning() << Tr::tr("pluginmanager: couldn't load plugin '%1': %2")
+ .arg(QDir::toNativeSeparators(fileName), lib->errorString());
+ continue;
+ }
+
+ getScanners_f getScanners = reinterpret_cast<getScanners_f>(lib->resolve("getScanners"));
+ if (!getScanners) {
+ logger.qbsWarning() << Tr::tr("pluginmanager: couldn't resolve "
+ "symbol in '%1'.").arg(QDir::toNativeSeparators(fileName));
+ continue;
+ }
+
+ ScannerPlugin **plugins = getScanners();
+ if (plugins == 0) {
+ logger.qbsWarning() << Tr::tr("pluginmanager: no scanners "
+ "returned from '%1'.").arg(QDir::toNativeSeparators(fileName));
+ continue;
+ }
+
+ logger.qbsTrace() << QString::fromLocal8Bit("pluginmanager: scanner plugin '%1' "
+ "loaded.").arg(QDir::toNativeSeparators(fileName));
+
+ for (int i = 0; plugins[i] != 0; ++i)
+ m_scannerPlugins[FileTag(plugins[i]->fileTag)] += plugins[i];
+ m_libs.append(lib.take());
+ }
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/scannerpluginmanager.h b/src/lib/corelib/tools/scannerpluginmanager.h
new file mode 100644
index 000000000..ce1ab2d2d
--- /dev/null
+++ b/src/lib/corelib/tools/scannerpluginmanager.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_PLUGINS_H
+#define QBS_PLUGINS_H
+
+#include <language/filetags.h>
+#include <plugins/scanner/scanner.h>
+
+#include <QHash>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+class QLibrary;
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+class Logger;
+
+class ScannerPluginManager
+{
+public:
+ ~ScannerPluginManager();
+ static ScannerPluginManager *instance();
+ static QList<ScannerPlugin *> scannersForFileTag(const FileTag &fileTag);
+ void loadPlugins(const QStringList &paths, const Logger &logger);
+
+private:
+ ScannerPluginManager();
+
+private:
+ QList<QLibrary *> m_libs;
+ QHash<FileTag, QList<ScannerPlugin*> > m_scannerPlugins;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif
diff --git a/src/lib/corelib/tools/scripttools.cpp b/src/lib/corelib/tools/scripttools.cpp
new file mode 100644
index 000000000..c87898da5
--- /dev/null
+++ b/src/lib/corelib/tools/scripttools.cpp
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** 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 "scripttools.h"
+
+#include <QScriptEngine>
+#include <QScriptValueIterator>
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator<< (QDataStream &s, const QScriptProgram &script)
+{
+ s << script.sourceCode()
+ << script.fileName()
+ << script.firstLineNumber();
+ return s;
+}
+
+QDataStream &operator>> (QDataStream &s, QScriptProgram &script)
+{
+ QString fileName, sourceCode;
+ int lineNumber;
+ s >> sourceCode
+ >> fileName
+ >> lineNumber;
+ script = QScriptProgram(sourceCode, fileName, lineNumber);
+ return s;
+}
+
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value)
+{
+ if (name.length() == 1) {
+ cfg.insert(name.first(), value);
+ } else {
+ QVariant &subCfg = cfg[name.first()];
+ QVariantMap subCfgMap = subCfg.toMap();
+ setConfigProperty(subCfgMap, name.mid(1), value);
+ subCfg = subCfgMap;
+ }
+}
+
+QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name)
+{
+ if (name.length() == 1)
+ return cfg.value(name.first());
+ else
+ return getConfigProperty(cfg.value(name.first()).toMap(), name.mid(1));
+}
+
+QString toJSLiteral(const bool b)
+{
+ return b ? "true" : "false";
+}
+
+QString toJSLiteral(const QString &str)
+{
+ QString js = str;
+ js.replace(QRegExp("([\\\\\"])"), "\\\\1");
+ js.prepend('"');
+ js.append('"');
+ return js;
+}
+
+QString toJSLiteral(const QStringList &strs)
+{
+ QString js = "[";
+ for (int i = 0; i < strs.count(); ++i) {
+ if (i != 0)
+ js.append(", ");
+ js.append(toJSLiteral(strs.at(i)));
+ }
+ js.append(']');
+ return js;
+}
+
+QString toJSLiteral(const QVariant &val)
+{
+ if (!val.isValid()) {
+ return "undefined";
+ } else if (val.type() == QVariant::List || val.type() == QVariant::StringList) {
+ QString res;
+ foreach (const QVariant &child, val.toList()) {
+ if (res.length()) res.append(", ");
+ res.append(toJSLiteral(child));
+ }
+ res.prepend("[");
+ res.append("]");
+ return res;
+ } else if (val.type() == QVariant::Bool) {
+ return val.toBool() ? "true" : "false";
+ } else if (val.canConvert(QVariant::String)) {
+ return QLatin1Char('"') + val.toString() + QLatin1Char('"');
+ } else {
+ return QString("Unconvertible type %1").arg(val.typeName());
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/scripttools.h b/src/lib/corelib/tools/scripttools.h
new file mode 100644
index 000000000..4230c898c
--- /dev/null
+++ b/src/lib/corelib/tools/scripttools.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_SCRIPTTOOLS_H
+#define QBS_SCRIPTTOOLS_H
+
+#include <tools/qbs_export.h>
+
+#include <QScriptEngine>
+#include <QScriptProgram>
+#include <QScriptValue>
+#include <QSet>
+#include <QStringList>
+#include <QVariantMap>
+
+QT_BEGIN_NAMESPACE
+
+QDataStream &operator<< (QDataStream &s, const QScriptProgram &script);
+QDataStream &operator>> (QDataStream &s, QScriptProgram &script);
+
+QT_END_NAMESPACE
+
+namespace qbs {
+namespace Internal {
+
+template <typename C>
+QScriptValue toScriptValue(QScriptEngine *scriptEngine, const C &container)
+{
+ QScriptValue v = scriptEngine->newArray(container.count());
+ int i = 0;
+ foreach (const typename C::value_type &item, container)
+ v.setProperty(i++, scriptEngine->toScriptValue(item));
+ return v;
+}
+
+void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value);
+QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name);
+
+QString toJSLiteral(const bool b);
+QString toJSLiteral(const QString &str);
+QString toJSLiteral(const QStringList &strs);
+QString toJSLiteral(const QVariant &val);
+
+/**
+ * @brief push/pop a QScriptEngine's context the RAII way.
+ */
+class ScriptEngineContextPusher
+{
+public:
+ ScriptEngineContextPusher(QScriptEngine *scriptEngine)
+ : m_scriptEngine(scriptEngine)
+ {
+ m_scriptEngine->pushContext();
+ }
+
+ ~ScriptEngineContextPusher()
+ {
+ m_scriptEngine->popContext();
+ }
+
+private:
+ QScriptEngine *m_scriptEngine;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_SCRIPTTOOLS_H
diff --git a/src/lib/corelib/tools/settings.cpp b/src/lib/corelib/tools/settings.cpp
new file mode 100644
index 000000000..95d27ba24
--- /dev/null
+++ b/src/lib/corelib/tools/settings.cpp
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** 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 "settings.h"
+
+#include "error.h"
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+
+#include <QSettings>
+
+#include <algorithm>
+
+namespace qbs {
+using namespace Internal;
+
+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);
+}
+
+Settings::Settings(const QString &organization, const QString &application)
+ : m_settings(new QSettings(format(), QSettings::UserScope, organization, application))
+{
+ if (HostOsInfo::isOsxHost()) {
+ // 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 within a group, because QSettings sees extra system global
+ // settings on OS X we're not interested in.
+ m_settings->beginGroup(QLatin1String("org/qt-project/qbs"));
+ }
+}
+
+Settings::~Settings()
+{
+ delete m_settings;
+}
+
+QVariant Settings::value(const QString &key, const QVariant &defaultValue) const
+{
+ return m_settings->value(internalRepresentation(key), defaultValue);
+}
+
+QStringList Settings::allKeys() const
+{
+ QStringList keys = m_settings->allKeys();
+ fixupKeys(keys);
+ return keys;
+}
+
+QStringList Settings::directChildren(const QString &parentGroup)
+{
+ m_settings->beginGroup(internalRepresentation(parentGroup));
+ QStringList children = m_settings->childGroups();
+ children << m_settings->childKeys();
+ m_settings->endGroup();
+ fixupKeys(children);
+ return children;
+}
+
+QStringList Settings::allKeysWithPrefix(const QString &group) const
+{
+ m_settings->beginGroup(internalRepresentation(group));
+ QStringList keys = m_settings->allKeys();
+ m_settings->endGroup();
+ fixupKeys(keys);
+ return keys;
+}
+
+void Settings::setValue(const QString &key, const QVariant &value)
+{
+ m_settings->setValue(internalRepresentation(key), value);
+ checkStatus();
+}
+
+void Settings::remove(const QString &key)
+{
+ m_settings->remove(internalRepresentation(key));
+ checkStatus();
+}
+
+void Settings::clear()
+{
+ m_settings->clear();
+}
+
+QString Settings::defaultProfile() const
+{
+ return value(QLatin1String("defaultProfile")).toString();
+}
+
+QStringList Settings::profiles() const
+{
+ m_settings->beginGroup(QLatin1String("profiles"));
+ QStringList result = m_settings->childGroups();
+ m_settings->endGroup();
+ return result;
+}
+
+QString Settings::internalRepresentation(const QString &externalKey) const
+{
+ QString internalKey = externalKey;
+ return internalKey.replace(QLatin1Char('.'), QLatin1Char('/'));
+}
+
+QString Settings::externalRepresentation(const QString &internalKey) const
+{
+ QString externalKey = internalKey;
+ return externalKey.replace(QLatin1Char('/'), QLatin1Char('.'));
+}
+
+void Settings::fixupKeys(QStringList &keys) const
+{
+ keys.sort();
+ keys.removeDuplicates();
+ for (QStringList::Iterator it = keys.begin(); it != keys.end(); ++it)
+ *it = externalRepresentation(*it);
+}
+
+void Settings::checkStatus()
+{
+ m_settings->sync();
+ switch (m_settings->status()) {
+ case QSettings::NoError:
+ break;
+ case QSettings::AccessError:
+ throw ErrorInfo(Tr::tr("%1 is not accessible.").arg(m_settings->fileName()));
+ case QSettings::FormatError:
+ throw ErrorInfo(Tr::tr("Format error in %1.").arg(m_settings->fileName()));
+ }
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/settings.h b/src/lib/corelib/tools/settings.h
new file mode 100644
index 000000000..2542df4fb
--- /dev/null
+++ b/src/lib/corelib/tools/settings.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#ifndef QBS_SETTINGS_H
+#define QBS_SETTINGS_H
+
+#include "qbs_export.h"
+
+#include <QStringList>
+#include <QVariant>
+
+QT_BEGIN_NAMESPACE
+class QSettings;
+QT_END_NAMESPACE
+
+namespace qbs {
+
+class QBS_EXPORT Settings
+{
+public:
+ Settings(const QString &organization, const QString &application);
+ ~Settings();
+
+ QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+ QStringList allKeys() const;
+ QStringList directChildren(const QString &parentGroup); // Keys and groups.
+ QStringList allKeysWithPrefix(const QString &group) const;
+ void setValue(const QString &key, const QVariant &value);
+ void remove(const QString &key);
+ void clear();
+
+ QString defaultProfile() const;
+ QStringList profiles() const;
+
+private:
+ QString internalRepresentation(const QString &externalKey) const;
+ QString externalRepresentation(const QString &internalKey) const;
+ void fixupKeys(QStringList &keys) const;
+ void checkStatus();
+
+ QSettings * const m_settings;
+};
+
+} // namespace qbs
+
+#endif // QBS_SETTINGS_H
diff --git a/src/lib/corelib/tools/setupprojectparameters.cpp b/src/lib/corelib/tools/setupprojectparameters.cpp
new file mode 100644
index 000000000..c3b00f3c4
--- /dev/null
+++ b/src/lib/corelib/tools/setupprojectparameters.cpp
@@ -0,0 +1,434 @@
+/****************************************************************************
+**
+** 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 "setupprojectparameters.h"
+
+#include <logging/translator.h>
+#include <tools/profile.h>
+#include <tools/qbsassert.h>
+#include <tools/scripttools.h>
+#include <tools/settings.h>
+
+namespace qbs {
+namespace Internal {
+
+/*!
+ * \class SetupProjectParameters
+ * \brief The \c SetupProjectParameters class comprises data required to set up a qbs project.
+ */
+
+class SetupProjectParametersPrivate : public QSharedData
+{
+public:
+ SetupProjectParametersPrivate()
+ : ignoreDifferentProjectFilePath(false)
+ , dryRun(false)
+ , logElapsedTime(false)
+ , restoreBehavior(SetupProjectParameters::RestoreAndTrackChanges)
+ , environment(QProcessEnvironment::systemEnvironment())
+ {
+ }
+
+ QString projectFilePath;
+ QString buildRoot;
+ QStringList searchPaths;
+ QStringList pluginPaths;
+ QVariantMap overriddenValues;
+ QVariantMap buildConfiguration;
+ mutable QVariantMap overriddenValuesTree;
+ mutable QVariantMap buildConfigurationTree;
+ mutable QVariantMap finalBuildConfigtree;
+ bool ignoreDifferentProjectFilePath;
+ bool dryRun;
+ bool logElapsedTime;
+ SetupProjectParameters::RestoreBehavior restoreBehavior;
+ QProcessEnvironment environment;
+};
+
+} // namespace Internal
+
+SetupProjectParameters::SetupProjectParameters() : d(new Internal::SetupProjectParametersPrivate)
+{
+}
+
+SetupProjectParameters::SetupProjectParameters(const SetupProjectParameters &other) : d(other.d)
+{
+}
+
+SetupProjectParameters::~SetupProjectParameters()
+{
+}
+
+SetupProjectParameters &SetupProjectParameters::operator=(const SetupProjectParameters &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ * \brief Returns the absolute path to the qbs project file.
+ * This file typically has a ".qbs" suffix.
+ */
+QString SetupProjectParameters::projectFilePath() const
+{
+ return d->projectFilePath;
+}
+
+/*!
+ * \brief Sets the path to the main project file.
+ * \note The argument must be an absolute file path.
+ */
+void SetupProjectParameters::setProjectFilePath(const QString &projectFilePath)
+{
+ d->projectFilePath = projectFilePath;
+}
+
+/*!
+ * \brief Returns the base path of where to put the build artifacts and store the build graph.
+ */
+QString SetupProjectParameters::buildRoot() const
+{
+ return d->buildRoot;
+}
+
+/*!
+ * \brief Sets the base path of where to put the build artifacts and store the build graph.
+ * The same base path can be used for several build profiles of the same project without them
+ * interfering with each other.
+ * It might look as if this parameter would not be needed at the time of setting up the project,
+ * but keep in mind that the project information could already exist on disk, in which case
+ * loading it will be much faster than setting up the project from scratch.
+ * \note The argument must be an absolute path to a directory.
+ */
+void SetupProjectParameters::setBuildRoot(const QString &buildRoot)
+{
+ d->buildRoot = buildRoot;
+}
+
+/*!
+ * \brief Where to look for modules and items to import.
+ */
+QStringList SetupProjectParameters::searchPaths() const
+{
+ return d->searchPaths;
+}
+
+/*!
+ * \brief Sets the information about where to look for modules and items to import.
+ * \note The elements of the list must be absolute paths to directories.
+ */
+void SetupProjectParameters::setSearchPaths(const QStringList &searchPaths)
+{
+ d->searchPaths = searchPaths;
+}
+
+/*!
+ * \brief Where to look for plugins.
+ */
+QStringList SetupProjectParameters::pluginPaths() const
+{
+ return d->pluginPaths;
+}
+
+/*!
+ * \brief Sets the information about where to look for plugins.
+ * \note The elements of the list must be absolute paths to directories.
+ */
+void SetupProjectParameters::setPluginPaths(const QStringList &pluginPaths)
+{
+ d->pluginPaths = pluginPaths;
+}
+
+/*!
+ * Returns the overridden values of the build configuration.
+ */
+QVariantMap SetupProjectParameters::overriddenValues() const
+{
+ return d->overriddenValues;
+}
+
+/*!
+ * Set the overridden values of the build configuration.
+ */
+void SetupProjectParameters::setOverriddenValues(const QVariantMap &values)
+{
+ // warn if somebody tries to set a build configuration tree:
+ for (QVariantMap::const_iterator i = values.constBegin();
+ i != values.constEnd(); ++i) {
+ QBS_ASSERT(i.value().type() != QVariant::Map, return);
+ }
+ d->overriddenValues = values;
+ d->overriddenValuesTree.clear();
+ d->finalBuildConfigtree.clear();
+}
+
+static void provideValuesTree(const QVariantMap &values, QVariantMap *valueTree)
+{
+ if (!valueTree->isEmpty() || values.isEmpty())
+ return;
+
+ valueTree->clear();
+ for (QVariantMap::const_iterator it = values.constBegin(); it != values.constEnd(); ++it) {
+ QStringList nameElements = it.key().split(QLatin1Char('.'));
+ if (nameElements.count() > 2) { // ### workaround for submodules being represented internally as a single module of name "module/submodule" rather than two nested modules "module" and "submodule"
+ const QString last = nameElements.takeLast();
+ nameElements = QStringList(nameElements.join(QLatin1String("/")));
+ nameElements.append(last);
+ }
+ Internal::setConfigProperty(*valueTree, nameElements, it.value());
+ }
+}
+
+QVariantMap SetupProjectParameters::overriddenValuesTree() const
+{
+ provideValuesTree(d->overriddenValues, &d->overriddenValuesTree);
+ return d->overriddenValuesTree;
+}
+
+/*!
+ * \brief The collection of properties to use for resolving the project.
+ */
+QVariantMap SetupProjectParameters::buildConfiguration() const
+{
+ return d->buildConfiguration;
+}
+
+/*!
+ * Sets the collection of properties to use for resolving the project.
+ *
+ * Keys are expected to be in dotted syntax (e.g. Qt.declarative.qmlDebugging) that is
+ * used by "qbs config".
+ */
+void SetupProjectParameters::setBuildConfiguration(const QVariantMap &buildConfiguration)
+{
+ // warn if somebody tries to set a build configuration tree:
+ for (QVariantMap::const_iterator i = buildConfiguration.constBegin();
+ i != buildConfiguration.constEnd(); ++i) {
+ QBS_ASSERT(i.value().type() != QVariant::Map, return);
+ }
+ d->buildConfiguration = buildConfiguration;
+ d->buildConfigurationTree.clear();
+ d->finalBuildConfigtree.clear();
+}
+
+/*!
+ * \brief Returns the build configuration in tree form.
+ * \return the tree form of the build configuration.
+ */
+QVariantMap SetupProjectParameters::buildConfigurationTree() const
+{
+ provideValuesTree(d->buildConfiguration, &d->buildConfigurationTree);
+ return d->buildConfigurationTree;
+}
+
+/*!
+ * \brief Expands the build configuration based on the given settings.
+ *
+ * Expansion is the process by which the build configuration is completed based on the given
+ * settings. E.g. the information configured in a profile is filled into the build
+ * configuration by this step.
+ *
+ * This method returns an Error. The list of entries in this error will be empty is the
+ * expansion was successful.
+ */
+ErrorInfo SetupProjectParameters::expandBuildConfiguration(Settings *settings)
+{
+ ErrorInfo err;
+
+ // Generates a full build configuration from user input, using the settings.
+ QVariantMap expandedConfig = d->buildConfiguration;
+
+ const QString buildVariant = expandedConfig.value(QLatin1String("qbs.buildVariant")).toString();
+ if (buildVariant.isEmpty())
+ return ErrorInfo(Internal::Tr::tr("No build variant set."));
+ if (buildVariant != QLatin1String("debug") && buildVariant != QLatin1String("release")) {
+ err.append(Internal::Tr::tr("Invalid build variant '%1'. Must be 'debug' or 'release'.")
+ .arg(buildVariant));
+ return err;
+ }
+
+ // Fill in buildCfg in this order (making sure not to overwrite a key already set by a previous stage)
+ // 1) Things specified on command line (already in buildCfg at this point)
+ // 2) Everything from the profile key
+ QString profileName = expandedConfig.value("qbs.profile").toString();
+ if (profileName.isNull()) {
+ profileName = settings->defaultProfile();
+ if (profileName.isNull()) {
+ const QString profileNames = settings->profiles().join(QLatin1String(", "));
+ err.append(Internal::Tr::tr("No profile given and no default profile set.\n"
+ "Either set the configuration value 'defaultProfile' to a "
+ "valid profile name\n"
+ "or specify the profile with the command line parameter "
+ "'profile:name'.\n"
+ "The following profiles are available:\n%1")
+ .arg(profileNames));
+ return err;
+ }
+ expandedConfig.insert("qbs.profile", profileName);
+ }
+
+ // (2)
+ const Profile profile(profileName, settings);
+ const QStringList profileKeys = profile.allKeys(Profile::KeySelectionRecursive);
+ if (profileKeys.isEmpty()) {
+ err.append(Internal::Tr::tr("Unknown or empty profile '%1'.").arg(profileName));
+ return err;
+ }
+ foreach (const QString &profileKey, profileKeys) {
+ if (!expandedConfig.contains(profileKey))
+ expandedConfig.insert(profileKey, profile.value(profileKey));
+ }
+
+ if (d->buildConfiguration != expandedConfig) {
+ d->buildConfigurationTree.clear();
+ d->buildConfiguration = expandedConfig;
+ }
+ return err;
+}
+
+/*!
+ * \brief Returns the build configuration in tree form, with overridden values taken into account.
+ */
+QVariantMap SetupProjectParameters::finalBuildConfigurationTree() const
+{
+ if (d->finalBuildConfigtree.isEmpty()) {
+ QVariantMap finalMap = d->buildConfiguration;
+ for (QVariantMap::ConstIterator it = d->overriddenValues.constBegin();
+ it != d->overriddenValues.constEnd(); ++it) {
+ finalMap.insert(it.key(), it.value());
+ }
+ provideValuesTree(finalMap, &d->finalBuildConfigtree);
+ }
+ return d->finalBuildConfigtree;
+}
+
+/*!
+ * \variable SetupProjectParameters::ignoreDifferentProjectFilePath
+ * \brief Returns true iff the saved build graph should be used even if its path to the
+ * project file is different from \c SetupProjectParameters::projectFilePath()
+ */
+bool SetupProjectParameters::ignoreDifferentProjectFilePath() const
+{
+ return d->ignoreDifferentProjectFilePath;
+}
+
+/*!
+ * \brief Controls whether the path to the main project file may be different from the one
+ * stored in a possible build graph file.
+ * The default is false.
+ */
+void SetupProjectParameters::setIgnoreDifferentProjectFilePath(bool doIgnore)
+{
+ d->ignoreDifferentProjectFilePath = doIgnore;
+}
+
+ /*!
+ * \brief if true, qbs will not store the build graph of the resolved project.
+ */
+bool SetupProjectParameters::dryRun() const
+{
+ return d->dryRun;
+}
+
+ /*!
+ * \brief Controls whether the build graph will be stored.
+ * If the argument is true, qbs will not store the build graph after resolving the project.
+ * The default is false.
+ */
+void SetupProjectParameters::setDryRun(bool dryRun)
+{
+ d->dryRun = dryRun;
+}
+
+ /*!
+ * \brief Returns true iff the time the operation takes should be logged
+ */
+bool SetupProjectParameters::logElapsedTime() const
+{
+ return d->logElapsedTime;
+}
+
+/*!
+ * Controls whether to log the time taken up for resolving the project.
+ * The default is false.
+ */
+void SetupProjectParameters::setLogElapsedTime(bool logElapsedTime)
+{
+ d->logElapsedTime = logElapsedTime;
+}
+
+/*!
+ * \brief Gets the environment used while resolving the project.
+ */
+QProcessEnvironment SetupProjectParameters::environment() const
+{
+ return d->environment;
+}
+
+/*!
+ * \brief Sets the environment used while resolving the project.
+ */
+void SetupProjectParameters::setEnvironment(const QProcessEnvironment &env)
+{
+ d->environment = env;
+}
+
+
+/*!
+ * \enum SetupProjectParamaters::RestoreBehavior
+ * This enum type specifies how to deal with existing on-disk build information.
+ * \value RestoreOnly Indicates that a stored build graph is to be loaded and the information
+ * therein assumed to be up to date. It is then considered an error if no
+ * such build graph exists.
+ * \value ResolveOnly Indicates that no attempt should be made to restore an existing build graph.
+ * Instead, the project is to be resolved from scratch.
+ * \value RestoreAndTrackChanges Indicates that the build graph should be restored from disk
+ * if possible and otherwise set up from scratch. In the first case,
+ * (parts of) the project might still be re-resolved if certain
+ * parameters have changed (e.g. environment variables used in the
+ * project files).
+ */
+
+
+/*!
+ * Returns information about how restored build data will be handled.
+ */
+SetupProjectParameters::RestoreBehavior SetupProjectParameters::restoreBehavior() const
+{
+ return d->restoreBehavior;
+}
+
+/*!
+ * Controls how restored build data will be handled.
+ */
+void SetupProjectParameters::setRestoreBehavior(SetupProjectParameters::RestoreBehavior behavior)
+{
+ d->restoreBehavior = behavior;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/setupprojectparameters.h b/src/lib/corelib/tools/setupprojectparameters.h
new file mode 100644
index 000000000..d6d8bf88c
--- /dev/null
+++ b/src/lib/corelib/tools/setupprojectparameters.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_SETUPPROJECTPARAMETERS_H
+#define QBS_SETUPPROJECTPARAMETERS_H
+
+#include "qbs_export.h"
+
+#include <tools/error.h>
+
+#include <QProcessEnvironment>
+#include <QSharedDataPointer>
+#include <QStringList>
+#include <QVariantMap>
+
+namespace qbs {
+
+class Settings;
+
+namespace Internal { class SetupProjectParametersPrivate; }
+
+class QBS_EXPORT SetupProjectParameters
+{
+public:
+ SetupProjectParameters();
+ SetupProjectParameters(const SetupProjectParameters &other);
+ ~SetupProjectParameters();
+
+ SetupProjectParameters &operator=(const SetupProjectParameters &other);
+
+ QString projectFilePath() const;
+ void setProjectFilePath(const QString &projectFilePath);
+
+ QString buildRoot() const;
+ void setBuildRoot(const QString &buildRoot);
+
+ QStringList searchPaths() const;
+ void setSearchPaths(const QStringList &searchPaths);
+
+ QStringList pluginPaths() const;
+ void setPluginPaths(const QStringList &pluginPaths);
+
+ QVariantMap overriddenValues() const;
+ void setOverriddenValues(const QVariantMap &values);
+ QVariantMap overriddenValuesTree() const;
+
+ QVariantMap buildConfiguration() const;
+ void setBuildConfiguration(const QVariantMap &buildConfiguration);
+ QVariantMap buildConfigurationTree() const;
+ ErrorInfo expandBuildConfiguration(Settings *settings);
+
+ QVariantMap finalBuildConfigurationTree() const;
+
+ bool ignoreDifferentProjectFilePath() const;
+ void setIgnoreDifferentProjectFilePath(bool doIgnore);
+
+ bool dryRun() const;
+ void setDryRun(bool dryRun);
+
+ bool logElapsedTime() const;
+ void setLogElapsedTime(bool logElapsedTime);
+
+ QProcessEnvironment environment() const;
+ void setEnvironment(const QProcessEnvironment &env);
+
+ enum RestoreBehavior { RestoreOnly, ResolveOnly, RestoreAndTrackChanges };
+ RestoreBehavior restoreBehavior() const;
+ void setRestoreBehavior(RestoreBehavior behavior);
+
+private:
+ QSharedDataPointer<Internal::SetupProjectParametersPrivate> d;
+};
+
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri
new file mode 100644
index 000000000..fca3d8f4b
--- /dev/null
+++ b/src/lib/corelib/tools/tools.pri
@@ -0,0 +1,79 @@
+INCLUDEPATH += $$PWD/../.. # for plugins
+
+HEADERS += \
+ $$PWD/codelocation.h \
+ $$PWD/error.h \
+ $$PWD/fileinfo.h \
+ $$PWD/filetime.h \
+ $$PWD/id.h \
+ $$PWD/persistence.h \
+ $$PWD/scannerpluginmanager.h \
+ $$PWD/scripttools.h \
+ $$PWD/settings.h \
+ $$PWD/preferences.h \
+ $$PWD/profile.h \
+ $$PWD/processresult.h \
+ $$PWD/processresult_p.h \
+ $$PWD/progressobserver.h \
+ $$PWD/propertyfinder.h \
+ $$PWD/hostosinfo.h \
+ $$PWD/buildoptions.h \
+ $$PWD/installoptions.h \
+ $$PWD/cleanoptions.h \
+ $$PWD/setupprojectparameters.h \
+ $$PWD/persistentobject.h \
+ $$PWD/weakpointer.h \
+ $$PWD/qbs_export.h \
+ $$PWD/qbsassert.h \
+ $$PWD/qttools.h
+
+SOURCES += \
+ $$PWD/codelocation.cpp \
+ $$PWD/error.cpp \
+ $$PWD/fileinfo.cpp \
+ $$PWD/id.cpp \
+ $$PWD/persistence.cpp \
+ $$PWD/scannerpluginmanager.cpp \
+ $$PWD/scripttools.cpp \
+ $$PWD/settings.cpp \
+ $$PWD/preferences.cpp \
+ $$PWD/processresult.cpp \
+ $$PWD/profile.cpp \
+ $$PWD/progressobserver.cpp \
+ $$PWD/propertyfinder.cpp \
+ $$PWD/buildoptions.cpp \
+ $$PWD/installoptions.cpp \
+ $$PWD/cleanoptions.cpp \
+ $$PWD/setupprojectparameters.cpp \
+ $$PWD/qbsassert.cpp \
+ $$PWD/qttools.cpp
+
+win32 {
+ SOURCES += $$PWD/filetime_win.cpp
+}
+
+unix {
+ SOURCES += $$PWD/filetime_unix.cpp
+}
+
+all_tests {
+ HEADERS += $$PWD/tst_tools.h
+ SOURCES += $$PWD/tst_tools.cpp
+}
+
+!qbs_no_dev_install {
+ tools_headers.files = \
+ $$PWD/cleanoptions.h \
+ $$PWD/codelocation.h \
+ $$PWD/error.h \
+ $$PWD/settings.h \
+ $$PWD/preferences.h \
+ $$PWD/profile.h \
+ $$PWD/processresult.h \
+ $$PWD/qbs_export.h \
+ $$PWD/buildoptions.h \
+ $$PWD/installoptions.h \
+ $$PWD/setupprojectparameters.h
+ tools_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/tools
+ INSTALLS += tools_headers
+}
diff --git a/src/lib/corelib/tools/tst_tools.cpp b/src/lib/corelib/tools/tst_tools.cpp
new file mode 100644
index 000000000..c1af58f70
--- /dev/null
+++ b/src/lib/corelib/tools/tst_tools.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** 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 "tst_tools.h"
+
+#include <tools/buildoptions.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/hostosinfo.h>
+#include <tools/profile.h>
+#include <tools/settings.h>
+#include <tools/setupprojectparameters.h>
+
+#include <QFileInfo>
+#include <QTemporaryFile>
+#include <QTest>
+
+namespace qbs {
+namespace Internal {
+
+TestTools::TestTools(Settings *settings) : m_settings(settings)
+{
+}
+
+void TestTools::testFileInfo()
+{
+ QCOMPARE(FileInfo::fileName("C:/waffl/copter.exe"), QString("copter.exe"));
+ QCOMPARE(FileInfo::baseName("C:/waffl/copter.exe.lib"), QString("copter"));
+ QCOMPARE(FileInfo::completeBaseName("C:/waffl/copter.exe.lib"), QString("copter.exe"));
+ QCOMPARE(FileInfo::path("abc"), QString("."));
+ QCOMPARE(FileInfo::path("/abc/lol"), QString("/abc"));
+ QVERIFY(!FileInfo::isAbsolute("bla/lol"));
+ QVERIFY(FileInfo::isAbsolute("/bla/lol"));
+ if (HostOsInfo::isWindowsHost())
+ QVERIFY(FileInfo::isAbsolute("C:\\bla\\lol"));
+ QCOMPARE(FileInfo::resolvePath("/abc/lol", "waffl"), QString("/abc/lol/waffl"));
+ QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../foo/bar"), QString("/abc/def/ghi/foo/bar"));
+ QCOMPARE(FileInfo::resolvePath("/abc/def/ghi/jkl/", "../../foo/bar"), QString("/abc/def/foo/bar"));
+ QCOMPARE(FileInfo::resolvePath("/abc", "../../../foo/bar"), QString("/foo/bar"));
+ QCOMPARE(FileInfo("/does/not/exist").lastModified(), FileTime());
+}
+
+void TestTools::fileCaseCheck()
+{
+ QTemporaryFile tempFile(QLatin1String("CamelCase"));
+ QVERIFY(tempFile.open());
+ QFileInfo tempFileInfo(tempFile.fileName());
+ const QString lowerFilePath = tempFileInfo.absolutePath() + QLatin1Char('/')
+ + tempFileInfo.fileName().toLower();
+ const QString upperFilePath = tempFileInfo.absolutePath() + QLatin1Char('/')
+ + tempFileInfo.fileName().toUpper();
+ QVERIFY(FileInfo::isFileCaseCorrect(tempFileInfo.absoluteFilePath()));
+ if (QFile::exists(lowerFilePath))
+ QVERIFY(!FileInfo::isFileCaseCorrect(lowerFilePath));
+ if (QFile::exists(upperFilePath))
+ QVERIFY(!FileInfo::isFileCaseCorrect(upperFilePath));
+}
+
+void TestTools::testProfiles()
+{
+ bool exceptionCaught;
+ Profile parentProfile("parent", m_settings);
+ Profile childProfile("child", m_settings);
+ try {
+ parentProfile.removeBaseProfile();
+ parentProfile.remove("testKey");
+ QCOMPARE(parentProfile.value("testKey", "none").toString(), QLatin1String("none"));
+ parentProfile.setValue("testKey", "testValue");
+ QCOMPARE(parentProfile.value("testKey").toString(), QLatin1String("testValue"));
+
+ childProfile.remove("testKey");
+ childProfile.removeBaseProfile();
+ QCOMPARE(childProfile.value("testKey", "none").toString(), QLatin1String("none"));
+ childProfile.setBaseProfile("parent");
+ QCOMPARE(childProfile.value("testKey").toString(), QLatin1String("testValue"));
+
+ // Change base profile and check if the inherited value also changes.
+ Profile fooProfile("foo", m_settings);
+ fooProfile.setValue("testKey", "gnampf");
+ childProfile.setBaseProfile("foo");
+ QCOMPARE(childProfile.value("testKey", "none").toString(), QLatin1String("gnampf"));
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(!exceptionCaught);
+
+ try {
+ childProfile.setBaseProfile("SmurfAlongWithMe");
+ childProfile.value("blubb");
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(exceptionCaught);
+
+ try {
+ childProfile.setBaseProfile("parent");
+ parentProfile.setBaseProfile("child");
+ QVERIFY(!childProfile.value("blubb").isValid());
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(exceptionCaught);
+
+ try {
+ QVERIFY(!childProfile.allKeys(Profile::KeySelectionNonRecursive).isEmpty());
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(!exceptionCaught);
+
+ try {
+ QVERIFY(!childProfile.allKeys(Profile::KeySelectionRecursive).isEmpty());
+ exceptionCaught = false;
+ } catch (ErrorInfo &) {
+ exceptionCaught = true;
+ }
+ QVERIFY(exceptionCaught);
+}
+
+void TestTools::testBuildConfigMerging()
+{
+ QVariantMap buildConfigMap;
+ buildConfigMap.insert(QLatin1String("topLevelKey"), QLatin1String("topLevelValue"));
+ buildConfigMap.insert(QLatin1String("qbs.toolchain"), QLatin1String("gcc"));
+ buildConfigMap.insert(QLatin1String("qbs.architecture"),
+ QLatin1String("Jean-Claude Pillemann"));
+ buildConfigMap.insert(QLatin1String("cpp.treatWarningsAsErrors"), true);
+ QVariantMap overrideMap;
+ overrideMap.insert(QLatin1String("qbs.toolchain"), QLatin1String("clang"));
+ SetupProjectParameters params;
+ params.setBuildConfiguration(buildConfigMap);
+ params.setOverriddenValues(overrideMap);
+ const QVariantMap finalMap = params.finalBuildConfigurationTree();
+ QCOMPARE(finalMap.count(), 3);
+ QCOMPARE(finalMap.value(QLatin1String("topLevelKey")).toString(),
+ QString::fromLatin1("topLevelValue"));
+ const QVariantMap finalQbsMap = finalMap.value(QLatin1String("qbs")).toMap();
+ QCOMPARE(finalQbsMap.count(), 2);
+ QCOMPARE(finalQbsMap.value(QLatin1String("toolchain")).toString(),
+ QString::fromLatin1("clang"));
+ QCOMPARE(finalQbsMap.value(QLatin1String("architecture")).toString(),
+ QString::fromLatin1("Jean-Claude Pillemann"));
+ const QVariantMap finalCppMap = finalMap.value(QLatin1String("cpp")).toMap();
+ QCOMPARE(finalCppMap.count(), 1);
+ QCOMPARE(finalCppMap.value(QLatin1String("treatWarningsAsErrors")).toBool(), true);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/tst_tools.h b/src/lib/corelib/tools/tst_tools.h
new file mode 100644
index 000000000..c7168a686
--- /dev/null
+++ b/src/lib/corelib/tools/tst_tools.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** 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 "qbs_export.h"
+
+#include <QObject>
+
+namespace qbs {
+class Settings;
+
+namespace Internal {
+
+class QBS_EXPORT TestTools : public QObject
+{
+ Q_OBJECT
+
+public:
+ TestTools(Settings *settings);
+
+private slots:
+ void testFileInfo();
+ void fileCaseCheck();
+ void testProfiles();
+ void testBuildConfigMerging();
+
+private:
+ Settings * const m_settings;
+};
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/weakpointer.h b/src/lib/corelib/tools/weakpointer.h
new file mode 100644
index 000000000..3da1abd6d
--- /dev/null
+++ b/src/lib/corelib/tools/weakpointer.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+#ifndef QBS_WEAKPOINTER_H
+#define QBS_WEAKPOINTER_H
+
+#include <QWeakPointer>
+
+namespace qbs {
+namespace Internal {
+
+template<typename T> class WeakPointer : public QWeakPointer<T>
+{
+public:
+ WeakPointer() : QWeakPointer<T>() {}
+ WeakPointer(const QSharedPointer<T> &sharedPointer) : QWeakPointer<T>(sharedPointer) {}
+ template <class X> WeakPointer(const QSharedPointer<X> &sp) : QWeakPointer<T>(sp) { }
+
+
+ operator T*() const { return checkedData(); }
+ T *operator->() const { return checkedData(); }
+ T operator*() const { return *checkedData(); }
+
+private:
+ T *checkedData() const {
+ T * const d = QWeakPointer<T>::data();
+ Q_ASSERT(d); // Calling code is not expecting this situation.
+ return d;
+ }
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_WEAKPOINTER_H
diff --git a/src/lib/corelib/use_corelib.pri b/src/lib/corelib/use_corelib.pri
new file mode 100644
index 000000000..875c51f51
--- /dev/null
+++ b/src/lib/corelib/use_corelib.pri
@@ -0,0 +1,47 @@
+include(../../../qbs_version.pri)
+
+isEmpty(QBSLIBDIR) {
+ QBSLIBDIR = $$OUT_PWD/../../../lib
+}
+
+QT += script xml
+
+unix {
+ LIBS += -L$$QBSLIBDIR -lqbscore
+}
+
+!disable_rpath {
+ linux-*:QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,\$\$ORIGIN/../lib\'
+ macx:QMAKE_LFLAGS += -Wl,-rpath,@loader_path/../lib
+}
+
+!CONFIG(static, static|shared) {
+ QBSCORELIBSUFFIX = $$QBS_VERSION_MAJ
+}
+
+win32 {
+ CONFIG(debug, debug|release) {
+ QBSCORELIB = qbscored$$QBSCORELIBSUFFIX
+ }
+ CONFIG(release, debug|release) {
+ QBSCORELIB = qbscore$$QBSCORELIBSUFFIX
+ }
+ win32-msvc* {
+ LIBS += /LIBPATH:$$QBSLIBDIR
+ QBSCORELIB = $${QBSCORELIB}.lib
+ LIBS += Shell32.lib
+ } else {
+ LIBS += -L$${QBSLIBDIR}
+ QBSCORELIB = lib$${QBSCORELIB}
+ }
+ LIBS += $$QBSCORELIB
+}
+
+INCLUDEPATH += \
+ $$PWD
+
+CONFIG += depend_includepath
+
+CONFIG(static, static|shared) {
+ DEFINES += QBS_STATIC_LIB
+}
diff --git a/src/lib/corelib/use_installed_corelib.pri b/src/lib/corelib/use_installed_corelib.pri
new file mode 100644
index 000000000..bac8d6da7
--- /dev/null
+++ b/src/lib/corelib/use_installed_corelib.pri
@@ -0,0 +1,38 @@
+include(qbs_version.pri)
+
+QT += script xml
+
+QBSLIBDIR=$${PWD}/../../lib
+unix {
+ LIBS += -L$$QBSLIBDIR -lqbscore
+}
+
+!disable_rpath:unix:QMAKE_LFLAGS += -Wl,-rpath,$${QBSLIBDIR}
+
+!CONFIG(static, static|shared) {
+ QBSCORELIBSUFFIX = $$QBS_VERSION_MAJ
+}
+
+win32 {
+ CONFIG(debug, debug|release) {
+ QBSCORELIB = qbscored$$QBSCORELIBSUFFIX
+ }
+ CONFIG(release, debug|release) {
+ QBSCORELIB = qbscore$$QBSCORELIBSUFFIX
+ }
+ win32-msvc* {
+ LIBS += /LIBPATH:$$QBSLIBDIR
+ QBSCORELIB = $${QBSCORELIB}.lib
+ LIBS += Shell32.lib
+ } else {
+ LIBS += -L$${QBSLIBDIR}
+ QBSCORELIB = lib$${QBSCORELIB}
+ }
+ LIBS += $$QBSCORELIB
+}
+
+INCLUDEPATH += $${PWD} $${PWD}/..
+
+CONFIG(static, static|shared) {
+ DEFINES += QBS_STATIC_LIB
+}