From 28f88162f03aec03a2d86b1684200367873d06bc Mon Sep 17 00:00:00 2001 From: Dmitry Shachnev Date: Fri, 23 Dec 2016 08:22:04 +0000 Subject: [PATCH] Import qbs_1.7.0+dfsg.orig.tar.gz [dgit import orig qbs_1.7.0+dfsg.orig.tar.gz] --- LGPL_EXCEPTION.txt | 22 + LICENSE.LGPLv21 | 504 ++ LICENSE.LGPLv3 | 173 + README | 34 + bin/ibmsvc.xml | 12 + bin/ibqbs.bat | 1 + dist/dist.qbs | 176 + doc/classic.css | 295 ++ doc/config/macros.qdocconf | 2 + doc/config/qbs-project.qdocconf | 39 + doc/config/style/qt5-sidebar.html | 13 + doc/doc.pri | 66 + doc/doc.qbs | 35 + doc/fixnavi.pl | 182 + doc/qbs-online.qdocconf | 19 + doc/qbs.qdoc | 872 +++ doc/qbs.qdocconf | 2 + doc/reference/commands.qdoc | 218 + .../items/convenience/androidapk.qdoc | 103 + .../items/convenience/application.qdoc | 43 + .../convenience/applicationextension.qdoc | 41 + .../items/convenience/autotestrunner.qdoc | 105 + .../items/convenience/cppapplication.qdoc | 43 + .../items/convenience/dynamiclibrary.qdoc | 70 + .../items/convenience/innosetup.qdoc | 40 + .../items/convenience/installpackage.qdoc | 87 + .../convenience/javaclasscollection.qdoc | 42 + .../items/convenience/javajarfile.qdoc | 54 + .../items/convenience/loadablemodule.qdoc | 41 + .../items/convenience/qtapplication.qdoc | 45 + .../items/convenience/qtguiapplication.qdoc | 57 + .../items/convenience/staticlibrary.qdoc | 55 + .../items/convenience/xpcservice.qdoc | 40 + doc/reference/items/language/artifact.qdoc | 84 + doc/reference/items/language/depends.qdoc | 136 + doc/reference/items/language/export.qdoc | 69 + doc/reference/items/language/filetagger.qdoc | 86 + doc/reference/items/language/group.qbs | 46 + doc/reference/items/language/group.qdoc | 158 + doc/reference/items/language/module.qdoc | 201 + doc/reference/items/language/probe.qdoc | 70 + doc/reference/items/language/product.qdoc | 189 + doc/reference/items/language/project.qdoc | 111 + doc/reference/items/language/properties.qdoc | 115 + .../items/language/propertyoptions.qdoc | 67 + doc/reference/items/language/rule.qdoc | 294 ++ doc/reference/items/language/scanner.qdoc | 95 + doc/reference/items/language/subproject.qdoc | 75 + .../jsextensions/jsextension-environment.qdoc | 69 + .../jsextensions/jsextension-file.qdoc | 94 + .../jsextensions/jsextension-fileinfo.qdoc | 106 + .../jsextensions/jsextension-process.qdoc | 175 + .../jsextension-propertylist.qdoc | 131 + .../jsextension-temporarydir.qdoc | 68 + .../jsextensions/jsextension-textfile.qdoc | 105 + .../jsextensions/jsextension-utilities.qdoc | 56 + .../jsextensions/jsextensions-general.qdoc | 164 + doc/reference/list-of-tools.qdoc | 67 + doc/reference/modules/android-ndk-module.qdoc | 119 + doc/reference/modules/android-sdk-module.qdoc | 124 + doc/reference/modules/archiver-module.qdoc | 109 + doc/reference/modules/bundle-module.qdoc | 296 ++ doc/reference/modules/cpp-module.qdoc | 812 +++ doc/reference/modules/ib-module.qdoc | 198 + doc/reference/modules/innosetup-module.qdoc | 161 + doc/reference/modules/java-module.qdoc | 209 + doc/reference/modules/lexyacc-module.qdoc | 116 + doc/reference/modules/nodejs-module.qdoc | 58 + doc/reference/modules/nsis-module.qdoc | 159 + doc/reference/modules/qbs-module.qdoc | 327 ++ doc/reference/modules/qt-modules.qdoc | 639 +++ doc/reference/modules/typescript-module.qdoc | 167 + doc/reference/modules/wix-module.qdoc | 230 + doc/reference/modules/xcode-module.qdoc | 141 + doc/reference/reference.qdoc | 98 + doc/templates/images/arrow.png | Bin 0 -> 1071 bytes doc/templates/images/arrow_down.png | Bin 0 -> 177 bytes doc/templates/images/bg_l.png | Bin 0 -> 100 bytes doc/templates/images/bg_l_blank.png | Bin 0 -> 84 bytes doc/templates/images/bg_ll_blank.png | Bin 0 -> 320 bytes doc/templates/images/bg_r.png | Bin 0 -> 96 bytes doc/templates/images/bg_ul_blank.png | Bin 0 -> 304 bytes doc/templates/images/bgrContent.png | Bin 0 -> 149 bytes doc/templates/images/blu_dot.png | Bin 0 -> 168 bytes doc/templates/images/box_bg.png | Bin 0 -> 89 bytes doc/templates/images/breadcrumb.png | Bin 0 -> 134 bytes doc/templates/images/btn_next.png | Bin 0 -> 689 bytes doc/templates/images/btn_prev.png | Bin 0 -> 676 bytes doc/templates/images/bullet_dn.png | Bin 0 -> 230 bytes doc/templates/images/bullet_gt.png | Bin 0 -> 124 bytes doc/templates/images/bullet_sq.png | Bin 0 -> 74 bytes doc/templates/images/bullet_up.png | Bin 0 -> 210 bytes doc/templates/images/feedbackground.png | Bin 0 -> 263 bytes doc/templates/images/header.png | Bin 0 -> 3768 bytes doc/templates/images/header_bg.png | Bin 0 -> 114 bytes doc/templates/images/home.png | Bin 0 -> 1076 bytes doc/templates/images/horBar.png | Bin 0 -> 2807 bytes doc/templates/images/ico_note.png | Bin 0 -> 649 bytes doc/templates/images/ico_note_attention.png | Bin 0 -> 529 bytes doc/templates/images/ico_out.png | Bin 0 -> 362 bytes doc/templates/images/page.png | Bin 0 -> 3102 bytes doc/templates/images/page_bg.png | Bin 0 -> 84 bytes doc/templates/images/qt_icon.png | Bin 0 -> 4775 bytes doc/templates/images/spinner.gif | Bin 0 -> 2037 bytes doc/templates/images/sprites-combined.png | Bin 0 -> 62534 bytes doc/templates/scripts/functions.js | 194 + doc/templates/scripts/narrow.js | 89 + doc/templates/scripts/superfish.js | 121 + doc/templates/style/narrow.css | 270 + doc/templates/style/offline.css | 632 +++ doc/templates/style/style.css | 1592 ++++++ doc/templates/style/style_ie6.css | 54 + doc/templates/style/style_ie7.css | 19 + doc/templates/style/style_ie8.css | 0 doc/templates/style/superfish.css | 51 + doc/templates/style/superfish_skin.css | 83 + examples/app-and-lib/app/app.qbs | 48 + examples/app-and-lib/app/main.cpp | 56 + examples/app-and-lib/app_and_lib.qbs | 46 + examples/app-and-lib/lib/lib.cpp | 61 + examples/app-and-lib/lib/lib.h | 45 + examples/app-and-lib/lib/lib.qbs | 55 + .../cocoa-application/CocoaApplication.qbs | 70 + .../project.pbxproj | 309 ++ .../CocoaApplication/AppDelegate.h | 47 + .../CocoaApplication/AppDelegate.m | 47 + .../CocoaApplication-Info.plist | 34 + .../CocoaApplication-Prefix.pch | 33 + .../CocoaApplication/en.lproj/Credits.rtf | 29 + .../en.lproj/InfoPlist.strings | 1 + .../CocoaApplication/en.lproj/MainMenu.xib | 4666 +++++++++++++++++ .../cocoa-application/CocoaApplication/main.m | 36 + .../CocoaTouchApplication.qbs | 79 + .../project.pbxproj | 348 ++ .../CocoaTouchApplication/AppDelegate.h | 56 + .../CocoaTouchApplication/AppDelegate.m | 105 + .../CocoaTouchApplication-Info.plist | 55 + .../CocoaTouchApplication-Prefix.pch | 40 + .../CocoaTouchApplication/Default-568h@2x.png | Bin 0 -> 18594 bytes .../CocoaTouchApplication/Default.png | Bin 0 -> 6540 bytes .../CocoaTouchApplication/Default@2x.png | Bin 0 -> 16107 bytes .../DetailViewController.h | 54 + .../DetailViewController.m | 116 + .../MasterViewController.h | 53 + .../MasterViewController.m | 162 + .../en.lproj/DetailViewController_iPad.xib | 223 + .../en.lproj/DetailViewController_iPhone.xib | 253 + .../en.lproj/InfoPlist.strings | 1 + .../en.lproj/MasterViewController_iPad.xib | 152 + .../en.lproj/MasterViewController_iPhone.xib | 147 + .../CocoaTouchApplication/main.m | 40 + examples/code-generator/code-generator.qbs | 71 + examples/code-generator/hwgen.cpp | 65 + examples/collidingmice/collidingmice.qbs | 56 + examples/collidingmice/images/cheese.jpg | Bin 0 -> 3029 bytes examples/collidingmice/main.cpp | 105 + examples/collidingmice/mice.qrc | 5 + examples/collidingmice/mouse.cpp | 210 + examples/collidingmice/mouse.h | 67 + examples/examples.qbs | 51 + examples/helloworld-complex/hello.qbs | 76 + examples/helloworld-complex/src/foo.cpp | 59 + examples/helloworld-complex/src/foo.h | 46 + examples/helloworld-complex/src/main.cpp | 79 + .../helloworld-complex/src/specialfeature.cpp | 58 + .../helloworld-complex/src/specialfeature.h | 45 + examples/helloworld-minimal/hello.qbs | 43 + examples/helloworld-minimal/main.cpp | 56 + examples/helloworld-qt/hello.qbs | 43 + examples/helloworld-qt/main.cpp | 59 + examples/install-bundle/coreutils.cpp | 56 + examples/install-bundle/coreutils.h | 43 + examples/install-bundle/install-bundle.qbs | 57 + examples/install-bundle/main.cpp | 60 + qbs-resources/imports/QbsApp.qbs | 29 + qbs-resources/imports/QbsAutotest.qbs | 25 + .../imports/QbsFunctions/functions.js | 18 + qbs-resources/imports/QbsLibrary.qbs | 40 + qbs-resources/imports/QbsProduct.qbs | 17 + .../modules/qbsbuildconfig/qbsbuildconfig.qbs | 26 + qbs.pro | 59 + qbs.qbs | 59 + qbs_version.pri | 4 + .../imports/qbs/BundleTools/bundle-tools.js | 78 + .../imports/qbs/DarwinTools/darwin-tools.js | 254 + share/qbs/imports/qbs/ModUtils/utils.js | 579 ++ share/qbs/imports/qbs/PathTools/path-tools.js | 205 + .../imports/qbs/Probes/AndroidNdkProbe.qbs | 84 + .../imports/qbs/Probes/AndroidSdkProbe.qbs | 68 + share/qbs/imports/qbs/Probes/BinaryProbe.qbs | 37 + .../qbs/imports/qbs/Probes/FrameworkProbe.qbs | 46 + share/qbs/imports/qbs/Probes/GccProbe.qbs | 96 + share/qbs/imports/qbs/Probes/IncludeProbe.qbs | 40 + .../qbs/imports/qbs/Probes/InnoSetupProbe.qbs | 54 + share/qbs/imports/qbs/Probes/JdkProbe.qbs | 59 + share/qbs/imports/qbs/Probes/MsvcProbe.qbs | 69 + share/qbs/imports/qbs/Probes/NodeJsProbe.qbs | 49 + share/qbs/imports/qbs/Probes/NpmProbe.qbs | 59 + share/qbs/imports/qbs/Probes/PathProbe.qbs | 59 + .../qbs/imports/qbs/Probes/PkgConfigProbe.qbs | 95 + .../imports/qbs/Probes/TypeScriptProbe.qbs | 78 + share/qbs/imports/qbs/Probes/WiXProbe.qbs | 76 + share/qbs/imports/qbs/Probes/path-probe.js | 82 + share/qbs/imports/qbs/UnixUtils/unix-utils.js | 59 + .../imports/qbs/WindowsUtils/windows-utils.js | 84 + share/qbs/imports/qbs/base/AndroidApk.qbs | 76 + share/qbs/imports/qbs/base/Application.qbs | 46 + .../imports/qbs/base/ApplicationExtension.qbs | 64 + share/qbs/imports/qbs/base/AutotestRunner.qbs | 72 + share/qbs/imports/qbs/base/CppApplication.qbs | 36 + share/qbs/imports/qbs/base/DynamicLibrary.qbs | 35 + share/qbs/imports/qbs/base/InnoSetup.qbs | 34 + share/qbs/imports/qbs/base/InstallPackage.qbs | 69 + .../imports/qbs/base/JavaClassCollection.qbs | 34 + share/qbs/imports/qbs/base/JavaJarFile.qbs | 35 + share/qbs/imports/qbs/base/Library.qbs | 46 + share/qbs/imports/qbs/base/LoadableModule.qbs | 36 + share/qbs/imports/qbs/base/NSISSetup.qbs | 34 + share/qbs/imports/qbs/base/NetModule.qbs | 4 + .../imports/qbs/base/NodeJSApplication.qbs | 33 + share/qbs/imports/qbs/base/QtApplication.qbs | 35 + .../qbs/imports/qbs/base/QtGuiApplication.qbs | 41 + share/qbs/imports/qbs/base/StaticLibrary.qbs | 35 + .../qbs/base/WindowsInstallerPackage.qbs | 34 + .../imports/qbs/base/WindowsSetupPackage.qbs | 34 + share/qbs/imports/qbs/base/XPCService.qbs | 49 + share/qbs/modules/Android/ndk/ndk.qbs | 162 + share/qbs/modules/Android/ndk/utils.js | 122 + share/qbs/modules/Android/sdk/sdk.qbs | 305 ++ share/qbs/modules/Android/sdk/utils.js | 110 + share/qbs/modules/archiver/archiver.qbs | 239 + share/qbs/modules/bundle/BundleModule.qbs | 746 +++ .../bundle/MacOSX-Package-Types.xcspec | 462 ++ .../bundle/MacOSX-Product-Types.xcspec | 585 +++ share/qbs/modules/bundle/bundle.js | 214 + share/qbs/modules/bundle/update-specs.sh | 50 + share/qbs/modules/cli/CLIModule.qbs | 194 + share/qbs/modules/cli/cli.js | 183 + share/qbs/modules/cli/mono.qbs | 27 + share/qbs/modules/cli/windows-dotnet.qbs | 29 + share/qbs/modules/cpp/CppModule.qbs | 378 ++ share/qbs/modules/cpp/DarwinGCC.qbs | 176 + share/qbs/modules/cpp/GenericGCC.qbs | 526 ++ share/qbs/modules/cpp/LinuxGCC.qbs | 69 + share/qbs/modules/cpp/UnixGCC.qbs | 47 + share/qbs/modules/cpp/android-gcc.qbs | 261 + share/qbs/modules/cpp/gcc.js | 1016 ++++ share/qbs/modules/cpp/genericunix-gcc.qbs | 36 + share/qbs/modules/cpp/ios-gcc.qbs | 79 + share/qbs/modules/cpp/macos-gcc.qbs | 43 + share/qbs/modules/cpp/msvc.js | 363 ++ share/qbs/modules/cpp/tvos-gcc.qbs | 46 + share/qbs/modules/cpp/watchos-gcc.qbs | 47 + share/qbs/modules/cpp/windows-mingw.qbs | 112 + share/qbs/modules/cpp/windows-msvc.qbs | 368 ++ share/qbs/modules/ib/IBModule.qbs | 206 + share/qbs/modules/ib/ib.js | 301 ++ .../qbs/modules/innosetup/InnoSetupModule.qbs | 142 + share/qbs/modules/java/JavaModule.qbs | 295 ++ .../qbs/modules/java/io/qt/qbs/Artifact.java | 75 + .../io/qt/qbs/ArtifactListJsonWriter.java | 153 + .../io/qt/qbs/ArtifactListTextWriter.java | 60 + .../java/io/qt/qbs/ArtifactListWriter.java | 40 + .../java/io/qt/qbs/ArtifactListXmlWriter.java | 98 + .../qt/qbs/tools/JavaCompilerScannerTool.java | 66 + .../qt/qbs/tools/utils/ArtifactProcessor.java | 74 + .../qt/qbs/tools/utils/ArtifactScanner.java | 126 + .../qbs/tools/utils/JavaCompilerOptions.java | 154 + .../qbs/tools/utils/JavaCompilerScanner.java | 194 + .../io/qt/qbs/tools/utils/NullFileObject.java | 147 + share/qbs/modules/java/utils.js | 341 ++ share/qbs/modules/lex_yacc/lexyacc.js | 17 + share/qbs/modules/lex_yacc/lexyacc.qbs | 70 + share/qbs/modules/nodejs/NodeJS.qbs | 151 + share/qbs/modules/nodejs/nodejs.js | 41 + share/qbs/modules/nsis/NSISModule.qbs | 242 + share/qbs/modules/qbs/common.qbs | 204 + .../modules/typescript/TypeScriptModule.qbs | 295 ++ .../typescript/qbs-tsc-scan/.gitignore | 5 + .../typescript/qbs-tsc-scan/qbs-tsc-scan.ts | 68 + share/qbs/modules/typescript/typescript.js | 273 + share/qbs/modules/wix/WiXModule.qbs | 441 ++ share/qbs/modules/xcode/xcode.js | 131 + share/qbs/modules/xcode/xcode.qbs | 400 ++ share/share.qbs | 53 + src/app/app.pri | 22 + src/app/app.pro | 10 + src/app/apps.qbs | 13 + src/app/config-ui/Info.plist | 14 + src/app/config-ui/commandlineparser.cpp | 106 + src/app/config-ui/commandlineparser.h | 65 + src/app/config-ui/config-ui.pro | 26 + src/app/config-ui/config-ui.qbs | 31 + src/app/config-ui/fgapp.mm | 47 + src/app/config-ui/main.cpp | 71 + src/app/config-ui/mainwindow.cpp | 206 + src/app/config-ui/mainwindow.h | 74 + src/app/config-ui/mainwindow.ui | 37 + src/app/config/config.pro | 13 + src/app/config/config.qbs | 14 + src/app/config/configcommand.h | 60 + src/app/config/configcommandexecutor.cpp | 152 + src/app/config/configcommandexecutor.h | 66 + src/app/config/configcommandlineparser.cpp | 145 + src/app/config/configcommandlineparser.h | 67 + src/app/config/configmain.cpp | 73 + src/app/qbs-qmltypes/main.cpp | 77 + src/app/qbs-qmltypes/qbs-qmltypes.pro | 6 + src/app/qbs-qmltypes/qbs-qmltypes.qbs | 9 + src/app/qbs-setup-android/android-setup.cpp | 115 + src/app/qbs-setup-android/android-setup.h | 55 + .../qbs-setup-android/commandlineparser.cpp | 139 + src/app/qbs-setup-android/commandlineparser.h | 74 + src/app/qbs-setup-android/main.cpp | 68 + .../qbs-setup-android/qbs-setup-android.pro | 12 + .../qbs-setup-android/qbs-setup-android.qbs | 12 + src/app/qbs-setup-qt/commandlineparser.cpp | 134 + src/app/qbs-setup-qt/commandlineparser.h | 72 + src/app/qbs-setup-qt/main.cpp | 103 + .../qbs-setup-qt/qbs-setup-qt.exe.manifest | 13 + src/app/qbs-setup-qt/qbs-setup-qt.pro | 17 + src/app/qbs-setup-qt/qbs-setup-qt.qbs | 19 + src/app/qbs-setup-qt/qbs-setup-qt.rc | 4 + src/app/qbs-setup-qt/setupqt.cpp | 590 +++ src/app/qbs-setup-qt/setupqt.h | 67 + .../commandlineparser.cpp | 141 + .../qbs-setup-toolchains/commandlineparser.h | 74 + src/app/qbs-setup-toolchains/main.cpp | 82 + src/app/qbs-setup-toolchains/msvcprobe.cpp | 317 ++ src/app/qbs-setup-toolchains/msvcprobe.h | 55 + src/app/qbs-setup-toolchains/probe.cpp | 312 ++ src/app/qbs-setup-toolchains/probe.h | 56 + .../qbs-setup-toolchains.exe.manifest | 13 + .../qbs-setup-toolchains.pro | 20 + .../qbs-setup-toolchains.qbs | 17 + .../qbs-setup-toolchains.rc | 4 + src/app/qbs-setup-toolchains/xcodeprobe.cpp | 235 + src/app/qbs-setup-toolchains/xcodeprobe.h | 52 + src/app/qbs/application.cpp | 78 + src/app/qbs/application.h | 66 + src/app/qbs/commandlinefrontend.cpp | 641 +++ src/app/qbs/commandlinefrontend.h | 124 + src/app/qbs/consoleprogressobserver.cpp | 112 + src/app/qbs/consoleprogressobserver.h | 71 + src/app/qbs/ctrlchandler.cpp | 80 + src/app/qbs/ctrlchandler.h | 45 + src/app/qbs/main.cpp | 96 + src/app/qbs/parser/command.cpp | 539 ++ src/app/qbs/parser/command.h | 234 + src/app/qbs/parser/commandlineoption.cpp | 661 +++ src/app/qbs/parser/commandlineoption.h | 405 ++ src/app/qbs/parser/commandlineoptionpool.cpp | 273 + src/app/qbs/parser/commandlineoptionpool.h | 87 + src/app/qbs/parser/commandlineparser.cpp | 648 +++ src/app/qbs/parser/commandlineparser.h | 96 + src/app/qbs/parser/commandpool.cpp | 97 + src/app/qbs/parser/commandpool.h | 66 + src/app/qbs/parser/commandtype.h | 52 + src/app/qbs/parser/parser.pri | 16 + src/app/qbs/qbs.pro | 28 + src/app/qbs/qbs.qbs | 47 + src/app/qbs/qbstool.cpp | 104 + src/app/qbs/qbstool.h | 66 + src/app/qbs/status.cpp | 168 + src/app/qbs/status.h | 50 + src/app/shared/logging/coloredoutput.cpp | 101 + src/app/shared/logging/coloredoutput.h | 72 + src/app/shared/logging/consolelogger.cpp | 116 + src/app/shared/logging/consolelogger.h | 88 + src/app/shared/logging/logging.pri | 2 + src/install_prefix.pri | 1 + src/lib/corelib/api/api.pri | 48 + src/lib/corelib/api/changeset.cpp | 397 ++ src/lib/corelib/api/changeset.h | 140 + src/lib/corelib/api/internaljobs.cpp | 451 ++ src/lib/corelib/api/internaljobs.h | 230 + src/lib/corelib/api/jobs.cpp | 360 ++ src/lib/corelib/api/jobs.h | 167 + src/lib/corelib/api/languageinfo.cpp | 133 + src/lib/corelib/api/languageinfo.h | 61 + src/lib/corelib/api/project.cpp | 1208 +++++ src/lib/corelib/api/project.h | 161 + src/lib/corelib/api/project_p.h | 137 + src/lib/corelib/api/projectdata.cpp | 872 +++ src/lib/corelib/api/projectdata.h | 241 + src/lib/corelib/api/projectdata_p.h | 142 + src/lib/corelib/api/projectfileupdater.cpp | 549 ++ src/lib/corelib/api/projectfileupdater.h | 147 + src/lib/corelib/api/propertymap_p.h | 57 + src/lib/corelib/api/qmljsrewriter.cpp | 728 +++ src/lib/corelib/api/qmljsrewriter.h | 130 + src/lib/corelib/api/rulecommand.cpp | 160 + src/lib/corelib/api/rulecommand.h | 88 + src/lib/corelib/api/rulecommand_p.h | 69 + src/lib/corelib/api/runenvironment.cpp | 361 ++ src/lib/corelib/api/runenvironment.h | 94 + .../buildgraph/abstractcommandexecutor.cpp | 85 + .../buildgraph/abstractcommandexecutor.h | 99 + src/lib/corelib/buildgraph/artifact.cpp | 189 + src/lib/corelib/buildgraph/artifact.h | 144 + .../corelib/buildgraph/artifactcleaner.cpp | 229 + src/lib/corelib/buildgraph/artifactcleaner.h | 72 + src/lib/corelib/buildgraph/artifactset.cpp | 101 + src/lib/corelib/buildgraph/artifactset.h | 70 + .../corelib/buildgraph/artifactvisitor.cpp | 82 + src/lib/corelib/buildgraph/artifactvisitor.h | 72 + src/lib/corelib/buildgraph/buildgraph.cpp | 552 ++ src/lib/corelib/buildgraph/buildgraph.h | 95 + src/lib/corelib/buildgraph/buildgraph.pri | 82 + .../corelib/buildgraph/buildgraphloader.cpp | 879 ++++ src/lib/corelib/buildgraph/buildgraphloader.h | 148 + src/lib/corelib/buildgraph/buildgraphnode.cpp | 90 + src/lib/corelib/buildgraph/buildgraphnode.h | 95 + .../corelib/buildgraph/buildgraphvisitor.h | 67 + src/lib/corelib/buildgraph/command.cpp | 429 ++ src/lib/corelib/buildgraph/command.h | 180 + src/lib/corelib/buildgraph/cycledetector.cpp | 107 + src/lib/corelib/buildgraph/cycledetector.h | 78 + src/lib/corelib/buildgraph/depscanner.cpp | 224 + src/lib/corelib/buildgraph/depscanner.h | 110 + .../buildgraph/emptydirectoriesremover.cpp | 116 + .../buildgraph/emptydirectoriesremover.h | 72 + src/lib/corelib/buildgraph/executor.cpp | 1182 +++++ src/lib/corelib/buildgraph/executor.h | 186 + src/lib/corelib/buildgraph/executorjob.cpp | 174 + src/lib/corelib/buildgraph/executorjob.h | 98 + src/lib/corelib/buildgraph/filedependency.cpp | 102 + src/lib/corelib/buildgraph/filedependency.h | 87 + src/lib/corelib/buildgraph/forward_decls.h | 73 + .../buildgraph/inputartifactscanner.cpp | 407 ++ .../corelib/buildgraph/inputartifactscanner.h | 153 + .../corelib/buildgraph/jscommandexecutor.cpp | 245 + .../corelib/buildgraph/jscommandexecutor.h | 83 + src/lib/corelib/buildgraph/nodeset.cpp | 99 + src/lib/corelib/buildgraph/nodeset.h | 204 + src/lib/corelib/buildgraph/nodetreedumper.cpp | 126 + src/lib/corelib/buildgraph/nodetreedumper.h | 82 + .../buildgraph/processcommandexecutor.cpp | 366 ++ .../buildgraph/processcommandexecutor.h | 99 + .../corelib/buildgraph/productbuilddata.cpp | 155 + src/lib/corelib/buildgraph/productbuilddata.h | 94 + .../corelib/buildgraph/productinstaller.cpp | 235 + src/lib/corelib/buildgraph/productinstaller.h | 84 + .../corelib/buildgraph/projectbuilddata.cpp | 550 ++ src/lib/corelib/buildgraph/projectbuilddata.h | 121 + src/lib/corelib/buildgraph/qtmocscanner.cpp | 236 + src/lib/corelib/buildgraph/qtmocscanner.h | 88 + .../buildgraph/rescuableartifactdata.cpp | 99 + .../buildgraph/rescuableartifactdata.h | 96 + src/lib/corelib/buildgraph/rulegraph.cpp | 152 + src/lib/corelib/buildgraph/rulegraph.h | 90 + src/lib/corelib/buildgraph/rulenode.cpp | 188 + src/lib/corelib/buildgraph/rulenode.h | 90 + .../corelib/buildgraph/rulesapplicator.cpp | 561 ++ src/lib/corelib/buildgraph/rulesapplicator.h | 101 + .../buildgraph/rulesevaluationcontext.cpp | 129 + .../buildgraph/rulesevaluationcontext.h | 98 + .../corelib/buildgraph/scanresultcache.cpp | 74 + src/lib/corelib/buildgraph/scanresultcache.h | 96 + .../corelib/buildgraph/timestampsupdater.cpp | 96 + .../corelib/buildgraph/timestampsupdater.h | 60 + src/lib/corelib/buildgraph/transformer.cpp | 286 + src/lib/corelib/buildgraph/transformer.h | 101 + src/lib/corelib/buildgraph/tst_buildgraph.cpp | 140 + src/lib/corelib/buildgraph/tst_buildgraph.h | 76 + src/lib/corelib/corelib.pro | 35 + src/lib/corelib/corelib.qbs | 481 ++ .../clangcompilationdb/clangcompilationdb.pri | 5 + .../clangcompilationdb/clangcompilationdb.qbs | 19 + .../clangcompilationdbgenerator.cpp | 158 + .../clangcompilationdbgenerator.h | 67 + .../generators/generatableprojectiterator.cpp | 89 + .../generators/generatableprojectiterator.h | 63 + src/lib/corelib/generators/generator.cpp | 228 + src/lib/corelib/generators/generator.h | 104 + src/lib/corelib/generators/generatordata.cpp | 136 + src/lib/corelib/generators/generatordata.h | 80 + src/lib/corelib/generators/generators.pri | 13 + src/lib/corelib/generators/generators.qbs | 8 + .../generators/igeneratableprojectvisitor.h | 107 + .../visualstudio/io/msbuildprojectwriter.cpp | 229 + .../visualstudio/io/msbuildprojectwriter.h | 56 + .../io/visualstudiosolutionwriter.cpp | 145 + .../io/visualstudiosolutionwriter.h | 64 + .../visualstudio/msbuild/imsbuildgroup.cpp | 67 + .../visualstudio/msbuild/imsbuildgroup.h | 65 + .../visualstudio/msbuild/imsbuildnode.cpp | 39 + .../visualstudio/msbuild/imsbuildnode.h | 47 + .../msbuild/imsbuildnodevisitor.h | 81 + .../visualstudio/msbuild/imsbuildproperty.cpp | 83 + .../visualstudio/msbuild/imsbuildproperty.h | 66 + .../msbuild/items/msbuildclcompile.cpp | 42 + .../msbuild/items/msbuildclcompile.h | 48 + .../msbuild/items/msbuildclinclude.cpp | 42 + .../msbuild/items/msbuildclinclude.h | 48 + .../msbuild/items/msbuildfileitem.cpp | 74 + .../msbuild/items/msbuildfileitem.h | 60 + .../msbuild/items/msbuildfilter.cpp | 119 + .../msbuild/items/msbuildfilter.h | 68 + .../msbuild/items/msbuildlink.cpp | 44 + .../visualstudio/msbuild/items/msbuildlink.h | 49 + .../msbuild/items/msbuildnone.cpp | 40 + .../visualstudio/msbuild/items/msbuildnone.h | 48 + .../visualstudio/msbuild/msbuildimport.cpp | 88 + .../visualstudio/msbuild/msbuildimport.h | 70 + .../msbuild/msbuildimportgroup.cpp | 76 + .../visualstudio/msbuild/msbuildimportgroup.h | 66 + .../visualstudio/msbuild/msbuilditem.cpp | 95 + .../visualstudio/msbuild/msbuilditem.h | 72 + .../msbuild/msbuilditemdefinitiongroup.cpp | 59 + .../msbuild/msbuilditemdefinitiongroup.h | 59 + .../visualstudio/msbuild/msbuilditemgroup.cpp | 76 + .../visualstudio/msbuild/msbuilditemgroup.h | 66 + .../msbuild/msbuilditemmetadata.cpp | 57 + .../msbuild/msbuilditemmetadata.h | 60 + .../visualstudio/msbuild/msbuildproject.cpp | 99 + .../visualstudio/msbuild/msbuildproject.h | 68 + .../visualstudio/msbuild/msbuildproperty.cpp | 57 + .../visualstudio/msbuild/msbuildproperty.h | 59 + .../msbuild/msbuildpropertygroup.cpp | 82 + .../msbuild/msbuildpropertygroup.h | 68 + .../visualstudio/msbuildfiltersproject.cpp | 173 + .../visualstudio/msbuildfiltersproject.h | 54 + .../msbuildqbsgenerateproject.cpp | 70 + .../visualstudio/msbuildqbsgenerateproject.h | 53 + .../visualstudio/msbuildqbsproductproject.cpp | 355 ++ .../visualstudio/msbuildqbsproductproject.h | 69 + ...msbuildsharedsolutionpropertiesproject.cpp | 134 + .../msbuildsharedsolutionpropertiesproject.h | 53 + .../msbuildsolutionpropertiesproject.cpp | 62 + .../msbuildsolutionpropertiesproject.h | 55 + .../visualstudio/msbuildtargetproject.cpp | 138 + .../visualstudio/msbuildtargetproject.h | 73 + .../generators/visualstudio/msbuildutils.h | 115 + .../solution/ivisualstudiosolutionproject.cpp | 73 + .../solution/ivisualstudiosolutionproject.h | 66 + .../solution/visualstudiosolution.cpp | 119 + .../solution/visualstudiosolution.h | 84 + .../visualstudiosolutionfileproject.cpp | 78 + .../visualstudiosolutionfileproject.h | 61 + .../visualstudiosolutionfolderproject.cpp | 47 + .../visualstudiosolutionfolderproject.h | 50 + .../visualstudiosolutionglobalsection.cpp | 87 + .../visualstudiosolutionglobalsection.h | 64 + .../generators/visualstudio/visualstudio.pri | 87 + .../generators/visualstudio/visualstudio.qbs | 112 + .../visualstudio/visualstudiogenerator.cpp | 371 ++ .../visualstudio/visualstudiogenerator.h | 82 + .../visualstudio/visualstudioguidpool.cpp | 86 + .../visualstudio/visualstudioguidpool.h | 62 + src/lib/corelib/jsextensions/domxml.cpp | 380 ++ src/lib/corelib/jsextensions/domxml.h | 129 + .../jsextensions/environmentextension.cpp | 164 + .../jsextensions/environmentextension.h | 58 + src/lib/corelib/jsextensions/file.cpp | 298 ++ src/lib/corelib/jsextensions/file.h | 54 + .../jsextensions/fileinfoextension.cpp | 260 + .../corelib/jsextensions/fileinfoextension.h | 53 + src/lib/corelib/jsextensions/jsextensions.cpp | 98 + src/lib/corelib/jsextensions/jsextensions.h | 65 + src/lib/corelib/jsextensions/jsextensions.pri | 31 + .../corelib/jsextensions/moduleproperties.cpp | 161 + .../corelib/jsextensions/moduleproperties.h | 70 + src/lib/corelib/jsextensions/process.cpp | 282 + src/lib/corelib/jsextensions/process.h | 110 + src/lib/corelib/jsextensions/propertylist.h | 114 + src/lib/corelib/jsextensions/propertylist.mm | 340 ++ .../corelib/jsextensions/propertylistutils.h | 74 + .../corelib/jsextensions/propertylistutils.mm | 206 + src/lib/corelib/jsextensions/temporarydir.cpp | 95 + src/lib/corelib/jsextensions/temporarydir.h | 70 + src/lib/corelib/jsextensions/textfile.cpp | 205 + src/lib/corelib/jsextensions/textfile.h | 91 + .../jsextensions/utilitiesextension.cpp | 320 ++ .../corelib/jsextensions/utilitiesextension.h | 53 + .../corelib/language/artifactproperties.cpp | 75 + src/lib/corelib/language/artifactproperties.h | 80 + .../corelib/language/astimportshandler.cpp | 293 ++ src/lib/corelib/language/astimportshandler.h | 94 + .../language/astpropertiesitemhandler.cpp | 186 + .../language/astpropertiesitemhandler.h | 63 + src/lib/corelib/language/asttools.cpp | 74 + src/lib/corelib/language/asttools.h | 58 + .../corelib/language/builtindeclarations.cpp | 482 ++ .../corelib/language/builtindeclarations.h | 98 + src/lib/corelib/language/deprecationinfo.h | 71 + src/lib/corelib/language/evaluationdata.h | 65 + src/lib/corelib/language/evaluator.cpp | 265 + src/lib/corelib/language/evaluator.h | 117 + .../corelib/language/evaluatorscriptclass.cpp | 616 +++ .../corelib/language/evaluatorscriptclass.h | 112 + src/lib/corelib/language/filecontext.cpp | 64 + src/lib/corelib/language/filecontext.h | 74 + src/lib/corelib/language/filecontextbase.cpp | 61 + src/lib/corelib/language/filecontextbase.h | 78 + src/lib/corelib/language/filetags.cpp | 106 + src/lib/corelib/language/filetags.h | 91 + src/lib/corelib/language/forward_decls.h | 134 + .../corelib/language/functiondeclaration.h | 73 + src/lib/corelib/language/identifiersearch.cpp | 79 + src/lib/corelib/language/identifiersearch.h | 69 + src/lib/corelib/language/item.cpp | 332 ++ src/lib/corelib/language/item.h | 170 + src/lib/corelib/language/itemdeclaration.cpp | 64 + src/lib/corelib/language/itemdeclaration.h | 84 + src/lib/corelib/language/itemobserver.h | 58 + src/lib/corelib/language/itempool.cpp | 64 + src/lib/corelib/language/itempool.h | 71 + src/lib/corelib/language/itemreader.cpp | 104 + src/lib/corelib/language/itemreader.h | 99 + .../corelib/language/itemreaderastvisitor.cpp | 381 ++ .../corelib/language/itemreaderastvisitor.h | 97 + .../language/itemreadervisitorstate.cpp | 184 + .../corelib/language/itemreadervisitorstate.h | 78 + src/lib/corelib/language/itemtype.h | 84 + src/lib/corelib/language/jsimports.h | 77 + src/lib/corelib/language/language.cpp | 1292 +++++ src/lib/corelib/language/language.h | 534 ++ src/lib/corelib/language/language.pri | 87 + src/lib/corelib/language/loader.cpp | 142 + src/lib/corelib/language/loader.h | 76 + src/lib/corelib/language/moduleloader.cpp | 2456 +++++++++ src/lib/corelib/language/moduleloader.h | 314 ++ src/lib/corelib/language/modulemerger.cpp | 272 + src/lib/corelib/language/modulemerger.h | 87 + .../language/preparescriptobserver.cpp | 72 + .../corelib/language/preparescriptobserver.h | 67 + src/lib/corelib/language/projectresolver.cpp | 1297 +++++ src/lib/corelib/language/projectresolver.h | 149 + src/lib/corelib/language/property.cpp | 116 + src/lib/corelib/language/property.h | 97 + .../corelib/language/propertydeclaration.cpp | 230 + .../corelib/language/propertydeclaration.h | 124 + .../corelib/language/propertymapinternal.cpp | 115 + .../corelib/language/propertymapinternal.h | 74 + src/lib/corelib/language/qualifiedid.cpp | 83 + src/lib/corelib/language/qualifiedid.h | 77 + .../corelib/language/resolvedfilecontext.cpp | 90 + .../corelib/language/resolvedfilecontext.h | 79 + src/lib/corelib/language/scriptengine.cpp | 633 +++ src/lib/corelib/language/scriptengine.h | 233 + src/lib/corelib/language/scriptimporter.cpp | 170 + src/lib/corelib/language/scriptimporter.h | 65 + .../corelib/language/scriptpropertyobserver.h | 63 + src/lib/corelib/language/testdata/Banana | 1 + .../language/testdata/MyProperties.qbs | 4 + .../language/testdata/ParentWithExport.qbs | 6 + .../corelib/language/testdata/aboutdialog.cpp | 0 .../testdata/base-validate/base-validate.qbs | 5 + .../base-validate/modules/m/MParent.qbs | 6 + .../testdata/base-validate/modules/m/m.qbs | 11 + .../language/testdata/baseproperty.qbs | 7 + .../language/testdata/baseproperty_base.qbs | 4 + .../testdata/buildconfigstringlistsyntax.qbs | 3 + .../builtinFunctionInSearchPathsProperty.qbs | 9 + .../testdata/canonicalArchitecture.qbs | 6 + .../language/testdata/conditionaldepends.qbs | 67 + .../testdata/conditionaldepends_base.qbs | 10 + .../language/testdata/defaultvalue/egon.qbs | 14 + .../defaultvalue/modules/higher/higher.qbs | 7 + .../defaultvalue/modules/lower/lower.qbs | 7 + .../language/testdata/defaultvalue/test.txt | 0 .../testdata/dependencyOnAllProfiles.qbs | 19 + .../derived-sub-project/DerivedSubProject.qbs | 4 + .../testdata/derived-sub-project/project.qbs | 10 + .../derived-sub-project/subproject.qbs | 4 + .../corelib/language/testdata/drawline.asm | 0 src/lib/corelib/language/testdata/dummy.txt | 0 .../language/testdata/environmentvariable.qbs | 5 + .../testdata/erroneous/ParentItem.qbs | 5 + .../testdata/erroneous/ParentWithExport.qbs | 7 + ...conflicting-properties-in-export-items.qbs | 7 + .../erroneous/conflicting_fileTagsFilter.qbs | 13 + .../testdata/erroneous/dependency_cycle.qbs | 19 + .../testdata/erroneous/dependency_cycle2.qbs | 23 + .../testdata/erroneous/dependency_cycle3.qbs | 13 + .../testdata/erroneous/dependency_cycle4.qbs | 5 + .../testdata/erroneous/duplicate_sources.qbs | 8 + .../erroneous/duplicate_sources_wildcards.qbs | 8 + .../testdata/erroneous/importloop1.qbs | 5 + .../testdata/erroneous/importloop2.qbs | 5 + .../erroneous/invalid-property-option.qbs | 5 + .../erroneous/invalid_child_item_type.qbs | 6 + .../testdata/erroneous/invalid_file.qbs | 5 + .../erroneous/invalid_property_type.qbs | 5 + .../erroneous/invalid_stringlist_element.qbs | 3 + .../language/testdata/erroneous/main.cpp | 40 + .../erroneous/misused-inherited-property.qbs | 5 + .../erroneous/modules/module-a/module-a.qbs | 5 + .../erroneous/modules/module-b/module-b.qbs | 5 + .../module-with-wrong-property-option/m.qbs | 9 + .../erroneous/modules/prefix1/prefix1.qbs | 4 + .../modules/prefix1/suffix/suffix.qbs | 5 + .../erroneous/modules/prefix2/prefix2.qbs | 5 + .../modules/prefix2/suffix/suffix.qbs | 4 + .../testdata/erroneous/multiple_exports.qbs | 4 + .../multiple_properties_in_subproject.qbs | 8 + .../testdata/erroneous/nonexistentouter.qbs | 7 + .../testdata/erroneous/oldQbsVersion.qbs | 8 + ...properties-item-with-invalid-condition.qbs | 9 + .../testdata/erroneous/references_cycle.qbs | 6 + .../testdata/erroneous/references_cycle2.qbs | 6 + .../testdata/erroneous/references_cycle3.qbs | 6 + .../erroneous/reserved_name_in_import.qbs | 4 + .../erroneous/same-module-prefix1.qbs | 5 + .../erroneous/same-module-prefix2.qbs | 5 + .../testdata/erroneous/subproject_cycle.qbs | 8 + .../testdata/erroneous/subproject_cycle2.qbs | 8 + .../testdata/erroneous/subproject_cycle3.qbs | 8 + .../erroneous/throw_in_property_binding.qbs | 7 + .../testdata/erroneous/undeclared_item.qbs | 6 + .../erroneous/undeclared_property.qbs | 6 + ...undeclared_property_in_Properties_item.qbs | 8 + .../undeclared_property_in_export_item.qbs | 14 + .../undeclared_property_in_export_item2.qbs | 13 + .../undeclared_property_in_export_item3.qbs | 9 + .../erroneous/undeclared_property_wrapper.qbs | 5 + .../undefined_stringlist_element.qbs | 4 + .../testdata/erroneous/unknown_item_type.qbs | 3 + .../testdata/erroneous/unknown_module.qbs | 3 + .../erroneous/wrongQbsVersionFormat.qbs | 5 + src/lib/corelib/language/testdata/exports.qbs | 105 + .../language/testdata/exports_product.qbs | 8 + .../testdata/filecontextproperties.qbs | 5 + .../corelib/language/testdata/filetags.qbs | 61 + .../language/testdata/getNativeSetting.qbs | 24 + .../language/testdata/groupconditions.qbs | 53 + .../corelib/language/testdata/groupname.qbs | 20 + .../language/testdata/homeDirectory.qbs | 18 + .../language/testdata/id-uniqueness.qbs | 12 + src/lib/corelib/language/testdata/idusage.qbs | 20 + .../corelib/language/testdata/idusagebase.qbs | 5 + .../import-collection/collection/file1.js | 1 + .../import-collection/collection/file2.js | 1 + .../imports/Collection/file1.js | 1 + .../imports/Collection/file2.js | 1 + .../testdata/import-collection/product.qbs | 7 + .../testdata/import-collection/project.qbs | 6 + .../testdata/invalidBindingInDisabledItem.qbs | 16 + .../corelib/language/testdata/jsextensions.js | 65 + .../testdata/jsimportsinmultiplescopes.js | 12 + .../testdata/jsimportsinmultiplescopes.qbs | 7 + src/lib/corelib/language/testdata/main.cpp | 0 .../language/testdata/moduleproperties.qbs | 50 + .../testdata/modulepropertiesingroups.qbs | 73 + src/lib/corelib/language/testdata/modules.qbs | 57 + .../deepdummy/deep/moat/dummydeepmoat.qbs | 4 + .../language/testdata/modules/dummy/dummy.qbs | 19 + .../testdata/modules/dummy/dummy_base.qbs | 4 + .../testdata/modules/dummy2/dummy2.qbs | 7 + .../modules/dummyqt/core/dummycore.qbs | 17 + .../testdata/modules/dummyqt/gui/dummygui.qbs | 12 + .../modules/dummyqt/network/dummynetwork.qbs | 9 + .../testdata/modules/gmod/gmod1/gmod1.qbs | 18 + .../language/testdata/modules/gmod2/gmod2.qbs | 8 + .../language/testdata/modules/gmod3/qmod3.qbs | 7 + .../language/testdata/modules/gmod4/gmod4.qbs | 8 + .../testdata/modules/scopemod/scopemod.qbs | 12 + .../corelib/language/testdata/modulescope.qbs | 14 + .../language/testdata/modulescope_base.qbs | 6 + src/lib/corelib/language/testdata/narf | 0 src/lib/corelib/language/testdata/narf.zort | 0 .../language/testdata/nativesettings.ini | 1 + .../testdata/non-required-products.qbs | 35 + .../language/testdata/outerInGroup.qbs | 14 + .../language/testdata/pathproperties.qbs | 9 + .../language/testdata/productconditions.qbs | 36 + .../language/testdata/productdirectories.qbs | 5 + .../profilevaluesandoverriddenvalues.qbs | 21 + .../testdata/properties-block-in-group.qbs | 16 + .../language/testdata/propertiesblocks.qbs | 181 + .../testdata/propertiesblocks_base.qbs | 11 + .../qbs-properties-in-project-condition.qbs | 9 + .../recursive-dependencies.qbs | 19 + .../testdata/relaxed-error-mode/file1.txt | 0 .../testdata/relaxed-error-mode/file2.txt | 0 .../relaxed-error-mode/relaxed-error-mode.qbs | 31 + .../complicated.qbs | 7 + .../dependency-via-export.qbs | 15 + .../dependency-via-module.qbs | 6 + .../direct-dependencies.qbs | 6 + .../failing-validation-indirect.qbs | 5 + .../failing-validation/failing-validation.qbs | 5 + .../required-chain-export-indirect.qbs | 22 + .../required-chain-export.qbs | 15 + .../required-chain-module.qbs | 6 + .../language/testdata/rfc1034identifier.qbs | 7 + .../testdata/subdir/exports-mylib.qbs | 14 + .../testdata/subdir/pathproperties_base.qbs | 4 + .../testdata/subdir2/exports-mylib2.qbs | 13 + .../language/testdata/throwing-probe.qbs | 12 + .../language/testdata/versionCompare.qbs | 21 + src/lib/corelib/language/testdata/zort | 0 src/lib/corelib/language/tst_language.cpp | 2256 ++++++++ src/lib/corelib/language/tst_language.h | 145 + src/lib/corelib/language/value.cpp | 181 + src/lib/corelib/language/value.h | 201 + src/lib/corelib/logging/ilogsink.cpp | 128 + src/lib/corelib/logging/ilogsink.h | 91 + src/lib/corelib/logging/logger.cpp | 229 + src/lib/corelib/logging/logger.h | 140 + src/lib/corelib/logging/logging.pri | 16 + src/lib/corelib/logging/translator.h | 57 + src/lib/corelib/parser/parser.pri | 21 + src/lib/corelib/parser/qmlerror.cpp | 292 ++ src/lib/corelib/parser/qmlerror.h | 86 + src/lib/corelib/parser/qmljs.g | 3011 +++++++++++ src/lib/corelib/parser/qmljsast.cpp | 925 ++++ src/lib/corelib/parser/qmljsast_p.h | 2633 ++++++++++ src/lib/corelib/parser/qmljsastfwd_p.h | 182 + src/lib/corelib/parser/qmljsastvisitor.cpp | 54 + src/lib/corelib/parser/qmljsastvisitor_p.h | 325 ++ src/lib/corelib/parser/qmljsengine_p.cpp | 161 + src/lib/corelib/parser/qmljsengine_p.h | 125 + src/lib/corelib/parser/qmljsglobal_p.h | 62 + src/lib/corelib/parser/qmljsgrammar.cpp | 1011 ++++ src/lib/corelib/parser/qmljsgrammar_p.h | 211 + src/lib/corelib/parser/qmljskeywords_p.h | 862 +++ src/lib/corelib/parser/qmljslexer.cpp | 1151 ++++ src/lib/corelib/parser/qmljslexer_p.h | 243 + src/lib/corelib/parser/qmljsmemorypool_p.h | 167 + src/lib/corelib/parser/qmljsparser.cpp | 1820 +++++++ src/lib/corelib/parser/qmljsparser_p.h | 240 + src/lib/corelib/qbs.h | 62 + src/lib/corelib/tools/applecodesignutils.cpp | 152 + src/lib/corelib/tools/applecodesignutils.h | 56 + src/lib/corelib/tools/architectures.cpp | 100 + src/lib/corelib/tools/architectures.h | 50 + src/lib/corelib/tools/buildgraphlocker.cpp | 165 + src/lib/corelib/tools/buildgraphlocker.h | 73 + src/lib/corelib/tools/buildoptions.cpp | 375 ++ src/lib/corelib/tools/buildoptions.h | 109 + src/lib/corelib/tools/cleanoptions.cpp | 150 + src/lib/corelib/tools/cleanoptions.h | 73 + src/lib/corelib/tools/codelocation.cpp | 165 + src/lib/corelib/tools/codelocation.h | 91 + src/lib/corelib/tools/commandechomode.cpp | 98 + src/lib/corelib/tools/commandechomode.h | 66 + src/lib/corelib/tools/error.cpp | 287 + src/lib/corelib/tools/error.h | 119 + src/lib/corelib/tools/executablefinder.cpp | 155 + src/lib/corelib/tools/executablefinder.h | 80 + src/lib/corelib/tools/fileinfo.cpp | 525 ++ src/lib/corelib/tools/fileinfo.h | 109 + src/lib/corelib/tools/filesaver.cpp | 92 + src/lib/corelib/tools/filesaver.h | 74 + src/lib/corelib/tools/filetime.h | 139 + src/lib/corelib/tools/filetime_unix.cpp | 88 + src/lib/corelib/tools/filetime_win.cpp | 116 + src/lib/corelib/tools/generateoptions.cpp | 102 + src/lib/corelib/tools/generateoptions.h | 71 + src/lib/corelib/tools/hostosinfo.h | 188 + src/lib/corelib/tools/id.cpp | 294 ++ src/lib/corelib/tools/id.h | 94 + src/lib/corelib/tools/installoptions.cpp | 231 + src/lib/corelib/tools/installoptions.h | 88 + src/lib/corelib/tools/jsliterals.cpp | 102 + src/lib/corelib/tools/jsliterals.h | 58 + src/lib/corelib/tools/msvcinfo.cpp | 231 + src/lib/corelib/tools/msvcinfo.h | 111 + src/lib/corelib/tools/pathutils.h | 65 + src/lib/corelib/tools/persistence.cpp | 328 ++ src/lib/corelib/tools/persistence.h | 214 + src/lib/corelib/tools/persistentobject.h | 58 + src/lib/corelib/tools/preferences.cpp | 143 + src/lib/corelib/tools/preferences.h | 76 + src/lib/corelib/tools/processresult.cpp | 132 + src/lib/corelib/tools/processresult.h | 82 + src/lib/corelib/tools/processresult_p.h | 66 + src/lib/corelib/tools/processutils.cpp | 132 + src/lib/corelib/tools/processutils.h | 55 + src/lib/corelib/tools/profile.cpp | 246 + src/lib/corelib/tools/profile.h | 104 + src/lib/corelib/tools/profiling.cpp | 126 + src/lib/corelib/tools/profiling.h | 82 + src/lib/corelib/tools/progressobserver.cpp | 105 + src/lib/corelib/tools/progressobserver.h | 72 + .../corelib/tools/projectgeneratormanager.cpp | 92 + .../corelib/tools/projectgeneratormanager.h | 80 + src/lib/corelib/tools/propertyfinder.cpp | 77 + src/lib/corelib/tools/propertyfinder.h | 66 + src/lib/corelib/tools/qbs_export.h | 54 + src/lib/corelib/tools/qbsassert.cpp | 60 + src/lib/corelib/tools/qbsassert.h | 69 + src/lib/corelib/tools/qttools.cpp | 50 + src/lib/corelib/tools/qttools.h | 50 + .../corelib/tools/scannerpluginmanager.cpp | 125 + src/lib/corelib/tools/scannerpluginmanager.h | 76 + src/lib/corelib/tools/scripttools.cpp | 104 + src/lib/corelib/tools/scripttools.h | 102 + src/lib/corelib/tools/settings.cpp | 156 + src/lib/corelib/tools/settings.h | 88 + src/lib/corelib/tools/settingscreator.cpp | 152 + src/lib/corelib/tools/settingscreator.h | 80 + src/lib/corelib/tools/settingsmodel.cpp | 415 ++ src/lib/corelib/tools/settingsmodel.h | 89 + .../corelib/tools/setupprojectparameters.cpp | 633 +++ .../corelib/tools/setupprojectparameters.h | 141 + src/lib/corelib/tools/shellutils.cpp | 225 + src/lib/corelib/tools/shellutils.h | 87 + src/lib/corelib/tools/toolchains.cpp | 104 + src/lib/corelib/tools/toolchains.h | 52 + src/lib/corelib/tools/tools.pri | 130 + src/lib/corelib/tools/tst_tools.cpp | 288 + src/lib/corelib/tools/tst_tools.h | 80 + src/lib/corelib/tools/version.cpp | 161 + src/lib/corelib/tools/version.h | 107 + .../corelib/tools/visualstudioversioninfo.cpp | 183 + .../corelib/tools/visualstudioversioninfo.h | 84 + .../corelib/tools/vsenvironmentdetector.cpp | 247 + src/lib/corelib/tools/vsenvironmentdetector.h | 79 + src/lib/corelib/tools/weakpointer.h | 70 + src/lib/corelib/use_corelib.pri | 47 + src/lib/corelib/use_installed_corelib.pri | 37 + src/lib/library.pri | 46 + src/lib/libs.qbs | 9 + src/lib/qtprofilesetup/qtenvironment.h | 85 + src/lib/qtprofilesetup/qtmoduleinfo.cpp | 672 +++ src/lib/qtprofilesetup/qtmoduleinfo.h | 112 + src/lib/qtprofilesetup/qtprofilesetup.cpp | 437 ++ src/lib/qtprofilesetup/qtprofilesetup.h | 57 + src/lib/qtprofilesetup/qtprofilesetup.pro | 24 + src/lib/qtprofilesetup/qtprofilesetup.qbs | 29 + src/lib/qtprofilesetup/templates.qrc | 16 + src/lib/qtprofilesetup/templates/QtModule.qbs | 56 + src/lib/qtprofilesetup/templates/QtPlugin.qbs | 32 + src/lib/qtprofilesetup/templates/core.qbs | 420 ++ src/lib/qtprofilesetup/templates/dbus.js | 61 + src/lib/qtprofilesetup/templates/dbus.qbs | 71 + src/lib/qtprofilesetup/templates/gui.qbs | 63 + src/lib/qtprofilesetup/templates/moc.js | 70 + src/lib/qtprofilesetup/templates/module.qbs | 27 + src/lib/qtprofilesetup/templates/phonon.qbs | 9 + src/lib/qtprofilesetup/templates/plugin.qbs | 25 + src/lib/qtprofilesetup/templates/qdoc.js | 84 + src/lib/qtprofilesetup/templates/scxml.qbs | 68 + .../use_installed_qtprofilesetup.pri | 20 + src/lib/qtprofilesetup/use_qtprofilesetup.pri | 48 + src/libexec/libexec.pri | 8 + src/libexec/libexec.pro | 1 + src/libexec/libexec.qbs | 6 + src/library_dirname.pri | 1 + src/plugins/plugins.pri | 22 + src/plugins/plugins.pro | 2 + src/plugins/plugins.qbs | 9 + .../cpp/CPlusPlusForwardDeclarations.h | 153 + src/plugins/scanner/cpp/Lexer.cpp | 671 +++ src/plugins/scanner/cpp/Lexer.h | 164 + src/plugins/scanner/cpp/Token.cpp | 157 + src/plugins/scanner/cpp/Token.h | 374 ++ src/plugins/scanner/cpp/cpp.pro | 11 + src/plugins/scanner/cpp/cpp.qbs | 18 + src/plugins/scanner/cpp/cpp_global.h | 49 + src/plugins/scanner/cpp/cppscanner.cpp | 416 ++ src/plugins/scanner/qt/qt.pro | 9 + src/plugins/scanner/qt/qt.qbs | 11 + src/plugins/scanner/qt/qtscanner.cpp | 193 + src/plugins/scanner/scanner.h | 107 + src/plugins/scanner/scanner.pro | 3 + src/plugins/scanner/scannerplugin.qbs | 15 + src/src.qbs | 10 + static.pro | 41 + sync.profile | 4 + tests/auto/api/api.pro | 25 + tests/auto/api/api.qbs | 19 + tests/auto/api/testdata/QBS-728/project.qbs | 7 + .../add-qobject-macro-to-cpp-file/main.cpp | 35 + .../add-qobject-macro-to-cpp-file/object.cpp | 41 + .../add-qobject-macro-to-cpp-file/object.h | 32 + .../add-qobject-macro-to-cpp-file/project.qbs | 6 + .../testdata/added-file-persistent/file.cpp | 29 + .../testdata/added-file-persistent/main.cpp | 31 + .../added-file-persistent/project.qbs | 8 + .../auto/api/testdata/app-without-sources/a.c | 29 + .../auto/api/testdata/app-without-sources/b.c | 37 + .../testdata/app-without-sources/project.qbs | 39 + .../testdata/base-properties/imports/Bar.qbs | 6 + .../testdata/base-properties/imports/Foo.qbs | 8 + .../api/testdata/base-properties/main.cpp | 42 + .../auto/api/testdata/base-properties/prj.qbs | 10 + .../testdata/build-properties-source/main.cpp | 38 + .../build-properties-source/project.qbs | 17 + .../testdata/build-single-file/compiled.cpp | 29 + .../testdata/build-single-file/ignored1.cpp | 0 .../testdata/build-single-file/ignored2.cpp | 0 .../testdata/build-single-file/project.qbs | 10 + .../testdata/buildgraph-locking/project.qbs | 4 + .../change-dependent-lib.qbs | 24 + .../testdata/change-dependent-lib/main.cpp | 46 + .../testdata/change-dependent-lib/mylib.cpp | 43 + tests/auto/api/testdata/check-outputs/foo.txt | 1 + .../api/testdata/check-outputs/project.qbs | 37 + tests/auto/api/testdata/codegen/foo.txt | 1 + tests/auto/api/testdata/codegen/project.qbs | 73 + .../api/testdata/command-extraction/main.cpp | 29 + .../testdata/command-extraction/project.qbs | 5 + .../disabled-product/disabledProduct.qbs | 10 + .../api/testdata/disabled-product/main.cpp | 1 + .../disabled-project/disabled_project.qbs | 7 + .../testdata/disabled_install_group/main.cpp | 29 + .../disabled_install_group/project.qbs | 11 + .../duplicate-product-names/explicit.qbs | 7 + .../implicit-indirect.qbs | 5 + .../duplicate-product-names/implicit.qbs | 7 + .../subdir1/subproject.qbs | 3 + .../subdir2/subproject.qbs | 3 + tests/auto/api/testdata/dynamic-libs/lib1.cpp | 46 + tests/auto/api/testdata/dynamic-libs/lib2.cpp | 46 + tests/auto/api/testdata/dynamic-libs/lib3.cpp | 49 + tests/auto/api/testdata/dynamic-libs/lib4.cpp | 43 + tests/auto/api/testdata/dynamic-libs/lib4.h | 54 + .../testdata/dynamic-libs/link_dynamiclib.qbs | 66 + tests/auto/api/testdata/dynamic-libs/main.cpp | 50 + .../empty-filetag-list/dontcompilethis.cpp | 1 + .../testdata/empty-filetag-list/project.qbs | 8 + .../empty-submodules-list/project.qbs | 8 + .../enable-and-disable-product/main.cpp | 29 + .../enable-and-disable-product/project.qbs | 8 + .../error-in-setup-run-environment.qbs | 5 + .../modules/mymodule/mymodule.qbs | 7 + .../explicitly-depends-on/dependency.txt | 0 .../explicitly-depends-on/project.qbs | 30 + .../testdata/export-item-with-group/main.cpp | 29 + .../export-item-with-group/project.qbs | 17 + .../auto/api/testdata/export-simple/lib1.cpp | 43 + .../auto/api/testdata/export-simple/main.cpp | 45 + .../api/testdata/export-simple/project.qbs | 50 + .../export-with-recursive-depends/main1.cpp | 29 + .../export-with-recursive-depends/main2.cpp | 34 + .../modules/module1/module1.qbs | 5 + .../modules/module2/module2.qbs | 6 + .../export-with-recursive-depends/project.qbs | 15 + tests/auto/api/testdata/file-tagger/bla.txt | 15 + .../auto/api/testdata/file-tagger/moc_cpp.qbs | 43 + .../filetagsfilter_override/InstalledApp.qbs | 11 + .../testdata/filetagsfilter_override/main.cpp | 29 + .../filetagsfilter_override/project.qbs | 11 + .../generated-files-list.qbs | 15 + .../testdata/generated-files-list/main.cpp | 39 + .../generated-files-list/mainwindow.cpp | 42 + .../generated-files-list/mainwindow.h | 50 + .../generated-files-list/mainwindow.ui | 24 + .../infinite-loop-js/infinite-loop.qbs | 21 + .../infinite-loop-process/infinite-loop.qbs | 28 + .../testdata/infinite-loop-process/main.cpp | 40 + .../infinite-loop-resolving/project.qbs | 5 + .../inherit-qbs-search-paths/imports/Foo.qbs | 8 + .../inherit-qbs-search-paths/main.cpp | 37 + .../testdata/inherit-qbs-search-paths/prj.qbs | 13 + .../subdir/modules/bli/m.qbs | 7 + .../subdir2/modules/bla/m.qbs | 7 + .../installed-artifact/installed_artifact.qbs | 27 + .../api/testdata/installed-artifact/main.cpp | 29 + .../auto/api/testdata/is-runnable/project.qbs | 11 + .../api/testdata/lib-same-source/main.cpp | 34 + .../api/testdata/lib-same-source/project.qbs | 23 + .../link-static-lib/helper1/helper1.cpp | 35 + .../link-static-lib/helper1/helper1.h | 34 + .../link-static-lib/helper2/helper2.cpp | 34 + .../link-static-lib/helper2/helper2.h | 34 + .../api/testdata/link-static-lib/main.cpp | 37 + .../testdata/link-static-lib/mystaticlib.cpp | 37 + .../link-static-lib/mystaticlibhelper.cpp | 33 + .../api/testdata/link-static-lib/project.qbs | 47 + .../api/testdata/lots-of-dots/dotty.matrix.ui | 21 + .../api/testdata/lots-of-dots/m.a.i.n.cpp | 38 + .../api/testdata/lots-of-dots/object.narf.cpp | 35 + .../api/testdata/lots-of-dots/object.narf.h | 41 + .../api/testdata/lots-of-dots/polka.dots.qrc | 5 + .../api/testdata/lots-of-dots/project.qbs | 18 + .../testdata/missing-qobject-header/main.cpp | 34 + .../missing-qobject-header/myobject.cpp | 33 + .../missing-qobject-header/myobject.h | 38 + tests/auto/api/testdata/moc-cpp/bla.cpp | 43 + tests/auto/api/testdata/moc-cpp/project.qbs | 15 + .../api/testdata/moc-hpp-included/object.cpp | 43 + .../api/testdata/moc-hpp-included/object.h | 41 + .../api/testdata/moc-hpp-included/object2.h | 41 + .../api/testdata/moc-hpp-included/object2.mm | 16 + .../api/testdata/moc-hpp-included/project.qbs | 19 + tests/auto/api/testdata/moc-hpp/object.cpp | 41 + tests/auto/api/testdata/moc-hpp/object.h | 41 + tests/auto/api/testdata/moc-hpp/project.qbs | 17 + .../api/testdata/multi-arch/host+target.input | 0 .../api/testdata/multi-arch/host-tool.input | 0 .../auto/api/testdata/multi-arch/project.qbs | 54 + .../new-output-artifact-in-dependency/lib.cpp | 31 + .../main.cpp | 34 + .../project.qbs | 17 + .../testdata/new-pattern-match/project.qbs | 5 + .../invalidaccessfromproduct.qbs | 3 + .../nonexistingprojectproperties/project.qbs | 3 + tests/auto/api/testdata/objc/main.mm | 14 + tests/auto/api/testdata/objc/objc.qbs | 9 + .../precompiled-header-dynamic/autogen.h.in | 1 + .../precompiled-header-dynamic/main.cpp | 32 + .../testdata/precompiled-header-dynamic/pch.h | 29 + .../precompiled-header-dynamic/project.qbs | 31 + .../testdata/precompiled-header-new/main.cpp | 55 + .../precompiled-header-new/myobject.cpp | 41 + .../precompiled-header-new/myobject.h | 43 + .../precompiled-header-new/project.qbs | 13 + .../testdata/precompiled-header-new/stable.h | 37 + .../api/testdata/precompiled-header/main.cpp | 55 + .../testdata/precompiled-header/myobject.cpp | 41 + .../testdata/precompiled-header/myobject.h | 43 + .../testdata/precompiled-header/project.qbs | 15 + .../api/testdata/precompiled-header/stable.h | 37 + .../auto/api/testdata/process-result/main.cpp | 37 + .../process-result/process-result.qbs | 33 + .../api/testdata/productNameWithDots/app.cpp | 29 + .../api/testdata/productNameWithDots/lib.cpp | 29 + .../testdata/productNameWithDots/project.qbs | 17 + .../file.cpp | 29 + .../main.cpp | 29 + ...roject-data-after-product-invalidation.qbs | 9 + .../project-editing/existingfile1.txt | 0 .../project-editing/existingfile2.txt | 0 .../project-editing/existingfile3.txt | 0 .../api/testdata/project-editing/file.cpp | 27 + .../auto/api/testdata/project-editing/file.h | 27 + .../api/testdata/project-editing/main.cpp | 27 + .../api/testdata/project-editing/newfile1.txt | 0 .../api/testdata/project-editing/newfile2.txt | 0 .../api/testdata/project-editing/newfile3.txt | 0 .../api/testdata/project-editing/newfile4.txt | 0 .../project-editing/project-with-no-files.qbs | 7 + .../api/testdata/project-editing/project.qbs | 39 + .../testdata/project-editing/subdir/file.txt | 0 .../testdata/project-editing/test.wildcard | 0 .../project.early-error.qbs | 6 + .../project.late-error.qbs | 14 + .../project-invalidation/project.no-error.qbs | 5 + .../api/testdata/project-locking/project.qbs | 4 + .../project-properties-by-name/main1.cpp | 38 + .../project-properties-by-name/main2.cpp | 38 + .../project-properties-by-name/project.qbs | 22 + .../project-with-properties-item/project.qbs | 12 + tests/auto/api/testdata/projectd | 0 .../api/testdata/properties-blocks/main.cpp | 46 + .../properties-blocks/propertiesblocks.qbs | 29 + .../api/testdata/qt5-plugin/echointerface.h | 51 + .../api/testdata/qt5-plugin/echoplugin.cpp | 34 + .../auto/api/testdata/qt5-plugin/echoplugin.h | 45 + .../qt5-plugin/echoplugin.json.source | 1 + .../testdata/qt5-plugin/echoplugin_dummy.cpp | 29 + .../auto/api/testdata/qt5-plugin/project.qbs | 48 + tests/auto/api/testdata/rc/main.cpp | 32 + tests/auto/api/testdata/rc/rc.qbs | 15 + tests/auto/api/testdata/rc/test.rc | 22 + .../recursive-wildcards/dir/file1.txt | 0 .../recursive-wildcards/dir/subdir/file2.txt | 0 .../recursive_wildcards.qbs | 7 + .../ambiguousdir/p1.qbs | 0 .../ambiguousdir/p2.qbs | 0 .../testdata/referenced-file-errors/cycle.qbs | 7 + .../emptydir/.gitignore | 0 .../modules/brokenmodule/brokenmodule.qbs | 5 + .../testdata/referenced-file-errors/okay.qbs | 3 + .../testdata/referenced-file-errors/okay2.qbs | 3 + .../referenced-file-errors.qbs | 32 + .../referenced-file-errors/wrongtype.qbs | 3 + .../auto/api/testdata/references/invalid1.qbs | 5 + .../auto/api/testdata/references/invalid2.qbs | 5 + .../subproject1.qbs | 0 .../subproject2.qbs | 0 .../subproject3.qbs | 0 .../subdir-with-no-project/test.txt | 0 .../references/subdir-with-one-project/p.qbs | 3 + .../subdir-with-one-project/test.txt | 0 tests/auto/api/testdata/references/valid.qbs | 5 + .../relaxed-mode-recovery.qbs | 10 + .../testdata/remove-file-dependency/main.cpp | 37 + .../removeFileDependency.qbs | 7 + .../remove-file-dependency/someheader.h | 29 + .../auto/api/testdata/rename-product/lib.cpp | 31 + .../auto/api/testdata/rename-product/main.cpp | 34 + .../api/testdata/rename-product/rename.qbs | 18 + .../testdata/rename-target-artifact/lib.cpp | 31 + .../testdata/rename-target-artifact/main.cpp | 34 + .../rename-target-artifact/rename.qbs | 19 + .../api/testdata/restored-warnings/file.cpp | 1 + .../api/testdata/restored-warnings/main.cpp | 1 + .../restored-warnings/restored-warnings.qbs | 15 + .../auto/api/testdata/rule-conflict/main.cpp | 29 + tests/auto/api/testdata/rule-conflict/pch1.h | 27 + tests/auto/api/testdata/rule-conflict/pch2.h | 27 + .../testdata/rule-conflict/rule-conflict.qbs | 11 + tests/auto/api/testdata/same-base-name/lib.c | 34 + .../auto/api/testdata/same-base-name/lib.cpp | 34 + tests/auto/api/testdata/same-base-name/lib.m | 6 + tests/auto/api/testdata/same-base-name/lib.mm | 8 + tests/auto/api/testdata/same-base-name/main.c | 46 + .../api/testdata/same-base-name/project.qbs | 33 + tests/auto/api/testdata/simple-probe/main.cpp | 29 + .../api/testdata/simple-probe/project.qbs | 32 + .../api/testdata/soft-dependency/main.cpp | 32 + .../api/testdata/soft-dependency/project.qbs | 14 + .../source-file-in-build-dir/file.cpp | 27 + .../source-file-in-build-dir/project.qbs | 8 + .../qt-debug/moc_blubb.cpp | 0 .../qt-debug/qt-debug.bg | 0 .../auto/api/testdata/static-lib-deps/a1.cpp | 35 + .../auto/api/testdata/static-lib-deps/a2.cpp | 35 + tests/auto/api/testdata/static-lib-deps/b.cpp | 35 + tests/auto/api/testdata/static-lib-deps/c.cpp | 35 + tests/auto/api/testdata/static-lib-deps/d.cpp | 62 + tests/auto/api/testdata/static-lib-deps/e.cpp | 35 + .../api/testdata/static-lib-deps/main.cpp | 35 + .../api/testdata/static-lib-deps/project.qbs | 104 + .../resources/imports/LibraryType/type.js | 1 + .../modules/QtCoreDepender/qtcoredepender.qbs | 5 + .../testdata/subprojects/subproject1/main.cpp | 34 + .../subprojects/subproject2/subproject2.qbs | 15 + .../subproject2/subproject3/subproject3.qbs | 16 + .../subproject2/subproject3/testlib.cpp | 31 + .../testdata/subprojects/toplevelproject.qbs | 19 + tests/auto/api/testdata/transformers/main.cpp | 70 + .../testdata/transformers/transformers.qbs | 93 + .../modules/mymodule/mymodule.qbs | 24 + .../modules/myothermodule/myothermodule.qbs | 5 + .../two-default-property-values/project.qbs | 13 + .../two-default-property-values/test.txt | 0 tests/auto/api/testdata/type-change/main.cpp | 29 + .../auto/api/testdata/type-change/project.qbs | 7 + tests/auto/api/testdata/uic/bla.cpp | 36 + tests/auto/api/testdata/uic/bla.h | 29 + tests/auto/api/testdata/uic/ui.h | 29 + tests/auto/api/testdata/uic/ui.ui | 31 + tests/auto/api/testdata/uic/uic.qbs | 16 + tests/auto/api/tst_api.cpp | 2189 ++++++++ tests/auto/api/tst_api.h | 151 + tests/auto/auto.pri | 12 + tests/auto/auto.pro | 15 + tests/auto/auto.qbs | 13 + tests/auto/blackbox/blackbox-clangdb.pro | 16 + tests/auto/blackbox/blackbox-java.pro | 16 + tests/auto/blackbox/blackbox.pro | 16 + tests/auto/blackbox/blackbox.qbs | 68 + tests/auto/blackbox/find/find-android.qbs | 41 + tests/auto/blackbox/find/find-jdk.qbs | 36 + tests/auto/blackbox/find/find-nodejs.qbs | 34 + tests/auto/blackbox/find/find-typescript.qbs | 35 + .../project1/i like spaces.cpp | 42 + .../testdata-clangdb/project1/project.qbs | 24 + .../multiple-apks-per-project.qbs | 8 + .../product1/product1.qbs | 31 + .../product1/src/main/AndroidManifest.xml | 16 + .../src/main/java/io/qt/dummy/Dummy.java | 11 + .../product1/src/main/jni/lib1.cpp | 29 + .../product1/src/main/jni/lib2.cpp | 29 + .../product1/src/main/res/values/strings.xml | 3 + .../product2/product2.qbs | 27 + .../product2/src/main/AndroidManifest.xml | 16 + .../src/main/java/io/qt/dummy/Dummy.java | 11 + .../product2/src/main/jni/lib1.cpp | 29 + .../product2/src/main/jni/lib2.cpp | 29 + .../multiple-libs-per-apk.qbs | 27 + .../src/main/AndroidManifest.xml | 16 + .../src/main/java/io/qt/dummy/Dummy.java | 11 + .../src/main/jni/lib1.cpp | 29 + .../src/main/jni/lib2.cpp | 29 + .../src/main/res/values/strings.xml | 3 + .../android/no-native/no-native.qbs | 8 + .../testdata-java/android/teapot/teapot.qbs | 93 + .../auto/blackbox/testdata-java/java/Car.java | 23 + .../blackbox/testdata-java/java/Car8.java | 28 + .../testdata-java/java/HelloWorld.java | 15 + .../testdata-java/java/HelloWorld8.java | 8 + .../auto/blackbox/testdata-java/java/Jet.java | 7 + .../testdata-java/java/NoPackage.java | 5 + .../testdata-java/java/RandomStuff.java | 6 + .../blackbox/testdata-java/java/Ship.java | 12 + .../blackbox/testdata-java/java/Vehicle.java | 4 + .../blackbox/testdata-java/java/Vehicles.java | 37 + .../auto/blackbox/testdata-java/java/engine.c | 38 + .../java/inner-class/InnerClass.java | 6 + .../java/inner-class/inner-class.qbs | 5 + .../blackbox/testdata-java/java/vehicles.qbs | 98 + .../QTBUG-51237/modules/mymodule/mymodule.qbs | 6 + .../testdata/QTBUG-51237/qtbug-51237.qbs | 22 + .../blackbox/testdata/always-run/dummy.txt | 0 .../blackbox/testdata/always-run/rule.qbs | 29 + .../testdata/always-run/transformer.qbs | 23 + .../blackbox/testdata/archiver/archivable.qbs | 13 + .../auto/blackbox/testdata/archiver/list.txt | 2 + .../auto/blackbox/testdata/archiver/test.txt | 0 .../blackbox/testdata/assembly/assembly.qbs | 33 + tests/auto/blackbox/testdata/assembly/testa.s | 3 + tests/auto/blackbox/testdata/assembly/testb.S | 3 + .../auto/blackbox/testdata/assembly/testc.sx | 3 + .../blackbox/testdata/assembly/testd_x86.asm | 11 + .../testdata/assembly/testd_x86_64.asm | 8 + .../blackbox/testdata/auto-qrc/auto-qrc.qbs | 52 + .../auto/blackbox/testdata/auto-qrc/main.cpp | 18 + .../testdata/auto-qrc/qrc-base/resource1.txt | 1 + .../auto-qrc/qrc-base/subdir/resource2.txt | 1 + .../auto-qrc/qrc-base/subdir/resource3.txt | 1 + .../badInterpreter/badInterpreter.qbs | 49 + .../qbs/modules/script-test/script-test.qbs | 39 + .../badInterpreter/script-interp-missing | 1 + .../badInterpreter/script-interp-noexec | 1 + .../testdata/badInterpreter/script-noexec | 0 .../testdata/badInterpreter/script-ok | 1 + .../testdata/build-directories/project.qbs | 45 + .../bundle-structure/bundle-structure.qbs | 167 + .../testdata/bundle-structure/dummy.c | 29 + .../testdata/bundle-structure/dummy.h | 27 + .../testdata/bundle-structure/dummy_p.h | 27 + .../testdata/bundle-structure/resource.txt | 0 .../change-in-disabled-product/project.qbs | 9 + .../change-in-disabled-product/test1.txt | 0 .../change-in-disabled-product/test2.txt | 0 .../change-in-imported-file.qbs | 23 + .../change-in-imported-file/prepare.js | 3 + .../testdata/change-in-imported-file/test.txt | 0 .../blackbox/testdata/changed-files/file1.cpp | 29 + .../blackbox/testdata/changed-files/file2.cpp | 29 + .../blackbox/testdata/changed-files/main.cpp | 29 + .../testdata/changed-files/project.qbs | 30 + tests/auto/blackbox/testdata/clean/clean.qbs | 19 + tests/auto/blackbox/testdata/clean/dep.cpp | 31 + tests/auto/blackbox/testdata/clean/main.cpp | 29 + .../auto/blackbox/testdata/cli/HelloWorld.cs | 12 + tests/auto/blackbox/testdata/cli/Libby.cs | 10 + tests/auto/blackbox/testdata/cli/Libby2.cs | 10 + tests/auto/blackbox/testdata/cli/Module.cs | 10 + tests/auto/blackbox/testdata/cli/Module.vb | 7 + .../auto/blackbox/testdata/cli/dotnettest.qbs | 51 + tests/auto/blackbox/testdata/cli/fshello.fs | 1 + tests/auto/blackbox/testdata/cli/fshello.qbs | 8 + .../concurrent-executor.qbs | 71 + .../testdata/concurrent-executor/dummy1.input | 0 .../testdata/concurrent-executor/dummy2.input | 0 .../testdata/concurrent-executor/util.js | 8 + .../conditional-export/conditional-export.qbs | 18 + .../testdata/conditional-export/main.cpp | 33 + .../conflicting-artifacts.qbs | 16 + .../testdata/conflicting-artifacts/main.cpp | 29 + .../THIS.IS.A.STRANGE.FILENAME.CAR.XML | 11 + .../blackbox/testdata/dbus-adaptors/car.cpp | 147 + .../blackbox/testdata/dbus-adaptors/car.h | 61 + .../blackbox/testdata/dbus-adaptors/car.qbs | 19 + .../blackbox/testdata/dbus-adaptors/main.cpp | 82 + .../blackbox/testdata/dbus-interfaces/car.xml | 11 + .../testdata/dbus-interfaces/controller.cpp | 92 + .../testdata/dbus-interfaces/controller.h | 57 + .../testdata/dbus-interfaces/controller.qbs | 20 + .../testdata/dbus-interfaces/controller.ui | 64 + .../testdata/dbus-interfaces/main.cpp | 62 + .../dependenciesProperty.qbs | 40 + .../dependenciesProperty/product2.cpp | 29 + .../dependency-profile-mismatch.qbs | 14 + .../testdata/deploymentTarget/deployment.qbs | 9 + .../blackbox/testdata/deploymentTarget/main.c | 29 + .../deprecated-property.qbs | 8 + .../modules/themodule/m.qbs | 22 + .../dynamicMultiplexRule.qbs | 33 + .../testdata/dynamicMultiplexRule/one.txt | 0 .../testdata/dynamicMultiplexRule/three.txt | 0 .../testdata/dynamicMultiplexRule/two.txt | 0 .../dynamicRuleOutputs/after/numbers.l | 81 + .../before/flexoptionsreader.js | 103 + .../dynamicRuleOutputs/before/genlexer.qbs | 115 + .../dynamicRuleOutputs/before/numbers.l | 82 + .../embedInfoPlist/embedInfoPlist.qbs | 53 + .../blackbox/testdata/embedInfoPlist/main.m | 9 + .../testdata/enableExceptions/empty.m | 1 + .../testdata/enableExceptions/empty.mm | 1 + .../testdata/enableExceptions/emptymain.cpp | 29 + .../enableExceptions/exceptions-objc.qbs | 6 + .../exceptions-objcpp-cpp.qbs | 6 + .../enableExceptions/exceptions-objcpp.qbs | 7 + .../testdata/enableExceptions/exceptions.qbs | 7 + .../testdata/enableExceptions/main.cpp | 36 + .../blackbox/testdata/enableExceptions/main.m | 5 + .../testdata/enableExceptions/none.qbs | 10 + .../blackbox/testdata/enableRtti/main.cpp | 51 + .../blackbox/testdata/enableRtti/rtti.qbs | 13 + .../nonexistentWorkingDir/project.qbs | 20 + .../blackbox/testdata/error-info/helper.js | 9 + .../blackbox/testdata/error-info/project.qbs | 72 + .../blackbox/testdata/export-rule/blubber.cpp | 29 + .../testdata/export-rule/export-rule.qbs | 37 + .../blackbox/testdata/export-rule/myapp.blubb | 8 + .../export-to-outside-searchpath.qbs | 21 + .../qbs-resources/modules/aModule/aModule.qbs | 4 + .../fileDependencies/awesomelib/awesome.h | 36 + .../fileDependencies/awesomelib/magnificent.h | 34 + .../fileDependencies/fileDependencies.qbs | 13 + .../testdata/fileDependencies/src/narf.cpp | 35 + .../testdata/fileDependencies/src/narf.h | 30 + .../testdata/fileDependencies/src/zort.cpp | 36 + .../auto/blackbox/testdata/find/find-cli.qbs | 34 + .../testdata/frameworkStructure/BaseResource | 0 .../testdata/frameworkStructure/Widget.cpp | 29 + .../testdata/frameworkStructure/Widget.h | 29 + .../frameworkStructure/WidgetPrivate.h | 27 + .../en.lproj/EnglishResource | 0 .../frameworkStructure/frameworkStructure.qbs | 15 + .../input.txt | 1 + .../p.qbs | 55 + .../group-condition-change.qbs | 27 + .../group-condition-change/input_kaputt.txt | 0 .../group-location-warning/GroupInSameDir.qbs | 5 + .../group-location-warning.qbs | 27 + .../group-location-warning/modules/gm/gm.qbs | 7 + ...ed-from-group-in-other-dir-via-wildcard.wc | 0 .../referenced-from-group-in-other-dir.txt | 0 .../referenced-from-group-in-same-dir.txt | 0 .../referenced-from-module.txt | 0 .../referenced-from-parent-in-other-dir.txt | 0 .../referenced-from-product.txt | 0 .../referenced-via-absolute-path.txt | 0 .../referenced-via-absolute-prefix.txt | 0 .../subdir/AGroupInOtherDir.qbs | 5 + .../subdir/AndAnotherGroupInOtherDir.qbs | 6 + .../subdir/OtherGroupInOtherDir.qbs | 5 + .../subdir/ParentInOtherDir.qbs | 7 + .../subdir/YetAnotherGroupInOtherDir.qbs | 6 + .../groups-in-modules/groups-in-modules.qbs | 24 + .../modules/helper/chunk.coal | 1 + .../modules/helper/diamondc.c | 54 + .../modules/helper/helper.qbs | 48 + .../modules/helper2/helper2.c | 29 + .../modules/helper2/helper2.qbs | 17 + .../modules/helper3/helper3.c | 29 + .../modules/helper3/helper3.qbs | 13 + .../modules/helper4/helper4.c | 29 + .../modules/helper4/helper4.qbs | 12 + .../modules/helper5/helper5.c | 33 + .../modules/helper5/helper5.qbs | 16 + .../modules/helper6/helper6.c | 29 + .../modules/helper6/helper6.qbs | 13 + .../testdata/groups-in-modules/rock.coal | 1 + .../groups-in-modules/someotherfile.txt | 0 .../groups-in-modules/someotherfile2.txt | 0 .../assetcatalog/EmptyStoryboard.storyboard | 8 + .../testdata/ib/assetcatalog/MainMenu.xib | 680 +++ .../ib/assetcatalog/Storyboard.storyboard | 53 + .../ib/assetcatalog/assetcatalogempty.qbs | 19 + .../empty.iconset/icon_16x16.png | Bin 0 -> 649 bytes .../empty.iconset/icon_16x16@2x.png | Bin 0 -> 665 bytes .../other.imageset/Contents.json | 22 + .../other.imageset/icon_16x16.png | Bin 0 -> 649 bytes .../other.imageset/icon_16x16@2x.png | Bin 0 -> 665 bytes .../blackbox/testdata/ib/assetcatalog/main.c | 32 + .../assetcatalog1.xcassets/.keep | 0 .../assetcatalog2.xcassets/.keep | 0 .../testdata/ib/empty-asset-catalogs/main.c | 29 + .../multiple-asset-catalogs.qbs | 10 + .../blackbox/testdata/ib/iconset/iconset.qbs | 7 + .../ib/iconset/white.iconset/icon_16x16.png | Bin 0 -> 649 bytes .../iconset/white.iconset/icon_16x16@2x.png | Bin 0 -> 665 bytes .../testdata/ib/iconsetapp/iconsetapp.qbs | 6 + .../blackbox/testdata/ib/iconsetapp/main.c | 32 + .../iconsetapp/white.iconset/icon_16x16.png | Bin 0 -> 649 bytes .../white.iconset/icon_16x16@2x.png | Bin 0 -> 665 bytes .../other.imageset/Contents.json | 22 + .../other.imageset/icon_16x16.png | Bin 0 -> 649 bytes .../other.imageset/icon_16x16@2x.png | Bin 0 -> 665 bytes .../other.imageset/Contents.json | 22 + .../other.imageset/icon_16x16.png | Bin 0 -> 649 bytes .../other.imageset/icon_16x16@2x.png | Bin 0 -> 665 bytes .../ib/multiple-asset-catalogs/main.c | 29 + .../multiple-asset-catalogs.qbs | 10 + .../import-in-properties-condition.qbs | 5 + .../modules/amodule/m.qbs | 10 + .../modules/depmodule/m.qbs | 5 + .../testdata/importing-product/header.h.in | 1 + .../importing-product/importing-product.qbs | 44 + .../testdata/importing-product/main.cpp | 29 + .../imports-conflict/imports-conflict.qbs | 11 + .../imports-conflict/modules/themodule/m.qbs | 5 + .../modules/themodule/utils.js | 1 + .../blackbox/testdata/infoplist/infoplist.qbs | 6 + tests/auto/blackbox/testdata/infoplist/main.c | 29 + .../testdata/innosetup/inc/qbsinc.iss | 0 .../blackbox/testdata/innosetup/innosetup.qbs | 21 + .../auto/blackbox/testdata/innosetup/test.iss | 6 + .../inputs-from-dependencies/file1.txt | 0 .../inputs-from-dependencies/file2.txt | 0 .../inputs-from-dependencies/file3.txt | 0 .../inputs-from-dependencies/file4.txt | 0 .../inputs-from-dependencies/project.qbs | 51 + .../install-duplicates/dir1/file1.txt | 1 + .../install-duplicates/dir1/file2.txt | 1 + .../install-duplicates/dir2/file1.txt | 1 + .../install-duplicates/dir2/file2.txt | 1 + .../install-duplicates/dir2/file3.txt | 1 + .../install-duplicates/install-duplicates.qbs | 10 + .../testdata/install-tree/data/foo.txt | 0 .../install-tree/data/subdir1/bar.txt | 0 .../install-tree/data/subdir2/baz.txt | 0 .../blackbox/testdata/install-tree/main.cpp | 29 + .../testdata/install-tree/project.qbs | 11 + .../testdata/installable/installable.qbs | 43 + .../blackbox/testdata/installable/main.cpp | 29 + .../testdata/installed-source-files/main.cpp | 29 + .../installed-source-files/project.qbs | 20 + .../installed-source-files/readme.txt | 1 + .../installed-transformer-output/qbs668.qbs | 31 + .../installed_artifact/installed_artifact.qbs | 19 + .../testdata/installed_artifact/main.cpp | 29 + .../installpackage/installpackage.qbs | 43 + .../blackbox/testdata/installpackage/lib.cpp | 31 + .../blackbox/testdata/installpackage/lib.h | 37 + .../blackbox/testdata/installpackage/main.cpp | 34 + .../invalid-command-property/input.txt | 0 .../invalid-command-property.qbs | 27 + .../invalid-extension-instantiation.qbs | 27 + .../invalid-library-names.qbs | 12 + .../testdata/invalid-library-names/main.cpp | 29 + .../testdata/jsextensions-file/file.qbs | 49 + .../jsextensions-fileinfo/fileinfo.qbs | 47 + .../testdata/jsextensions-process/process.qbs | 69 + .../propertylist.qbs | 129 + .../jsextensions-temporarydir.qbs | 28 + .../jsextensions-textfile/textfile.qbs | 36 + tests/auto/blackbox/testdata/ld/coreutils.cpp | 34 + tests/auto/blackbox/testdata/ld/coreutils.h | 32 + tests/auto/blackbox/testdata/ld/ld.qbs | 26 + tests/auto/blackbox/testdata/ld/main.cpp | 36 + .../testdata/lexyacc/one-grammar/lexer.l | 20 + .../lexyacc/one-grammar/one-grammar.qbs | 15 + .../testdata/lexyacc/one-grammar/parser.y | 30 + .../testdata/lexyacc/one-grammar/types.h | 50 + .../testdata/lexyacc/two-grammars/g1.l | 10 + .../testdata/lexyacc/two-grammars/g1.y | 7 + .../testdata/lexyacc/two-grammars/g2.l | 10 + .../testdata/lexyacc/two-grammars/g2.y | 7 + .../testdata/lexyacc/two-grammars/main.c | 37 + .../lexyacc/two-grammars/two-grammars.qbs | 13 + .../auto/blackbox/testdata/linkerMode/main.c | 36 + .../blackbox/testdata/linkerMode/main.cpp | 36 + .../auto/blackbox/testdata/linkerMode/main.m | 7 + .../auto/blackbox/testdata/linkerMode/main.mm | 10 + .../auto/blackbox/testdata/linkerMode/main.s | 5 + .../blackbox/testdata/linkerMode/project.qbs | 83 + .../testdata/linkerMode/staticlib.cpp | 37 + .../blackbox/testdata/linkerMode/staticmain.c | 34 + .../testdata/linkerscripts/linkerscript1 | 1 + .../testdata/linkerscripts/linkerscript2 | 1 + .../testdata/linkerscripts/linkerscripts.qbs | 33 + .../blackbox/testdata/linkerscripts/testlib.c | 29 + .../list-properties-with-outer/dummy.txt | 0 .../modules/higher/higher.qbs | 6 + .../modules/lower/lower.qbs | 22 + .../list-properties-with-outer/project.qbs | 12 + .../testdata/list-property-order/dummy.txt | 0 .../modules/higher1/higher1.qbs | 6 + .../modules/higher2/higher2.qbs | 6 + .../modules/higher3/higher3.qbs | 6 + .../modules/lower/lower.qbs | 23 + .../testdata/list-property-order/product.qbs | 13 + .../testdata/loadablemodule/exported.cpp | 35 + .../testdata/loadablemodule/exported.h | 33 + .../loadablemodule/loadablemodule.qbs | 32 + .../blackbox/testdata/loadablemodule/main.cpp | 64 + tests/auto/blackbox/testdata/lrelease/de.ts | 23 + tests/auto/blackbox/testdata/lrelease/hu.ts | 15 + .../blackbox/testdata/lrelease/lrelease.qbs | 8 + .../testdata/missing-dependency/main.cpp | 33 + .../missing-dependency/missing-dependency.qbs | 33 + .../testdata/mixed-build-variants/main.cpp | 34 + .../mixed-build-variants.qbs | 6 + .../auto/blackbox/testdata/moc-flags/blubb.h | 37 + .../auto/blackbox/testdata/moc-flags/main.cpp | 34 + .../blackbox/testdata/moc-flags/moc-flags.qbs | 5 + .../testdata/multiple-changes/dummy.txt | 0 .../testdata/multiple-changes/project.qbs | 33 + .../blackbox/testdata/nested-groups/file1.cpp | 37 + .../blackbox/testdata/nested-groups/file1.h | 29 + .../blackbox/testdata/nested-groups/file2.cpp | 37 + .../blackbox/testdata/nested-groups/file2.h | 29 + .../blackbox/testdata/nested-groups/file3.cpp | 33 + .../blackbox/testdata/nested-groups/file3.h | 29 + .../blackbox/testdata/nested-groups/main.cpp | 40 + .../blackbox/testdata/nested-groups/main2.cpp | 29 + .../blackbox/testdata/nested-groups/main3.cpp | 29 + .../modules/themodule/themodule.qbs | 11 + .../testdata/nested-groups/nested-groups.qbs | 40 + .../blackbox/testdata/nested-groups/other.cpp | 37 + .../blackbox/testdata/nested-groups/other.h | 29 + .../testdata/nested-properties/dummy.txt | 0 .../modules/higherlevel/higher-level.qbs | 6 + .../modules/lowerlevel/lower-level.qbs | 22 + .../testdata/nested-properties/product.qbs | 22 + .../testdata/new-output-artifact/input.txt | 0 .../new-output-artifact.qbs | 37 + tests/auto/blackbox/testdata/nodejs/hello.js | 3 + tests/auto/blackbox/testdata/nodejs/hello.qbs | 6 + .../broken.cpp | 1 + .../fine.cpp | 29 + .../project.qbs | 6 + .../testdata/non-default-product/main.cpp | 29 + .../testdata/non-default-product/project.qbs | 17 + tests/auto/blackbox/testdata/nsis/hello.bat | 1 + tests/auto/blackbox/testdata/nsis/hello.nsi | 19 + tests/auto/blackbox/testdata/nsis/hello.qbs | 10 + tests/auto/blackbox/testdata/objc-arc/arc.m | 3 + tests/auto/blackbox/testdata/objc-arc/arc.mm | 3 + tests/auto/blackbox/testdata/objc-arc/main.m | 4 + tests/auto/blackbox/testdata/objc-arc/mrc.m | 3 + tests/auto/blackbox/testdata/objc-arc/mrc.mm | 3 + .../blackbox/testdata/objc-arc/objc-arc.qbs | 21 + .../broken.cpp.in | 1 + .../output-artifact-auto-tagging/main.cpp.in | 1 + .../output-artifact-auto-tagging.qbs | 27 + .../overrideProjectProperties/helper_lib.qbs | 8 + .../overrideProjectProperties/helperlib.cpp | 36 + .../overrideProjectProperties/main.cpp | 32 + .../overrideProjectProperties/main2.cpp | 42 + .../overrideProjectProperties/project.qbs | 35 + .../project_using_helper_lib.qbs | 14 + .../testdata/pch-change-tracking/header1.h | 29 + .../testdata/pch-change-tracking/header2.h | 27 + .../testdata/pch-change-tracking/main.cpp | 35 + .../pch-change-tracking.qbs | 14 + .../testdata/pch-change-tracking/pch.h | 29 + .../modules/themodule/themodule.qbs | 28 + .../pkg-config-probe-sysroot/pkg-config.qbs | 29 + .../sysroot1/usr/share/pkgconfig/dummy.pc | 7 + .../sysroot2/usr/share/pkgconfig/dummy.pc | 7 + .../pkg-config-probe/dummy1/dummy1.pc | 7 + .../pkg-config-probe/dummy2/dummy2.pc | 7 + .../modules/themodule/themodule.qbs | 42 + .../testdata/pkg-config-probe/pkg-config.qbs | 23 + .../testdata/plugin-meta-data/app.cpp | 51 + .../plugin-meta-data/plugin-meta-data.qbs | 39 + .../testdata/plugin-meta-data/theplugin.cpp | 38 + .../probe-change-tracking.qbs | 13 + .../probe-in-exported-module/dependee.qbs | 16 + .../probe-in-exported-module/dependency.qbs | 8 + .../modules/depmodule/depmodule.qbs | 23 + .../modules/mymodule/mymodule.qbs | 28 + .../modules/myothermodule/myothermodule.qbs | 6 + .../probe-in-exported-module.qbs | 5 + .../testdata/probe-in-exported-module/test.in | 0 .../probe-in-exported-module/test2.in | 0 .../testdata/probeProperties/bin/tool | 0 .../blackbox/testdata/probeProperties/main.c | 29 + .../probeProperties/probeProperties.qbs | 30 + .../modules/mymodule/mymodule.qbs | 32 + .../probes-and-array-properties.qbs | 8 + .../modules/inner/inner.qbs | 20 + .../modules/outer/outer.qbs | 19 + .../probes-in-nested-modules.qbs | 35 + .../product-dependencies-by-type/main.cpp | 29 + .../product-dependencies-by-type/project.qbs | 66 + .../testdata/productproperties/app.qbs | 12 + .../productproperties/blubb_header.h.in | 0 .../testdata/productproperties/header.qbs | 35 + .../testdata/productproperties/main.cpp | 36 + .../testdata/productproperties/project.qbs | 6 + .../testdata/project_filepath_check/main.cpp | 29 + .../project_filepath_check/project1.qbs | 5 + .../project_filepath_check/project2.qbs | 5 + .../blackbox/testdata/proper quoting/main.cpp | 41 + .../proper quoting/my static lib helper.cpp | 33 + .../testdata/proper quoting/my static lib.cpp | 37 + .../proper quoting/proper quoting.qbs | 40 + .../some helper/some helper.cpp | 35 + .../proper quoting/some helper/some helper.h | 35 + .../properties-in-export-items/main1.cpp | 31 + .../properties-in-export-items/main2.cpp | 31 + .../properties-in-export-items.qbs | 31 + .../testdata/property-precedence/dep.qbs | 11 + .../testdata/property-precedence/dummy.txt | 0 .../property-precedence/modules/leaf/leaf.qbs | 25 + .../modules/nonleaf/nonleaf.qbs | 8 + .../testdata/property-precedence/project.qbs | 18 + .../blackbox/testdata/propertyChanges/lib.cpp | 31 + .../modules/TestModule/module.qbs | 31 + .../testdata/propertyChanges/project.qbs | 91 + .../testdata/propertyChanges/ruletest.qbs | 11 + .../testdata/propertyChanges/source1.cpp | 29 + .../testdata/propertyChanges/source2.cpp | 30 + .../testdata/propertyChanges/source3.cpp | 30 + .../blackbox/testdata/propertyChanges/test.in | 1 + .../testdata/qbsVersion/qbs-version.qbs | 22 + .../blackbox/testdata/qml-debugging/main.cpp | 56 + .../testdata/qml-debugging/project.qbs | 9 + .../blackbox/testdata/qobject-in-mm/main.mm | 13 + .../testdata/qobject-in-mm/project.qbs | 6 + tests/auto/blackbox/testdata/qrc/bla.cpp | 33 + tests/auto/blackbox/testdata/qrc/bla.qrc | 5 + tests/auto/blackbox/testdata/qrc/i.qbs | 20 + tests/auto/blackbox/testdata/qrc/stuff.txt | 1 + .../testdata/qtscxml/dummystatemachine.scxml | 3 + tests/auto/blackbox/testdata/qtscxml/main.cpp | 15 + .../blackbox/testdata/qtscxml/qtscxml.qbs | 52 + .../rad-after-incomplete-build/dummy.txt | 0 .../project_with_rule.qbs | 26 + .../recursive_renaming/dir/subdir/blubb.txt | 0 .../recursive_renaming/dir/wasser.txt | 0 .../recursive_renaming/recursive_renaming.qbs | 9 + .../recursive_wildcards/dir/file1.txt | 0 .../recursive_wildcards/dir/subdir/file2.txt | 0 .../recursive_wildcards.qbs | 7 + .../testdata/referenceErrorInExport/main.c | 29 + .../referenceErrorInExport/project.qbs | 20 + .../testdata/renameDependency/after/lib2.cpp | 35 + .../testdata/renameDependency/after/lib2.h | 29 + .../testdata/renameDependency/before/lib.cpp | 35 + .../testdata/renameDependency/before/lib.h | 29 + .../testdata/renameDependency/before/main.cpp | 36 + .../before/renameDependency.qbs | 5 + .../testdata/reproducible-build/file1.cpp | 31 + .../testdata/reproducible-build/file2.cpp | 31 + .../testdata/reproducible-build/main.cpp | 36 + .../reproducible-build/reproducible-build.qbs | 7 + .../response-files/cat-response-file.cpp | 67 + .../response-files/response-files.qbs | 37 + .../rule-with-no-inputs.qbs | 26 + .../blackbox/testdata/ruleConditions/foo.narf | 0 .../blackbox/testdata/ruleConditions/main.cpp | 29 + .../modules/narfzort/narfzort.qbs | 30 + .../ruleConditions/ruleConditions.qbs | 12 + .../ruleConditions/templates/zorduct.qbs | 12 + .../blackbox/testdata/ruleCycle/happy.grass | 1 + .../blackbox/testdata/ruleCycle/ruleCycle.qbs | 47 + .../testdata/separate-debug-info/foo.cpp | 35 + .../testdata/separate-debug-info/main.cpp | 29 + .../testdata/separate-debug-info/project.qbs | 115 + .../auto/blackbox/testdata/soversion/lib.cpp | 1 + .../blackbox/testdata/soversion/soversion.qbs | 9 + .../subprofile-change-tracking/main1.cpp | 29 + .../subprofile-change-tracking/main2.cpp | 29 + .../subprofile-change-tracking.qbs | 9 + .../testdata/successive-changes/input.in | 0 .../successive-changes/successive-changes.qbs | 30 + .../suspicious-calls/copy-command.qbs | 23 + .../testdata/suspicious-calls/copy-eval.qbs | 10 + .../suspicious-calls/copy-prepare.qbs | 24 + .../testdata/suspicious-calls/copy-probe.qbs | 13 + .../suspicious-calls/direntries-command.qbs | 25 + .../suspicious-calls/direntries-eval.qbs | 6 + .../suspicious-calls/direntries-prepare.qbs | 24 + .../suspicious-calls/direntries-probe.qbs | 14 + .../testdata/suspicious-calls/test.txt | 0 .../symlink-removal/symlink-removal.qbs | 21 + .../testdata/system-run-paths/lib.cpp | 29 + .../testdata/system-run-paths/main.cpp | 34 + .../system-run-paths/system-run-paths.qbs | 24 + .../qbs-resources/imports/MyProduct.qbs | 3 + .../toplevel-searchpath.qbs | 3 + .../testdata/trackAddFile/after/main.cpp | 41 + .../testdata/trackAddFile/after/project.qbs | 16 + .../testdata/trackAddFile/after/zort.cpp | 35 + .../testdata/trackAddFile/after/zort.h | 38 + .../testdata/trackAddFile/before/main.cpp | 38 + .../testdata/trackAddFile/before/narf.cpp | 35 + .../testdata/trackAddFile/before/narf.h | 38 + .../testdata/trackAddFile/before/project.qbs | 12 + .../trackAddMocInclude/after/main.cpp | 49 + .../trackAddMocInclude/before/main.cpp | 47 + .../trackAddMocInclude/before/test.qbs | 7 + .../environmentChange.cpp | 29 + .../trackExternalProductChanges/fileList.js | 6 + .../hidden/hiddenheaderqbs.h | 27 + .../trackExternalProductChanges/including.cpp | 31 + .../jsFileChange.cpp | 29 + .../trackExternalProductChanges/main.cpp | 29 + .../trackExternalProductChanges/project.qbs | 15 + .../testdata/trackFileTags/after/main.cpp | 37 + .../testdata/trackFileTags/after/project.qbs | 53 + .../testdata/trackFileTags/before/main.cpp | 35 + .../testdata/trackFileTags/before/project.qbs | 53 + .../testdata/trackProducts/after/product3.qbs | 8 + .../trackProducts/after/trackProducts.qbs | 7 + .../testdata/trackProducts/after/zoo.cpp | 34 + .../testdata/trackProducts/before/bar.cpp | 34 + .../testdata/trackProducts/before/foo.cpp | 34 + .../trackProducts/before/product1.qbs | 8 + .../trackProducts/before/product2.qbs | 8 + .../trackProducts/before/trackProducts.qbs | 7 + .../blackbox/testdata/trackQObjChange/bla.cpp | 35 + .../testdata/trackQObjChange/bla_noqobject.h | 34 + .../testdata/trackQObjChange/bla_qobject.h | 35 + .../blackbox/testdata/trackQObjChange/i.qbs | 19 + .../modules/a/a.qbs | 5 + .../modules/b/b.qbs | 5 + .../transitive-optional-dependencies.qbs | 5 + .../blackbox/testdata/typescript/animals.ts | 21 + .../blackbox/testdata/typescript/extra.js | 3 + .../auto/blackbox/testdata/typescript/foo.ts | 5 + .../auto/blackbox/testdata/typescript/foo2.ts | 1 + .../blackbox/testdata/typescript/hello.ts | 1 + .../auto/blackbox/testdata/typescript/main.ts | 22 + .../testdata/typescript/typescript.qbs | 56 + .../testdata/typescript/woosh/extra.ts | 2 + .../custom1.in | 0 .../custom2.in | 0 .../project.qbs | 62 + .../versioncheck/modules/higher/higher.qbs | 10 + .../versioncheck/modules/lower/lower.qbs | 3 + .../testdata/versioncheck/versioncheck.qbs | 12 + .../blackbox/testdata/versionscript/testlib.c | 30 + .../testdata/versionscript/versionscript | 1 + .../testdata/versionscript/versionscript.qbs | 33 + .../testdata/wildcard_renaming/pioniere.txt | 0 .../wildcard_renaming/wildcard_renaming.qbs | 8 + .../testdata/wildcards-and-rules/input1.inp | 0 .../testdata/wildcards-and-rules/project.qbs | 37 + .../blackbox/testdata/wix/ExampleScript.bat | 1 + .../blackbox/testdata/wix/QbsBootstrapper.wxs | 10 + tests/auto/blackbox/testdata/wix/QbsSetup.wxs | 34 + tests/auto/blackbox/testdata/wix/Qt.wxs | 5 + .../blackbox/testdata/wix/WiXInstallers.qbs | 34 + tests/auto/blackbox/testdata/wix/de.wxl | 1 + .../blackbox/testdata/xcode/xcode-project.qbs | 37 + tests/auto/blackbox/tst_blackbox.cpp | 4437 ++++++++++++++++ tests/auto/blackbox/tst_blackbox.h | 213 + tests/auto/blackbox/tst_blackboxbase.cpp | 222 + tests/auto/blackbox/tst_blackboxbase.h | 97 + tests/auto/blackbox/tst_blackboxjava.cpp | 309 ++ tests/auto/blackbox/tst_blackboxjava.h | 52 + tests/auto/blackbox/tst_clangdb.cpp | 196 + tests/auto/blackbox/tst_clangdb.h | 63 + tests/auto/buildgraph/buildgraph.pro | 6 + tests/auto/buildgraph/buildgraph.qbs | 7 + tests/auto/buildgraph/tst_buildgraph.cpp | 39 + tests/auto/cmdlineparser/cmdlineparser.pro | 7 + tests/auto/cmdlineparser/cmdlineparser.qbs | 30 + .../data/dirwithmultipleprojects/project.qbs | 0 .../data/dirwithmultipleprojects/project2.qbs | 0 .../data/dirwithnoprojects/.gitignore | 2 + .../data/dirwithoneproject/project.qbs | 0 .../auto/cmdlineparser/tst_cmdlineparser.cpp | 183 + tests/auto/language/language.pro | 6 + tests/auto/language/language.qbs | 7 + tests/auto/language/tst_language.cpp | 42 + tests/auto/shared.h | 171 + tests/auto/tools/tools.pro | 5 + tests/auto/tools/tools.qbs | 7 + tests/auto/tools/tst_tools.cpp | 41 + tests/benchmarker/activities.h | 42 + tests/benchmarker/benchmarker-main.cpp | 107 + tests/benchmarker/benchmarker.cpp | 111 + tests/benchmarker/benchmarker.h | 80 + tests/benchmarker/benchmarker.pro | 20 + tests/benchmarker/benchmarker.qbs | 23 + tests/benchmarker/commandlineparser.cpp | 115 + tests/benchmarker/commandlineparser.h | 64 + tests/benchmarker/exception.h | 53 + tests/benchmarker/runsupport.cpp | 68 + tests/benchmarker/runsupport.h | 47 + tests/benchmarker/valgrindrunner.cpp | 242 + tests/benchmarker/valgrindrunner.h | 90 + tests/fuzzy-test/commandlineparser.cpp | 132 + tests/fuzzy-test/commandlineparser.h | 76 + tests/fuzzy-test/fuzzy-test.pro | 11 + tests/fuzzy-test/fuzzy-test.qbs | 16 + tests/fuzzy-test/fuzzytester.cpp | 300 ++ tests/fuzzy-test/fuzzytester.h | 84 + tests/fuzzy-test/main.cpp | 88 + tests/manual/configure/configure.qbs | 15 + tests/manual/configure/main.cpp | 36 + .../configure/modules/definition/module.qbs | 32 + tests/manual/includeLookup/includeLookup.qbs | 15 + tests/manual/includeLookup/main.cpp | 36 + .../modules/definition/module.qbs | 12 + .../localDeployment/localDeployment.qbs | 19 + tests/manual/localDeployment/main.cpp | 40 + tests/manual/minimumSystemVersion/main.cpp | 103 + .../minimumSystemVersion.qbs | 76 + tests/manual/pkgconfig/main.cpp | 36 + tests/manual/pkgconfig/pkgconfig.qbs | 18 + tests/manual/run-qbs-tests.bat | 8 + tests/manual/run-qbs-tests.sh | 11 + tests/tests.pro | 2 + 1872 files changed, 158350 insertions(+) create mode 100644 LGPL_EXCEPTION.txt create mode 100644 LICENSE.LGPLv21 create mode 100644 LICENSE.LGPLv3 create mode 100644 README create mode 100644 bin/ibmsvc.xml create mode 100644 bin/ibqbs.bat create mode 100644 dist/dist.qbs create mode 100644 doc/classic.css create mode 100644 doc/config/macros.qdocconf create mode 100644 doc/config/qbs-project.qdocconf create mode 100644 doc/config/style/qt5-sidebar.html create mode 100644 doc/doc.pri create mode 100644 doc/doc.qbs create mode 100644 doc/fixnavi.pl create mode 100644 doc/qbs-online.qdocconf create mode 100644 doc/qbs.qdoc create mode 100644 doc/qbs.qdocconf create mode 100644 doc/reference/commands.qdoc create mode 100644 doc/reference/items/convenience/androidapk.qdoc create mode 100644 doc/reference/items/convenience/application.qdoc create mode 100644 doc/reference/items/convenience/applicationextension.qdoc create mode 100644 doc/reference/items/convenience/autotestrunner.qdoc create mode 100644 doc/reference/items/convenience/cppapplication.qdoc create mode 100644 doc/reference/items/convenience/dynamiclibrary.qdoc create mode 100644 doc/reference/items/convenience/innosetup.qdoc create mode 100644 doc/reference/items/convenience/installpackage.qdoc create mode 100644 doc/reference/items/convenience/javaclasscollection.qdoc create mode 100644 doc/reference/items/convenience/javajarfile.qdoc create mode 100644 doc/reference/items/convenience/loadablemodule.qdoc create mode 100644 doc/reference/items/convenience/qtapplication.qdoc create mode 100644 doc/reference/items/convenience/qtguiapplication.qdoc create mode 100644 doc/reference/items/convenience/staticlibrary.qdoc create mode 100644 doc/reference/items/convenience/xpcservice.qdoc create mode 100644 doc/reference/items/language/artifact.qdoc create mode 100644 doc/reference/items/language/depends.qdoc create mode 100644 doc/reference/items/language/export.qdoc create mode 100644 doc/reference/items/language/filetagger.qdoc create mode 100644 doc/reference/items/language/group.qbs create mode 100644 doc/reference/items/language/group.qdoc create mode 100644 doc/reference/items/language/module.qdoc create mode 100644 doc/reference/items/language/probe.qdoc create mode 100644 doc/reference/items/language/product.qdoc create mode 100644 doc/reference/items/language/project.qdoc create mode 100644 doc/reference/items/language/properties.qdoc create mode 100644 doc/reference/items/language/propertyoptions.qdoc create mode 100644 doc/reference/items/language/rule.qdoc create mode 100644 doc/reference/items/language/scanner.qdoc create mode 100644 doc/reference/items/language/subproject.qdoc create mode 100644 doc/reference/jsextensions/jsextension-environment.qdoc create mode 100644 doc/reference/jsextensions/jsextension-file.qdoc create mode 100644 doc/reference/jsextensions/jsextension-fileinfo.qdoc create mode 100644 doc/reference/jsextensions/jsextension-process.qdoc create mode 100644 doc/reference/jsextensions/jsextension-propertylist.qdoc create mode 100644 doc/reference/jsextensions/jsextension-temporarydir.qdoc create mode 100644 doc/reference/jsextensions/jsextension-textfile.qdoc create mode 100644 doc/reference/jsextensions/jsextension-utilities.qdoc create mode 100644 doc/reference/jsextensions/jsextensions-general.qdoc create mode 100644 doc/reference/list-of-tools.qdoc create mode 100644 doc/reference/modules/android-ndk-module.qdoc create mode 100644 doc/reference/modules/android-sdk-module.qdoc create mode 100644 doc/reference/modules/archiver-module.qdoc create mode 100644 doc/reference/modules/bundle-module.qdoc create mode 100644 doc/reference/modules/cpp-module.qdoc create mode 100644 doc/reference/modules/ib-module.qdoc create mode 100644 doc/reference/modules/innosetup-module.qdoc create mode 100644 doc/reference/modules/java-module.qdoc create mode 100644 doc/reference/modules/lexyacc-module.qdoc create mode 100644 doc/reference/modules/nodejs-module.qdoc create mode 100644 doc/reference/modules/nsis-module.qdoc create mode 100644 doc/reference/modules/qbs-module.qdoc create mode 100644 doc/reference/modules/qt-modules.qdoc create mode 100644 doc/reference/modules/typescript-module.qdoc create mode 100644 doc/reference/modules/wix-module.qdoc create mode 100644 doc/reference/modules/xcode-module.qdoc create mode 100644 doc/reference/reference.qdoc create mode 100644 doc/templates/images/arrow.png create mode 100644 doc/templates/images/arrow_down.png create mode 100644 doc/templates/images/bg_l.png create mode 100644 doc/templates/images/bg_l_blank.png create mode 100644 doc/templates/images/bg_ll_blank.png create mode 100644 doc/templates/images/bg_r.png create mode 100644 doc/templates/images/bg_ul_blank.png create mode 100644 doc/templates/images/bgrContent.png create mode 100644 doc/templates/images/blu_dot.png create mode 100644 doc/templates/images/box_bg.png create mode 100644 doc/templates/images/breadcrumb.png create mode 100644 doc/templates/images/btn_next.png create mode 100644 doc/templates/images/btn_prev.png create mode 100644 doc/templates/images/bullet_dn.png create mode 100644 doc/templates/images/bullet_gt.png create mode 100644 doc/templates/images/bullet_sq.png create mode 100644 doc/templates/images/bullet_up.png create mode 100644 doc/templates/images/feedbackground.png create mode 100644 doc/templates/images/header.png create mode 100644 doc/templates/images/header_bg.png create mode 100644 doc/templates/images/home.png create mode 100644 doc/templates/images/horBar.png create mode 100644 doc/templates/images/ico_note.png create mode 100644 doc/templates/images/ico_note_attention.png create mode 100644 doc/templates/images/ico_out.png create mode 100644 doc/templates/images/page.png create mode 100644 doc/templates/images/page_bg.png create mode 100644 doc/templates/images/qt_icon.png create mode 100644 doc/templates/images/spinner.gif create mode 100644 doc/templates/images/sprites-combined.png create mode 100644 doc/templates/scripts/functions.js create mode 100644 doc/templates/scripts/narrow.js create mode 100644 doc/templates/scripts/superfish.js create mode 100644 doc/templates/style/narrow.css create mode 100644 doc/templates/style/offline.css create mode 100644 doc/templates/style/style.css create mode 100644 doc/templates/style/style_ie6.css create mode 100644 doc/templates/style/style_ie7.css create mode 100644 doc/templates/style/style_ie8.css create mode 100644 doc/templates/style/superfish.css create mode 100644 doc/templates/style/superfish_skin.css create mode 100644 examples/app-and-lib/app/app.qbs create mode 100644 examples/app-and-lib/app/main.cpp create mode 100644 examples/app-and-lib/app_and_lib.qbs create mode 100644 examples/app-and-lib/lib/lib.cpp create mode 100644 examples/app-and-lib/lib/lib.h create mode 100644 examples/app-and-lib/lib/lib.qbs create mode 100644 examples/cocoa-application/CocoaApplication.qbs create mode 100644 examples/cocoa-application/CocoaApplication.xcodeproj/project.pbxproj create mode 100644 examples/cocoa-application/CocoaApplication/AppDelegate.h create mode 100644 examples/cocoa-application/CocoaApplication/AppDelegate.m create mode 100644 examples/cocoa-application/CocoaApplication/CocoaApplication-Info.plist create mode 100644 examples/cocoa-application/CocoaApplication/CocoaApplication-Prefix.pch create mode 100644 examples/cocoa-application/CocoaApplication/en.lproj/Credits.rtf create mode 100644 examples/cocoa-application/CocoaApplication/en.lproj/InfoPlist.strings create mode 100644 examples/cocoa-application/CocoaApplication/en.lproj/MainMenu.xib create mode 100644 examples/cocoa-application/CocoaApplication/main.m create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication.qbs create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication.xcodeproj/project.pbxproj create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.h create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.m create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Info.plist create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Prefix.pch create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/Default-568h@2x.png create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/Default.png create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/Default@2x.png create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.h create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.m create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.h create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.m create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/DetailViewController_iPad.xib create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/DetailViewController_iPhone.xib create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/InfoPlist.strings create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/MasterViewController_iPad.xib create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/MasterViewController_iPhone.xib create mode 100644 examples/cocoa-touch-application/CocoaTouchApplication/main.m create mode 100644 examples/code-generator/code-generator.qbs create mode 100644 examples/code-generator/hwgen.cpp create mode 100644 examples/collidingmice/collidingmice.qbs create mode 100644 examples/collidingmice/images/cheese.jpg create mode 100644 examples/collidingmice/main.cpp create mode 100644 examples/collidingmice/mice.qrc create mode 100644 examples/collidingmice/mouse.cpp create mode 100644 examples/collidingmice/mouse.h create mode 100644 examples/examples.qbs create mode 100644 examples/helloworld-complex/hello.qbs create mode 100644 examples/helloworld-complex/src/foo.cpp create mode 100644 examples/helloworld-complex/src/foo.h create mode 100644 examples/helloworld-complex/src/main.cpp create mode 100644 examples/helloworld-complex/src/specialfeature.cpp create mode 100644 examples/helloworld-complex/src/specialfeature.h create mode 100644 examples/helloworld-minimal/hello.qbs create mode 100644 examples/helloworld-minimal/main.cpp create mode 100644 examples/helloworld-qt/hello.qbs create mode 100644 examples/helloworld-qt/main.cpp create mode 100644 examples/install-bundle/coreutils.cpp create mode 100644 examples/install-bundle/coreutils.h create mode 100644 examples/install-bundle/install-bundle.qbs create mode 100644 examples/install-bundle/main.cpp create mode 100644 qbs-resources/imports/QbsApp.qbs create mode 100644 qbs-resources/imports/QbsAutotest.qbs create mode 100644 qbs-resources/imports/QbsFunctions/functions.js create mode 100644 qbs-resources/imports/QbsLibrary.qbs create mode 100644 qbs-resources/imports/QbsProduct.qbs create mode 100644 qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs create mode 100644 qbs.pro create mode 100644 qbs.qbs create mode 100644 qbs_version.pri create mode 100644 share/qbs/imports/qbs/BundleTools/bundle-tools.js create mode 100644 share/qbs/imports/qbs/DarwinTools/darwin-tools.js create mode 100644 share/qbs/imports/qbs/ModUtils/utils.js create mode 100644 share/qbs/imports/qbs/PathTools/path-tools.js create mode 100644 share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/BinaryProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/FrameworkProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/GccProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/IncludeProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/JdkProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/MsvcProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/NodeJsProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/NpmProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/PathProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/WiXProbe.qbs create mode 100644 share/qbs/imports/qbs/Probes/path-probe.js create mode 100644 share/qbs/imports/qbs/UnixUtils/unix-utils.js create mode 100644 share/qbs/imports/qbs/WindowsUtils/windows-utils.js create mode 100644 share/qbs/imports/qbs/base/AndroidApk.qbs create mode 100644 share/qbs/imports/qbs/base/Application.qbs create mode 100644 share/qbs/imports/qbs/base/ApplicationExtension.qbs create mode 100644 share/qbs/imports/qbs/base/AutotestRunner.qbs create mode 100644 share/qbs/imports/qbs/base/CppApplication.qbs create mode 100644 share/qbs/imports/qbs/base/DynamicLibrary.qbs create mode 100644 share/qbs/imports/qbs/base/InnoSetup.qbs create mode 100644 share/qbs/imports/qbs/base/InstallPackage.qbs create mode 100644 share/qbs/imports/qbs/base/JavaClassCollection.qbs create mode 100644 share/qbs/imports/qbs/base/JavaJarFile.qbs create mode 100644 share/qbs/imports/qbs/base/Library.qbs create mode 100644 share/qbs/imports/qbs/base/LoadableModule.qbs create mode 100644 share/qbs/imports/qbs/base/NSISSetup.qbs create mode 100644 share/qbs/imports/qbs/base/NetModule.qbs create mode 100644 share/qbs/imports/qbs/base/NodeJSApplication.qbs create mode 100644 share/qbs/imports/qbs/base/QtApplication.qbs create mode 100644 share/qbs/imports/qbs/base/QtGuiApplication.qbs create mode 100644 share/qbs/imports/qbs/base/StaticLibrary.qbs create mode 100644 share/qbs/imports/qbs/base/WindowsInstallerPackage.qbs create mode 100644 share/qbs/imports/qbs/base/WindowsSetupPackage.qbs create mode 100644 share/qbs/imports/qbs/base/XPCService.qbs create mode 100644 share/qbs/modules/Android/ndk/ndk.qbs create mode 100644 share/qbs/modules/Android/ndk/utils.js create mode 100644 share/qbs/modules/Android/sdk/sdk.qbs create mode 100644 share/qbs/modules/Android/sdk/utils.js create mode 100644 share/qbs/modules/archiver/archiver.qbs create mode 100644 share/qbs/modules/bundle/BundleModule.qbs create mode 100644 share/qbs/modules/bundle/MacOSX-Package-Types.xcspec create mode 100644 share/qbs/modules/bundle/MacOSX-Product-Types.xcspec create mode 100644 share/qbs/modules/bundle/bundle.js create mode 100755 share/qbs/modules/bundle/update-specs.sh create mode 100644 share/qbs/modules/cli/CLIModule.qbs create mode 100644 share/qbs/modules/cli/cli.js create mode 100644 share/qbs/modules/cli/mono.qbs create mode 100644 share/qbs/modules/cli/windows-dotnet.qbs create mode 100644 share/qbs/modules/cpp/CppModule.qbs create mode 100644 share/qbs/modules/cpp/DarwinGCC.qbs create mode 100644 share/qbs/modules/cpp/GenericGCC.qbs create mode 100644 share/qbs/modules/cpp/LinuxGCC.qbs create mode 100644 share/qbs/modules/cpp/UnixGCC.qbs create mode 100644 share/qbs/modules/cpp/android-gcc.qbs create mode 100644 share/qbs/modules/cpp/gcc.js create mode 100644 share/qbs/modules/cpp/genericunix-gcc.qbs create mode 100644 share/qbs/modules/cpp/ios-gcc.qbs create mode 100644 share/qbs/modules/cpp/macos-gcc.qbs create mode 100644 share/qbs/modules/cpp/msvc.js create mode 100644 share/qbs/modules/cpp/tvos-gcc.qbs create mode 100644 share/qbs/modules/cpp/watchos-gcc.qbs create mode 100644 share/qbs/modules/cpp/windows-mingw.qbs create mode 100644 share/qbs/modules/cpp/windows-msvc.qbs create mode 100644 share/qbs/modules/ib/IBModule.qbs create mode 100644 share/qbs/modules/ib/ib.js create mode 100644 share/qbs/modules/innosetup/InnoSetupModule.qbs create mode 100644 share/qbs/modules/java/JavaModule.qbs create mode 100644 share/qbs/modules/java/io/qt/qbs/Artifact.java create mode 100644 share/qbs/modules/java/io/qt/qbs/ArtifactListJsonWriter.java create mode 100644 share/qbs/modules/java/io/qt/qbs/ArtifactListTextWriter.java create mode 100644 share/qbs/modules/java/io/qt/qbs/ArtifactListWriter.java create mode 100644 share/qbs/modules/java/io/qt/qbs/ArtifactListXmlWriter.java create mode 100644 share/qbs/modules/java/io/qt/qbs/tools/JavaCompilerScannerTool.java create mode 100644 share/qbs/modules/java/io/qt/qbs/tools/utils/ArtifactProcessor.java create mode 100644 share/qbs/modules/java/io/qt/qbs/tools/utils/ArtifactScanner.java create mode 100644 share/qbs/modules/java/io/qt/qbs/tools/utils/JavaCompilerOptions.java create mode 100644 share/qbs/modules/java/io/qt/qbs/tools/utils/JavaCompilerScanner.java create mode 100644 share/qbs/modules/java/io/qt/qbs/tools/utils/NullFileObject.java create mode 100644 share/qbs/modules/java/utils.js create mode 100644 share/qbs/modules/lex_yacc/lexyacc.js create mode 100644 share/qbs/modules/lex_yacc/lexyacc.qbs create mode 100644 share/qbs/modules/nodejs/NodeJS.qbs create mode 100644 share/qbs/modules/nodejs/nodejs.js create mode 100644 share/qbs/modules/nsis/NSISModule.qbs create mode 100644 share/qbs/modules/qbs/common.qbs create mode 100644 share/qbs/modules/typescript/TypeScriptModule.qbs create mode 100644 share/qbs/modules/typescript/qbs-tsc-scan/.gitignore create mode 100644 share/qbs/modules/typescript/qbs-tsc-scan/qbs-tsc-scan.ts create mode 100644 share/qbs/modules/typescript/typescript.js create mode 100644 share/qbs/modules/wix/WiXModule.qbs create mode 100644 share/qbs/modules/xcode/xcode.js create mode 100644 share/qbs/modules/xcode/xcode.qbs create mode 100644 share/share.qbs create mode 100644 src/app/app.pri create mode 100644 src/app/app.pro create mode 100644 src/app/apps.qbs create mode 100644 src/app/config-ui/Info.plist create mode 100644 src/app/config-ui/commandlineparser.cpp create mode 100644 src/app/config-ui/commandlineparser.h create mode 100644 src/app/config-ui/config-ui.pro create mode 100644 src/app/config-ui/config-ui.qbs create mode 100644 src/app/config-ui/fgapp.mm create mode 100644 src/app/config-ui/main.cpp create mode 100644 src/app/config-ui/mainwindow.cpp create mode 100644 src/app/config-ui/mainwindow.h create mode 100644 src/app/config-ui/mainwindow.ui create mode 100644 src/app/config/config.pro create mode 100644 src/app/config/config.qbs create mode 100644 src/app/config/configcommand.h create mode 100644 src/app/config/configcommandexecutor.cpp create mode 100644 src/app/config/configcommandexecutor.h create mode 100644 src/app/config/configcommandlineparser.cpp create mode 100644 src/app/config/configcommandlineparser.h create mode 100644 src/app/config/configmain.cpp create mode 100644 src/app/qbs-qmltypes/main.cpp create mode 100644 src/app/qbs-qmltypes/qbs-qmltypes.pro create mode 100644 src/app/qbs-qmltypes/qbs-qmltypes.qbs create mode 100644 src/app/qbs-setup-android/android-setup.cpp create mode 100644 src/app/qbs-setup-android/android-setup.h create mode 100644 src/app/qbs-setup-android/commandlineparser.cpp create mode 100644 src/app/qbs-setup-android/commandlineparser.h create mode 100644 src/app/qbs-setup-android/main.cpp create mode 100644 src/app/qbs-setup-android/qbs-setup-android.pro create mode 100644 src/app/qbs-setup-android/qbs-setup-android.qbs create mode 100644 src/app/qbs-setup-qt/commandlineparser.cpp create mode 100644 src/app/qbs-setup-qt/commandlineparser.h create mode 100644 src/app/qbs-setup-qt/main.cpp create mode 100644 src/app/qbs-setup-qt/qbs-setup-qt.exe.manifest create mode 100644 src/app/qbs-setup-qt/qbs-setup-qt.pro create mode 100644 src/app/qbs-setup-qt/qbs-setup-qt.qbs create mode 100644 src/app/qbs-setup-qt/qbs-setup-qt.rc create mode 100644 src/app/qbs-setup-qt/setupqt.cpp create mode 100644 src/app/qbs-setup-qt/setupqt.h create mode 100644 src/app/qbs-setup-toolchains/commandlineparser.cpp create mode 100644 src/app/qbs-setup-toolchains/commandlineparser.h create mode 100644 src/app/qbs-setup-toolchains/main.cpp create mode 100644 src/app/qbs-setup-toolchains/msvcprobe.cpp create mode 100644 src/app/qbs-setup-toolchains/msvcprobe.h create mode 100644 src/app/qbs-setup-toolchains/probe.cpp create mode 100644 src/app/qbs-setup-toolchains/probe.h create mode 100644 src/app/qbs-setup-toolchains/qbs-setup-toolchains.exe.manifest create mode 100644 src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro create mode 100644 src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs create mode 100644 src/app/qbs-setup-toolchains/qbs-setup-toolchains.rc create mode 100644 src/app/qbs-setup-toolchains/xcodeprobe.cpp create mode 100644 src/app/qbs-setup-toolchains/xcodeprobe.h create mode 100644 src/app/qbs/application.cpp create mode 100644 src/app/qbs/application.h create mode 100644 src/app/qbs/commandlinefrontend.cpp create mode 100644 src/app/qbs/commandlinefrontend.h create mode 100644 src/app/qbs/consoleprogressobserver.cpp create mode 100644 src/app/qbs/consoleprogressobserver.h create mode 100644 src/app/qbs/ctrlchandler.cpp create mode 100644 src/app/qbs/ctrlchandler.h create mode 100644 src/app/qbs/main.cpp create mode 100644 src/app/qbs/parser/command.cpp create mode 100644 src/app/qbs/parser/command.h create mode 100644 src/app/qbs/parser/commandlineoption.cpp create mode 100644 src/app/qbs/parser/commandlineoption.h create mode 100644 src/app/qbs/parser/commandlineoptionpool.cpp create mode 100644 src/app/qbs/parser/commandlineoptionpool.h create mode 100644 src/app/qbs/parser/commandlineparser.cpp create mode 100644 src/app/qbs/parser/commandlineparser.h create mode 100644 src/app/qbs/parser/commandpool.cpp create mode 100644 src/app/qbs/parser/commandpool.h create mode 100644 src/app/qbs/parser/commandtype.h create mode 100644 src/app/qbs/parser/parser.pri create mode 100644 src/app/qbs/qbs.pro create mode 100644 src/app/qbs/qbs.qbs create mode 100644 src/app/qbs/qbstool.cpp create mode 100644 src/app/qbs/qbstool.h create mode 100644 src/app/qbs/status.cpp create mode 100644 src/app/qbs/status.h create mode 100644 src/app/shared/logging/coloredoutput.cpp create mode 100644 src/app/shared/logging/coloredoutput.h create mode 100644 src/app/shared/logging/consolelogger.cpp create mode 100644 src/app/shared/logging/consolelogger.h create mode 100644 src/app/shared/logging/logging.pri create mode 100644 src/install_prefix.pri create mode 100644 src/lib/corelib/api/api.pri create mode 100644 src/lib/corelib/api/changeset.cpp create mode 100644 src/lib/corelib/api/changeset.h create mode 100644 src/lib/corelib/api/internaljobs.cpp create mode 100644 src/lib/corelib/api/internaljobs.h create mode 100644 src/lib/corelib/api/jobs.cpp create mode 100644 src/lib/corelib/api/jobs.h create mode 100644 src/lib/corelib/api/languageinfo.cpp create mode 100644 src/lib/corelib/api/languageinfo.h create mode 100644 src/lib/corelib/api/project.cpp create mode 100644 src/lib/corelib/api/project.h create mode 100644 src/lib/corelib/api/project_p.h create mode 100644 src/lib/corelib/api/projectdata.cpp create mode 100644 src/lib/corelib/api/projectdata.h create mode 100644 src/lib/corelib/api/projectdata_p.h create mode 100644 src/lib/corelib/api/projectfileupdater.cpp create mode 100644 src/lib/corelib/api/projectfileupdater.h create mode 100644 src/lib/corelib/api/propertymap_p.h create mode 100644 src/lib/corelib/api/qmljsrewriter.cpp create mode 100644 src/lib/corelib/api/qmljsrewriter.h create mode 100644 src/lib/corelib/api/rulecommand.cpp create mode 100644 src/lib/corelib/api/rulecommand.h create mode 100644 src/lib/corelib/api/rulecommand_p.h create mode 100644 src/lib/corelib/api/runenvironment.cpp create mode 100644 src/lib/corelib/api/runenvironment.h create mode 100644 src/lib/corelib/buildgraph/abstractcommandexecutor.cpp create mode 100644 src/lib/corelib/buildgraph/abstractcommandexecutor.h create mode 100644 src/lib/corelib/buildgraph/artifact.cpp create mode 100644 src/lib/corelib/buildgraph/artifact.h create mode 100644 src/lib/corelib/buildgraph/artifactcleaner.cpp create mode 100644 src/lib/corelib/buildgraph/artifactcleaner.h create mode 100644 src/lib/corelib/buildgraph/artifactset.cpp create mode 100644 src/lib/corelib/buildgraph/artifactset.h create mode 100644 src/lib/corelib/buildgraph/artifactvisitor.cpp create mode 100644 src/lib/corelib/buildgraph/artifactvisitor.h create mode 100644 src/lib/corelib/buildgraph/buildgraph.cpp create mode 100644 src/lib/corelib/buildgraph/buildgraph.h create mode 100644 src/lib/corelib/buildgraph/buildgraph.pri create mode 100644 src/lib/corelib/buildgraph/buildgraphloader.cpp create mode 100644 src/lib/corelib/buildgraph/buildgraphloader.h create mode 100644 src/lib/corelib/buildgraph/buildgraphnode.cpp create mode 100644 src/lib/corelib/buildgraph/buildgraphnode.h create mode 100644 src/lib/corelib/buildgraph/buildgraphvisitor.h create mode 100644 src/lib/corelib/buildgraph/command.cpp create mode 100644 src/lib/corelib/buildgraph/command.h create mode 100644 src/lib/corelib/buildgraph/cycledetector.cpp create mode 100644 src/lib/corelib/buildgraph/cycledetector.h create mode 100644 src/lib/corelib/buildgraph/depscanner.cpp create mode 100644 src/lib/corelib/buildgraph/depscanner.h create mode 100644 src/lib/corelib/buildgraph/emptydirectoriesremover.cpp create mode 100644 src/lib/corelib/buildgraph/emptydirectoriesremover.h create mode 100644 src/lib/corelib/buildgraph/executor.cpp create mode 100644 src/lib/corelib/buildgraph/executor.h create mode 100644 src/lib/corelib/buildgraph/executorjob.cpp create mode 100644 src/lib/corelib/buildgraph/executorjob.h create mode 100644 src/lib/corelib/buildgraph/filedependency.cpp create mode 100644 src/lib/corelib/buildgraph/filedependency.h create mode 100644 src/lib/corelib/buildgraph/forward_decls.h create mode 100644 src/lib/corelib/buildgraph/inputartifactscanner.cpp create mode 100644 src/lib/corelib/buildgraph/inputartifactscanner.h create mode 100644 src/lib/corelib/buildgraph/jscommandexecutor.cpp create mode 100644 src/lib/corelib/buildgraph/jscommandexecutor.h create mode 100644 src/lib/corelib/buildgraph/nodeset.cpp create mode 100644 src/lib/corelib/buildgraph/nodeset.h create mode 100644 src/lib/corelib/buildgraph/nodetreedumper.cpp create mode 100644 src/lib/corelib/buildgraph/nodetreedumper.h create mode 100644 src/lib/corelib/buildgraph/processcommandexecutor.cpp create mode 100644 src/lib/corelib/buildgraph/processcommandexecutor.h create mode 100644 src/lib/corelib/buildgraph/productbuilddata.cpp create mode 100644 src/lib/corelib/buildgraph/productbuilddata.h create mode 100644 src/lib/corelib/buildgraph/productinstaller.cpp create mode 100644 src/lib/corelib/buildgraph/productinstaller.h create mode 100644 src/lib/corelib/buildgraph/projectbuilddata.cpp create mode 100644 src/lib/corelib/buildgraph/projectbuilddata.h create mode 100644 src/lib/corelib/buildgraph/qtmocscanner.cpp create mode 100644 src/lib/corelib/buildgraph/qtmocscanner.h create mode 100644 src/lib/corelib/buildgraph/rescuableartifactdata.cpp create mode 100644 src/lib/corelib/buildgraph/rescuableartifactdata.h create mode 100644 src/lib/corelib/buildgraph/rulegraph.cpp create mode 100644 src/lib/corelib/buildgraph/rulegraph.h create mode 100644 src/lib/corelib/buildgraph/rulenode.cpp create mode 100644 src/lib/corelib/buildgraph/rulenode.h create mode 100644 src/lib/corelib/buildgraph/rulesapplicator.cpp create mode 100644 src/lib/corelib/buildgraph/rulesapplicator.h create mode 100644 src/lib/corelib/buildgraph/rulesevaluationcontext.cpp create mode 100644 src/lib/corelib/buildgraph/rulesevaluationcontext.h create mode 100644 src/lib/corelib/buildgraph/scanresultcache.cpp create mode 100644 src/lib/corelib/buildgraph/scanresultcache.h create mode 100644 src/lib/corelib/buildgraph/timestampsupdater.cpp create mode 100644 src/lib/corelib/buildgraph/timestampsupdater.h create mode 100644 src/lib/corelib/buildgraph/transformer.cpp create mode 100644 src/lib/corelib/buildgraph/transformer.h create mode 100644 src/lib/corelib/buildgraph/tst_buildgraph.cpp create mode 100644 src/lib/corelib/buildgraph/tst_buildgraph.h create mode 100644 src/lib/corelib/corelib.pro create mode 100644 src/lib/corelib/corelib.qbs create mode 100644 src/lib/corelib/generators/clangcompilationdb/clangcompilationdb.pri create mode 100644 src/lib/corelib/generators/clangcompilationdb/clangcompilationdb.qbs create mode 100644 src/lib/corelib/generators/clangcompilationdb/clangcompilationdbgenerator.cpp create mode 100644 src/lib/corelib/generators/clangcompilationdb/clangcompilationdbgenerator.h create mode 100644 src/lib/corelib/generators/generatableprojectiterator.cpp create mode 100644 src/lib/corelib/generators/generatableprojectiterator.h create mode 100644 src/lib/corelib/generators/generator.cpp create mode 100644 src/lib/corelib/generators/generator.h create mode 100644 src/lib/corelib/generators/generatordata.cpp create mode 100644 src/lib/corelib/generators/generatordata.h create mode 100644 src/lib/corelib/generators/generators.pri create mode 100644 src/lib/corelib/generators/generators.qbs create mode 100644 src/lib/corelib/generators/igeneratableprojectvisitor.h create mode 100644 src/lib/corelib/generators/visualstudio/io/msbuildprojectwriter.cpp create mode 100644 src/lib/corelib/generators/visualstudio/io/msbuildprojectwriter.h create mode 100644 src/lib/corelib/generators/visualstudio/io/visualstudiosolutionwriter.cpp create mode 100644 src/lib/corelib/generators/visualstudio/io/visualstudiosolutionwriter.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/imsbuildgroup.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/imsbuildgroup.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/imsbuildnode.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/imsbuildnode.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/imsbuildnodevisitor.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/imsbuildproperty.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/imsbuildproperty.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclcompile.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclcompile.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclinclude.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclinclude.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfileitem.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfileitem.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfilter.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfilter.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildlink.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildlink.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildnone.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/items/msbuildnone.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildimport.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildimport.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildimportgroup.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildimportgroup.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuilditem.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuilditem.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuilditemdefinitiongroup.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuilditemdefinitiongroup.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuilditemgroup.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuilditemgroup.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuilditemmetadata.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuilditemmetadata.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildproject.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildproperty.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildproperty.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildpropertygroup.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuild/msbuildpropertygroup.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuildfiltersproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuildfiltersproject.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuildqbsgenerateproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuildqbsgenerateproject.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuildqbsproductproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuildqbsproductproject.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuildsharedsolutionpropertiesproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuildsharedsolutionpropertiesproject.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuildsolutionpropertiesproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuildsolutionpropertiesproject.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuildtargetproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/msbuildtargetproject.h create mode 100644 src/lib/corelib/generators/visualstudio/msbuildutils.h create mode 100644 src/lib/corelib/generators/visualstudio/solution/ivisualstudiosolutionproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/solution/ivisualstudiosolutionproject.h create mode 100644 src/lib/corelib/generators/visualstudio/solution/visualstudiosolution.cpp create mode 100644 src/lib/corelib/generators/visualstudio/solution/visualstudiosolution.h create mode 100644 src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfileproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfileproject.h create mode 100644 src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfolderproject.cpp create mode 100644 src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfolderproject.h create mode 100644 src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionglobalsection.cpp create mode 100644 src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionglobalsection.h create mode 100644 src/lib/corelib/generators/visualstudio/visualstudio.pri create mode 100644 src/lib/corelib/generators/visualstudio/visualstudio.qbs create mode 100644 src/lib/corelib/generators/visualstudio/visualstudiogenerator.cpp create mode 100644 src/lib/corelib/generators/visualstudio/visualstudiogenerator.h create mode 100644 src/lib/corelib/generators/visualstudio/visualstudioguidpool.cpp create mode 100644 src/lib/corelib/generators/visualstudio/visualstudioguidpool.h create mode 100644 src/lib/corelib/jsextensions/domxml.cpp create mode 100644 src/lib/corelib/jsextensions/domxml.h create mode 100644 src/lib/corelib/jsextensions/environmentextension.cpp create mode 100644 src/lib/corelib/jsextensions/environmentextension.h create mode 100644 src/lib/corelib/jsextensions/file.cpp create mode 100644 src/lib/corelib/jsextensions/file.h create mode 100644 src/lib/corelib/jsextensions/fileinfoextension.cpp create mode 100644 src/lib/corelib/jsextensions/fileinfoextension.h create mode 100644 src/lib/corelib/jsextensions/jsextensions.cpp create mode 100644 src/lib/corelib/jsextensions/jsextensions.h create mode 100644 src/lib/corelib/jsextensions/jsextensions.pri create mode 100644 src/lib/corelib/jsextensions/moduleproperties.cpp create mode 100644 src/lib/corelib/jsextensions/moduleproperties.h create mode 100644 src/lib/corelib/jsextensions/process.cpp create mode 100644 src/lib/corelib/jsextensions/process.h create mode 100644 src/lib/corelib/jsextensions/propertylist.h create mode 100644 src/lib/corelib/jsextensions/propertylist.mm create mode 100644 src/lib/corelib/jsextensions/propertylistutils.h create mode 100644 src/lib/corelib/jsextensions/propertylistutils.mm create mode 100644 src/lib/corelib/jsextensions/temporarydir.cpp create mode 100644 src/lib/corelib/jsextensions/temporarydir.h create mode 100644 src/lib/corelib/jsextensions/textfile.cpp create mode 100644 src/lib/corelib/jsextensions/textfile.h create mode 100644 src/lib/corelib/jsextensions/utilitiesextension.cpp create mode 100644 src/lib/corelib/jsextensions/utilitiesextension.h create mode 100644 src/lib/corelib/language/artifactproperties.cpp create mode 100644 src/lib/corelib/language/artifactproperties.h create mode 100644 src/lib/corelib/language/astimportshandler.cpp create mode 100644 src/lib/corelib/language/astimportshandler.h create mode 100644 src/lib/corelib/language/astpropertiesitemhandler.cpp create mode 100644 src/lib/corelib/language/astpropertiesitemhandler.h create mode 100644 src/lib/corelib/language/asttools.cpp create mode 100644 src/lib/corelib/language/asttools.h create mode 100644 src/lib/corelib/language/builtindeclarations.cpp create mode 100644 src/lib/corelib/language/builtindeclarations.h create mode 100644 src/lib/corelib/language/deprecationinfo.h create mode 100644 src/lib/corelib/language/evaluationdata.h create mode 100644 src/lib/corelib/language/evaluator.cpp create mode 100644 src/lib/corelib/language/evaluator.h create mode 100644 src/lib/corelib/language/evaluatorscriptclass.cpp create mode 100644 src/lib/corelib/language/evaluatorscriptclass.h create mode 100644 src/lib/corelib/language/filecontext.cpp create mode 100644 src/lib/corelib/language/filecontext.h create mode 100644 src/lib/corelib/language/filecontextbase.cpp create mode 100644 src/lib/corelib/language/filecontextbase.h create mode 100644 src/lib/corelib/language/filetags.cpp create mode 100644 src/lib/corelib/language/filetags.h create mode 100644 src/lib/corelib/language/forward_decls.h create mode 100644 src/lib/corelib/language/functiondeclaration.h create mode 100644 src/lib/corelib/language/identifiersearch.cpp create mode 100644 src/lib/corelib/language/identifiersearch.h create mode 100644 src/lib/corelib/language/item.cpp create mode 100644 src/lib/corelib/language/item.h create mode 100644 src/lib/corelib/language/itemdeclaration.cpp create mode 100644 src/lib/corelib/language/itemdeclaration.h create mode 100644 src/lib/corelib/language/itemobserver.h create mode 100644 src/lib/corelib/language/itempool.cpp create mode 100644 src/lib/corelib/language/itempool.h create mode 100644 src/lib/corelib/language/itemreader.cpp create mode 100644 src/lib/corelib/language/itemreader.h create mode 100644 src/lib/corelib/language/itemreaderastvisitor.cpp create mode 100644 src/lib/corelib/language/itemreaderastvisitor.h create mode 100644 src/lib/corelib/language/itemreadervisitorstate.cpp create mode 100644 src/lib/corelib/language/itemreadervisitorstate.h create mode 100644 src/lib/corelib/language/itemtype.h create mode 100644 src/lib/corelib/language/jsimports.h create mode 100644 src/lib/corelib/language/language.cpp create mode 100644 src/lib/corelib/language/language.h create mode 100644 src/lib/corelib/language/language.pri create mode 100644 src/lib/corelib/language/loader.cpp create mode 100644 src/lib/corelib/language/loader.h create mode 100644 src/lib/corelib/language/moduleloader.cpp create mode 100644 src/lib/corelib/language/moduleloader.h create mode 100644 src/lib/corelib/language/modulemerger.cpp create mode 100644 src/lib/corelib/language/modulemerger.h create mode 100644 src/lib/corelib/language/preparescriptobserver.cpp create mode 100644 src/lib/corelib/language/preparescriptobserver.h create mode 100644 src/lib/corelib/language/projectresolver.cpp create mode 100644 src/lib/corelib/language/projectresolver.h create mode 100644 src/lib/corelib/language/property.cpp create mode 100644 src/lib/corelib/language/property.h create mode 100644 src/lib/corelib/language/propertydeclaration.cpp create mode 100644 src/lib/corelib/language/propertydeclaration.h create mode 100644 src/lib/corelib/language/propertymapinternal.cpp create mode 100644 src/lib/corelib/language/propertymapinternal.h create mode 100644 src/lib/corelib/language/qualifiedid.cpp create mode 100644 src/lib/corelib/language/qualifiedid.h create mode 100644 src/lib/corelib/language/resolvedfilecontext.cpp create mode 100644 src/lib/corelib/language/resolvedfilecontext.h create mode 100644 src/lib/corelib/language/scriptengine.cpp create mode 100644 src/lib/corelib/language/scriptengine.h create mode 100644 src/lib/corelib/language/scriptimporter.cpp create mode 100644 src/lib/corelib/language/scriptimporter.h create mode 100644 src/lib/corelib/language/scriptpropertyobserver.h create mode 100644 src/lib/corelib/language/testdata/Banana create mode 100644 src/lib/corelib/language/testdata/MyProperties.qbs create mode 100644 src/lib/corelib/language/testdata/ParentWithExport.qbs create mode 100644 src/lib/corelib/language/testdata/aboutdialog.cpp create mode 100644 src/lib/corelib/language/testdata/base-validate/base-validate.qbs create mode 100644 src/lib/corelib/language/testdata/base-validate/modules/m/MParent.qbs create mode 100644 src/lib/corelib/language/testdata/base-validate/modules/m/m.qbs create mode 100644 src/lib/corelib/language/testdata/baseproperty.qbs create mode 100644 src/lib/corelib/language/testdata/baseproperty_base.qbs create mode 100644 src/lib/corelib/language/testdata/buildconfigstringlistsyntax.qbs create mode 100644 src/lib/corelib/language/testdata/builtinFunctionInSearchPathsProperty.qbs create mode 100644 src/lib/corelib/language/testdata/canonicalArchitecture.qbs create mode 100644 src/lib/corelib/language/testdata/conditionaldepends.qbs create mode 100644 src/lib/corelib/language/testdata/conditionaldepends_base.qbs create mode 100644 src/lib/corelib/language/testdata/defaultvalue/egon.qbs create mode 100644 src/lib/corelib/language/testdata/defaultvalue/modules/higher/higher.qbs create mode 100644 src/lib/corelib/language/testdata/defaultvalue/modules/lower/lower.qbs create mode 100644 src/lib/corelib/language/testdata/defaultvalue/test.txt create mode 100644 src/lib/corelib/language/testdata/dependencyOnAllProfiles.qbs create mode 100644 src/lib/corelib/language/testdata/derived-sub-project/DerivedSubProject.qbs create mode 100644 src/lib/corelib/language/testdata/derived-sub-project/project.qbs create mode 100644 src/lib/corelib/language/testdata/derived-sub-project/subproject.qbs create mode 100644 src/lib/corelib/language/testdata/drawline.asm create mode 100644 src/lib/corelib/language/testdata/dummy.txt create mode 100644 src/lib/corelib/language/testdata/environmentvariable.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/ParentItem.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/ParentWithExport.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/conflicting-properties-in-export-items.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/dependency_cycle.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/dependency_cycle2.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/dependency_cycle3.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/dependency_cycle4.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/duplicate_sources.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/duplicate_sources_wildcards.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/importloop1.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/importloop2.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/invalid-property-option.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/invalid_child_item_type.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/invalid_file.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/invalid_property_type.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/invalid_stringlist_element.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/main.cpp create mode 100644 src/lib/corelib/language/testdata/erroneous/misused-inherited-property.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/modules/module-a/module-a.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/modules/module-b/module-b.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/modules/module-with-wrong-property-option/m.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/modules/prefix1/prefix1.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/modules/prefix1/suffix/suffix.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/modules/prefix2/prefix2.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/modules/prefix2/suffix/suffix.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/multiple_exports.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/multiple_properties_in_subproject.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/nonexistentouter.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/oldQbsVersion.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/properties-item-with-invalid-condition.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/references_cycle.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/references_cycle2.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/references_cycle3.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/reserved_name_in_import.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/same-module-prefix1.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/same-module-prefix2.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/subproject_cycle.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/subproject_cycle2.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/subproject_cycle3.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/throw_in_property_binding.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/undeclared_item.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/undeclared_property.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/undeclared_property_in_Properties_item.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item2.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item3.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/undeclared_property_wrapper.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/undefined_stringlist_element.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/unknown_item_type.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/unknown_module.qbs create mode 100644 src/lib/corelib/language/testdata/erroneous/wrongQbsVersionFormat.qbs create mode 100644 src/lib/corelib/language/testdata/exports.qbs create mode 100644 src/lib/corelib/language/testdata/exports_product.qbs create mode 100644 src/lib/corelib/language/testdata/filecontextproperties.qbs create mode 100644 src/lib/corelib/language/testdata/filetags.qbs create mode 100644 src/lib/corelib/language/testdata/getNativeSetting.qbs create mode 100644 src/lib/corelib/language/testdata/groupconditions.qbs create mode 100644 src/lib/corelib/language/testdata/groupname.qbs create mode 100644 src/lib/corelib/language/testdata/homeDirectory.qbs create mode 100644 src/lib/corelib/language/testdata/id-uniqueness.qbs create mode 100644 src/lib/corelib/language/testdata/idusage.qbs create mode 100644 src/lib/corelib/language/testdata/idusagebase.qbs create mode 100644 src/lib/corelib/language/testdata/import-collection/collection/file1.js create mode 100644 src/lib/corelib/language/testdata/import-collection/collection/file2.js create mode 100644 src/lib/corelib/language/testdata/import-collection/imports/Collection/file1.js create mode 100644 src/lib/corelib/language/testdata/import-collection/imports/Collection/file2.js create mode 100644 src/lib/corelib/language/testdata/import-collection/product.qbs create mode 100644 src/lib/corelib/language/testdata/import-collection/project.qbs create mode 100644 src/lib/corelib/language/testdata/invalidBindingInDisabledItem.qbs create mode 100644 src/lib/corelib/language/testdata/jsextensions.js create mode 100644 src/lib/corelib/language/testdata/jsimportsinmultiplescopes.js create mode 100644 src/lib/corelib/language/testdata/jsimportsinmultiplescopes.qbs create mode 100644 src/lib/corelib/language/testdata/main.cpp create mode 100644 src/lib/corelib/language/testdata/moduleproperties.qbs create mode 100644 src/lib/corelib/language/testdata/modulepropertiesingroups.qbs create mode 100644 src/lib/corelib/language/testdata/modules.qbs create mode 100644 src/lib/corelib/language/testdata/modules/deepdummy/deep/moat/dummydeepmoat.qbs create mode 100644 src/lib/corelib/language/testdata/modules/dummy/dummy.qbs create mode 100644 src/lib/corelib/language/testdata/modules/dummy/dummy_base.qbs create mode 100644 src/lib/corelib/language/testdata/modules/dummy2/dummy2.qbs create mode 100644 src/lib/corelib/language/testdata/modules/dummyqt/core/dummycore.qbs create mode 100644 src/lib/corelib/language/testdata/modules/dummyqt/gui/dummygui.qbs create mode 100644 src/lib/corelib/language/testdata/modules/dummyqt/network/dummynetwork.qbs create mode 100644 src/lib/corelib/language/testdata/modules/gmod/gmod1/gmod1.qbs create mode 100644 src/lib/corelib/language/testdata/modules/gmod2/gmod2.qbs create mode 100644 src/lib/corelib/language/testdata/modules/gmod3/qmod3.qbs create mode 100644 src/lib/corelib/language/testdata/modules/gmod4/gmod4.qbs create mode 100644 src/lib/corelib/language/testdata/modules/scopemod/scopemod.qbs create mode 100644 src/lib/corelib/language/testdata/modulescope.qbs create mode 100644 src/lib/corelib/language/testdata/modulescope_base.qbs create mode 100644 src/lib/corelib/language/testdata/narf create mode 100644 src/lib/corelib/language/testdata/narf.zort create mode 100644 src/lib/corelib/language/testdata/nativesettings.ini create mode 100644 src/lib/corelib/language/testdata/non-required-products.qbs create mode 100644 src/lib/corelib/language/testdata/outerInGroup.qbs create mode 100644 src/lib/corelib/language/testdata/pathproperties.qbs create mode 100644 src/lib/corelib/language/testdata/productconditions.qbs create mode 100644 src/lib/corelib/language/testdata/productdirectories.qbs create mode 100644 src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs create mode 100644 src/lib/corelib/language/testdata/properties-block-in-group.qbs create mode 100644 src/lib/corelib/language/testdata/propertiesblocks.qbs create mode 100644 src/lib/corelib/language/testdata/propertiesblocks_base.qbs create mode 100644 src/lib/corelib/language/testdata/qbs-properties-in-project-condition.qbs create mode 100644 src/lib/corelib/language/testdata/recursive-dependencies/recursive-dependencies.qbs create mode 100644 src/lib/corelib/language/testdata/relaxed-error-mode/file1.txt create mode 100644 src/lib/corelib/language/testdata/relaxed-error-mode/file2.txt create mode 100644 src/lib/corelib/language/testdata/relaxed-error-mode/relaxed-error-mode.qbs create mode 100644 src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/complicated.qbs create mode 100644 src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/dependency-via-export.qbs create mode 100644 src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/dependency-via-module.qbs create mode 100644 src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/direct-dependencies.qbs create mode 100644 src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/modules/failing-validation-indirect/failing-validation-indirect.qbs create mode 100644 src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/modules/failing-validation/failing-validation.qbs create mode 100644 src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-export-indirect.qbs create mode 100644 src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-export.qbs create mode 100644 src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-module.qbs create mode 100644 src/lib/corelib/language/testdata/rfc1034identifier.qbs create mode 100644 src/lib/corelib/language/testdata/subdir/exports-mylib.qbs create mode 100644 src/lib/corelib/language/testdata/subdir/pathproperties_base.qbs create mode 100644 src/lib/corelib/language/testdata/subdir2/exports-mylib2.qbs create mode 100644 src/lib/corelib/language/testdata/throwing-probe.qbs create mode 100644 src/lib/corelib/language/testdata/versionCompare.qbs create mode 100644 src/lib/corelib/language/testdata/zort create mode 100644 src/lib/corelib/language/tst_language.cpp create mode 100644 src/lib/corelib/language/tst_language.h create mode 100644 src/lib/corelib/language/value.cpp create mode 100644 src/lib/corelib/language/value.h create mode 100644 src/lib/corelib/logging/ilogsink.cpp create mode 100644 src/lib/corelib/logging/ilogsink.h create mode 100644 src/lib/corelib/logging/logger.cpp create mode 100644 src/lib/corelib/logging/logger.h create mode 100644 src/lib/corelib/logging/logging.pri create mode 100644 src/lib/corelib/logging/translator.h create mode 100644 src/lib/corelib/parser/parser.pri create mode 100644 src/lib/corelib/parser/qmlerror.cpp create mode 100644 src/lib/corelib/parser/qmlerror.h create mode 100644 src/lib/corelib/parser/qmljs.g create mode 100644 src/lib/corelib/parser/qmljsast.cpp create mode 100644 src/lib/corelib/parser/qmljsast_p.h create mode 100644 src/lib/corelib/parser/qmljsastfwd_p.h create mode 100644 src/lib/corelib/parser/qmljsastvisitor.cpp create mode 100644 src/lib/corelib/parser/qmljsastvisitor_p.h create mode 100644 src/lib/corelib/parser/qmljsengine_p.cpp create mode 100644 src/lib/corelib/parser/qmljsengine_p.h create mode 100644 src/lib/corelib/parser/qmljsglobal_p.h create mode 100644 src/lib/corelib/parser/qmljsgrammar.cpp create mode 100644 src/lib/corelib/parser/qmljsgrammar_p.h create mode 100644 src/lib/corelib/parser/qmljskeywords_p.h create mode 100644 src/lib/corelib/parser/qmljslexer.cpp create mode 100644 src/lib/corelib/parser/qmljslexer_p.h create mode 100644 src/lib/corelib/parser/qmljsmemorypool_p.h create mode 100644 src/lib/corelib/parser/qmljsparser.cpp create mode 100644 src/lib/corelib/parser/qmljsparser_p.h create mode 100644 src/lib/corelib/qbs.h create mode 100644 src/lib/corelib/tools/applecodesignutils.cpp create mode 100644 src/lib/corelib/tools/applecodesignutils.h create mode 100644 src/lib/corelib/tools/architectures.cpp create mode 100644 src/lib/corelib/tools/architectures.h create mode 100644 src/lib/corelib/tools/buildgraphlocker.cpp create mode 100644 src/lib/corelib/tools/buildgraphlocker.h create mode 100644 src/lib/corelib/tools/buildoptions.cpp create mode 100644 src/lib/corelib/tools/buildoptions.h create mode 100644 src/lib/corelib/tools/cleanoptions.cpp create mode 100644 src/lib/corelib/tools/cleanoptions.h create mode 100644 src/lib/corelib/tools/codelocation.cpp create mode 100644 src/lib/corelib/tools/codelocation.h create mode 100644 src/lib/corelib/tools/commandechomode.cpp create mode 100644 src/lib/corelib/tools/commandechomode.h create mode 100644 src/lib/corelib/tools/error.cpp create mode 100644 src/lib/corelib/tools/error.h create mode 100644 src/lib/corelib/tools/executablefinder.cpp create mode 100644 src/lib/corelib/tools/executablefinder.h create mode 100644 src/lib/corelib/tools/fileinfo.cpp create mode 100644 src/lib/corelib/tools/fileinfo.h create mode 100644 src/lib/corelib/tools/filesaver.cpp create mode 100644 src/lib/corelib/tools/filesaver.h create mode 100644 src/lib/corelib/tools/filetime.h create mode 100644 src/lib/corelib/tools/filetime_unix.cpp create mode 100644 src/lib/corelib/tools/filetime_win.cpp create mode 100644 src/lib/corelib/tools/generateoptions.cpp create mode 100644 src/lib/corelib/tools/generateoptions.h create mode 100644 src/lib/corelib/tools/hostosinfo.h create mode 100644 src/lib/corelib/tools/id.cpp create mode 100644 src/lib/corelib/tools/id.h create mode 100644 src/lib/corelib/tools/installoptions.cpp create mode 100644 src/lib/corelib/tools/installoptions.h create mode 100644 src/lib/corelib/tools/jsliterals.cpp create mode 100644 src/lib/corelib/tools/jsliterals.h create mode 100644 src/lib/corelib/tools/msvcinfo.cpp create mode 100644 src/lib/corelib/tools/msvcinfo.h create mode 100644 src/lib/corelib/tools/pathutils.h create mode 100644 src/lib/corelib/tools/persistence.cpp create mode 100644 src/lib/corelib/tools/persistence.h create mode 100644 src/lib/corelib/tools/persistentobject.h create mode 100644 src/lib/corelib/tools/preferences.cpp create mode 100644 src/lib/corelib/tools/preferences.h create mode 100644 src/lib/corelib/tools/processresult.cpp create mode 100644 src/lib/corelib/tools/processresult.h create mode 100644 src/lib/corelib/tools/processresult_p.h create mode 100644 src/lib/corelib/tools/processutils.cpp create mode 100644 src/lib/corelib/tools/processutils.h create mode 100644 src/lib/corelib/tools/profile.cpp create mode 100644 src/lib/corelib/tools/profile.h create mode 100644 src/lib/corelib/tools/profiling.cpp create mode 100644 src/lib/corelib/tools/profiling.h create mode 100644 src/lib/corelib/tools/progressobserver.cpp create mode 100644 src/lib/corelib/tools/progressobserver.h create mode 100644 src/lib/corelib/tools/projectgeneratormanager.cpp create mode 100644 src/lib/corelib/tools/projectgeneratormanager.h create mode 100644 src/lib/corelib/tools/propertyfinder.cpp create mode 100644 src/lib/corelib/tools/propertyfinder.h create mode 100644 src/lib/corelib/tools/qbs_export.h create mode 100644 src/lib/corelib/tools/qbsassert.cpp create mode 100644 src/lib/corelib/tools/qbsassert.h create mode 100644 src/lib/corelib/tools/qttools.cpp create mode 100644 src/lib/corelib/tools/qttools.h create mode 100644 src/lib/corelib/tools/scannerpluginmanager.cpp create mode 100644 src/lib/corelib/tools/scannerpluginmanager.h create mode 100644 src/lib/corelib/tools/scripttools.cpp create mode 100644 src/lib/corelib/tools/scripttools.h create mode 100644 src/lib/corelib/tools/settings.cpp create mode 100644 src/lib/corelib/tools/settings.h create mode 100644 src/lib/corelib/tools/settingscreator.cpp create mode 100644 src/lib/corelib/tools/settingscreator.h create mode 100644 src/lib/corelib/tools/settingsmodel.cpp create mode 100644 src/lib/corelib/tools/settingsmodel.h create mode 100644 src/lib/corelib/tools/setupprojectparameters.cpp create mode 100644 src/lib/corelib/tools/setupprojectparameters.h create mode 100644 src/lib/corelib/tools/shellutils.cpp create mode 100644 src/lib/corelib/tools/shellutils.h create mode 100644 src/lib/corelib/tools/toolchains.cpp create mode 100644 src/lib/corelib/tools/toolchains.h create mode 100644 src/lib/corelib/tools/tools.pri create mode 100644 src/lib/corelib/tools/tst_tools.cpp create mode 100644 src/lib/corelib/tools/tst_tools.h create mode 100644 src/lib/corelib/tools/version.cpp create mode 100644 src/lib/corelib/tools/version.h create mode 100644 src/lib/corelib/tools/visualstudioversioninfo.cpp create mode 100644 src/lib/corelib/tools/visualstudioversioninfo.h create mode 100644 src/lib/corelib/tools/vsenvironmentdetector.cpp create mode 100644 src/lib/corelib/tools/vsenvironmentdetector.h create mode 100644 src/lib/corelib/tools/weakpointer.h create mode 100644 src/lib/corelib/use_corelib.pri create mode 100644 src/lib/corelib/use_installed_corelib.pri create mode 100644 src/lib/library.pri create mode 100644 src/lib/libs.qbs create mode 100644 src/lib/qtprofilesetup/qtenvironment.h create mode 100644 src/lib/qtprofilesetup/qtmoduleinfo.cpp create mode 100644 src/lib/qtprofilesetup/qtmoduleinfo.h create mode 100644 src/lib/qtprofilesetup/qtprofilesetup.cpp create mode 100644 src/lib/qtprofilesetup/qtprofilesetup.h create mode 100644 src/lib/qtprofilesetup/qtprofilesetup.pro create mode 100644 src/lib/qtprofilesetup/qtprofilesetup.qbs create mode 100644 src/lib/qtprofilesetup/templates.qrc create mode 100644 src/lib/qtprofilesetup/templates/QtModule.qbs create mode 100644 src/lib/qtprofilesetup/templates/QtPlugin.qbs create mode 100644 src/lib/qtprofilesetup/templates/core.qbs create mode 100644 src/lib/qtprofilesetup/templates/dbus.js create mode 100644 src/lib/qtprofilesetup/templates/dbus.qbs create mode 100644 src/lib/qtprofilesetup/templates/gui.qbs create mode 100644 src/lib/qtprofilesetup/templates/moc.js create mode 100644 src/lib/qtprofilesetup/templates/module.qbs create mode 100644 src/lib/qtprofilesetup/templates/phonon.qbs create mode 100644 src/lib/qtprofilesetup/templates/plugin.qbs create mode 100644 src/lib/qtprofilesetup/templates/qdoc.js create mode 100644 src/lib/qtprofilesetup/templates/scxml.qbs create mode 100644 src/lib/qtprofilesetup/use_installed_qtprofilesetup.pri create mode 100644 src/lib/qtprofilesetup/use_qtprofilesetup.pri create mode 100644 src/libexec/libexec.pri create mode 100644 src/libexec/libexec.pro create mode 100644 src/libexec/libexec.qbs create mode 100644 src/library_dirname.pri create mode 100644 src/plugins/plugins.pri create mode 100644 src/plugins/plugins.pro create mode 100644 src/plugins/plugins.qbs create mode 100644 src/plugins/scanner/cpp/CPlusPlusForwardDeclarations.h create mode 100644 src/plugins/scanner/cpp/Lexer.cpp create mode 100644 src/plugins/scanner/cpp/Lexer.h create mode 100644 src/plugins/scanner/cpp/Token.cpp create mode 100644 src/plugins/scanner/cpp/Token.h create mode 100644 src/plugins/scanner/cpp/cpp.pro create mode 100644 src/plugins/scanner/cpp/cpp.qbs create mode 100644 src/plugins/scanner/cpp/cpp_global.h create mode 100644 src/plugins/scanner/cpp/cppscanner.cpp create mode 100644 src/plugins/scanner/qt/qt.pro create mode 100644 src/plugins/scanner/qt/qt.qbs create mode 100644 src/plugins/scanner/qt/qtscanner.cpp create mode 100644 src/plugins/scanner/scanner.h create mode 100644 src/plugins/scanner/scanner.pro create mode 100644 src/plugins/scanner/scannerplugin.qbs create mode 100644 src/src.qbs create mode 100644 static.pro create mode 100644 sync.profile create mode 100644 tests/auto/api/api.pro create mode 100644 tests/auto/api/api.qbs create mode 100644 tests/auto/api/testdata/QBS-728/project.qbs create mode 100644 tests/auto/api/testdata/add-qobject-macro-to-cpp-file/main.cpp create mode 100644 tests/auto/api/testdata/add-qobject-macro-to-cpp-file/object.cpp create mode 100644 tests/auto/api/testdata/add-qobject-macro-to-cpp-file/object.h create mode 100644 tests/auto/api/testdata/add-qobject-macro-to-cpp-file/project.qbs create mode 100644 tests/auto/api/testdata/added-file-persistent/file.cpp create mode 100644 tests/auto/api/testdata/added-file-persistent/main.cpp create mode 100644 tests/auto/api/testdata/added-file-persistent/project.qbs create mode 100644 tests/auto/api/testdata/app-without-sources/a.c create mode 100644 tests/auto/api/testdata/app-without-sources/b.c create mode 100644 tests/auto/api/testdata/app-without-sources/project.qbs create mode 100644 tests/auto/api/testdata/base-properties/imports/Bar.qbs create mode 100644 tests/auto/api/testdata/base-properties/imports/Foo.qbs create mode 100644 tests/auto/api/testdata/base-properties/main.cpp create mode 100644 tests/auto/api/testdata/base-properties/prj.qbs create mode 100644 tests/auto/api/testdata/build-properties-source/main.cpp create mode 100644 tests/auto/api/testdata/build-properties-source/project.qbs create mode 100644 tests/auto/api/testdata/build-single-file/compiled.cpp create mode 100644 tests/auto/api/testdata/build-single-file/ignored1.cpp create mode 100644 tests/auto/api/testdata/build-single-file/ignored2.cpp create mode 100644 tests/auto/api/testdata/build-single-file/project.qbs create mode 100644 tests/auto/api/testdata/buildgraph-locking/project.qbs create mode 100644 tests/auto/api/testdata/change-dependent-lib/change-dependent-lib.qbs create mode 100644 tests/auto/api/testdata/change-dependent-lib/main.cpp create mode 100644 tests/auto/api/testdata/change-dependent-lib/mylib.cpp create mode 100644 tests/auto/api/testdata/check-outputs/foo.txt create mode 100644 tests/auto/api/testdata/check-outputs/project.qbs create mode 100644 tests/auto/api/testdata/codegen/foo.txt create mode 100644 tests/auto/api/testdata/codegen/project.qbs create mode 100644 tests/auto/api/testdata/command-extraction/main.cpp create mode 100644 tests/auto/api/testdata/command-extraction/project.qbs create mode 100644 tests/auto/api/testdata/disabled-product/disabledProduct.qbs create mode 100644 tests/auto/api/testdata/disabled-product/main.cpp create mode 100644 tests/auto/api/testdata/disabled-project/disabled_project.qbs create mode 100644 tests/auto/api/testdata/disabled_install_group/main.cpp create mode 100644 tests/auto/api/testdata/disabled_install_group/project.qbs create mode 100644 tests/auto/api/testdata/duplicate-product-names/explicit.qbs create mode 100644 tests/auto/api/testdata/duplicate-product-names/implicit-indirect.qbs create mode 100644 tests/auto/api/testdata/duplicate-product-names/implicit.qbs create mode 100644 tests/auto/api/testdata/duplicate-product-names/subdir1/subproject.qbs create mode 100644 tests/auto/api/testdata/duplicate-product-names/subdir2/subproject.qbs create mode 100644 tests/auto/api/testdata/dynamic-libs/lib1.cpp create mode 100644 tests/auto/api/testdata/dynamic-libs/lib2.cpp create mode 100644 tests/auto/api/testdata/dynamic-libs/lib3.cpp create mode 100644 tests/auto/api/testdata/dynamic-libs/lib4.cpp create mode 100644 tests/auto/api/testdata/dynamic-libs/lib4.h create mode 100644 tests/auto/api/testdata/dynamic-libs/link_dynamiclib.qbs create mode 100644 tests/auto/api/testdata/dynamic-libs/main.cpp create mode 100644 tests/auto/api/testdata/empty-filetag-list/dontcompilethis.cpp create mode 100644 tests/auto/api/testdata/empty-filetag-list/project.qbs create mode 100644 tests/auto/api/testdata/empty-submodules-list/project.qbs create mode 100644 tests/auto/api/testdata/enable-and-disable-product/main.cpp create mode 100644 tests/auto/api/testdata/enable-and-disable-product/project.qbs create mode 100644 tests/auto/api/testdata/error-in-setup-run-environment/error-in-setup-run-environment.qbs create mode 100644 tests/auto/api/testdata/error-in-setup-run-environment/modules/mymodule/mymodule.qbs create mode 100644 tests/auto/api/testdata/explicitly-depends-on/dependency.txt create mode 100644 tests/auto/api/testdata/explicitly-depends-on/project.qbs create mode 100644 tests/auto/api/testdata/export-item-with-group/main.cpp create mode 100644 tests/auto/api/testdata/export-item-with-group/project.qbs create mode 100644 tests/auto/api/testdata/export-simple/lib1.cpp create mode 100644 tests/auto/api/testdata/export-simple/main.cpp create mode 100644 tests/auto/api/testdata/export-simple/project.qbs create mode 100644 tests/auto/api/testdata/export-with-recursive-depends/main1.cpp create mode 100644 tests/auto/api/testdata/export-with-recursive-depends/main2.cpp create mode 100644 tests/auto/api/testdata/export-with-recursive-depends/modules/module1/module1.qbs create mode 100644 tests/auto/api/testdata/export-with-recursive-depends/modules/module2/module2.qbs create mode 100644 tests/auto/api/testdata/export-with-recursive-depends/project.qbs create mode 100644 tests/auto/api/testdata/file-tagger/bla.txt create mode 100644 tests/auto/api/testdata/file-tagger/moc_cpp.qbs create mode 100644 tests/auto/api/testdata/filetagsfilter_override/InstalledApp.qbs create mode 100644 tests/auto/api/testdata/filetagsfilter_override/main.cpp create mode 100644 tests/auto/api/testdata/filetagsfilter_override/project.qbs create mode 100644 tests/auto/api/testdata/generated-files-list/generated-files-list.qbs create mode 100644 tests/auto/api/testdata/generated-files-list/main.cpp create mode 100644 tests/auto/api/testdata/generated-files-list/mainwindow.cpp create mode 100644 tests/auto/api/testdata/generated-files-list/mainwindow.h create mode 100644 tests/auto/api/testdata/generated-files-list/mainwindow.ui create mode 100644 tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs create mode 100644 tests/auto/api/testdata/infinite-loop-process/infinite-loop.qbs create mode 100644 tests/auto/api/testdata/infinite-loop-process/main.cpp create mode 100644 tests/auto/api/testdata/infinite-loop-resolving/project.qbs create mode 100644 tests/auto/api/testdata/inherit-qbs-search-paths/imports/Foo.qbs create mode 100644 tests/auto/api/testdata/inherit-qbs-search-paths/main.cpp create mode 100644 tests/auto/api/testdata/inherit-qbs-search-paths/prj.qbs create mode 100644 tests/auto/api/testdata/inherit-qbs-search-paths/subdir/modules/bli/m.qbs create mode 100644 tests/auto/api/testdata/inherit-qbs-search-paths/subdir2/modules/bla/m.qbs create mode 100644 tests/auto/api/testdata/installed-artifact/installed_artifact.qbs create mode 100644 tests/auto/api/testdata/installed-artifact/main.cpp create mode 100644 tests/auto/api/testdata/is-runnable/project.qbs create mode 100644 tests/auto/api/testdata/lib-same-source/main.cpp create mode 100644 tests/auto/api/testdata/lib-same-source/project.qbs create mode 100644 tests/auto/api/testdata/link-static-lib/helper1/helper1.cpp create mode 100644 tests/auto/api/testdata/link-static-lib/helper1/helper1.h create mode 100644 tests/auto/api/testdata/link-static-lib/helper2/helper2.cpp create mode 100644 tests/auto/api/testdata/link-static-lib/helper2/helper2.h create mode 100644 tests/auto/api/testdata/link-static-lib/main.cpp create mode 100644 tests/auto/api/testdata/link-static-lib/mystaticlib.cpp create mode 100644 tests/auto/api/testdata/link-static-lib/mystaticlibhelper.cpp create mode 100644 tests/auto/api/testdata/link-static-lib/project.qbs create mode 100644 tests/auto/api/testdata/lots-of-dots/dotty.matrix.ui create mode 100644 tests/auto/api/testdata/lots-of-dots/m.a.i.n.cpp create mode 100644 tests/auto/api/testdata/lots-of-dots/object.narf.cpp create mode 100644 tests/auto/api/testdata/lots-of-dots/object.narf.h create mode 100644 tests/auto/api/testdata/lots-of-dots/polka.dots.qrc create mode 100644 tests/auto/api/testdata/lots-of-dots/project.qbs create mode 100644 tests/auto/api/testdata/missing-qobject-header/main.cpp create mode 100644 tests/auto/api/testdata/missing-qobject-header/myobject.cpp create mode 100644 tests/auto/api/testdata/missing-qobject-header/myobject.h create mode 100644 tests/auto/api/testdata/moc-cpp/bla.cpp create mode 100644 tests/auto/api/testdata/moc-cpp/project.qbs create mode 100644 tests/auto/api/testdata/moc-hpp-included/object.cpp create mode 100644 tests/auto/api/testdata/moc-hpp-included/object.h create mode 100644 tests/auto/api/testdata/moc-hpp-included/object2.h create mode 100644 tests/auto/api/testdata/moc-hpp-included/object2.mm create mode 100644 tests/auto/api/testdata/moc-hpp-included/project.qbs create mode 100644 tests/auto/api/testdata/moc-hpp/object.cpp create mode 100644 tests/auto/api/testdata/moc-hpp/object.h create mode 100644 tests/auto/api/testdata/moc-hpp/project.qbs create mode 100644 tests/auto/api/testdata/multi-arch/host+target.input create mode 100644 tests/auto/api/testdata/multi-arch/host-tool.input create mode 100644 tests/auto/api/testdata/multi-arch/project.qbs create mode 100644 tests/auto/api/testdata/new-output-artifact-in-dependency/lib.cpp create mode 100644 tests/auto/api/testdata/new-output-artifact-in-dependency/main.cpp create mode 100644 tests/auto/api/testdata/new-output-artifact-in-dependency/project.qbs create mode 100644 tests/auto/api/testdata/new-pattern-match/project.qbs create mode 100644 tests/auto/api/testdata/nonexistingprojectproperties/invalidaccessfromproduct.qbs create mode 100644 tests/auto/api/testdata/nonexistingprojectproperties/project.qbs create mode 100644 tests/auto/api/testdata/objc/main.mm create mode 100644 tests/auto/api/testdata/objc/objc.qbs create mode 100644 tests/auto/api/testdata/precompiled-header-dynamic/autogen.h.in create mode 100644 tests/auto/api/testdata/precompiled-header-dynamic/main.cpp create mode 100644 tests/auto/api/testdata/precompiled-header-dynamic/pch.h create mode 100644 tests/auto/api/testdata/precompiled-header-dynamic/project.qbs create mode 100644 tests/auto/api/testdata/precompiled-header-new/main.cpp create mode 100644 tests/auto/api/testdata/precompiled-header-new/myobject.cpp create mode 100644 tests/auto/api/testdata/precompiled-header-new/myobject.h create mode 100644 tests/auto/api/testdata/precompiled-header-new/project.qbs create mode 100644 tests/auto/api/testdata/precompiled-header-new/stable.h create mode 100644 tests/auto/api/testdata/precompiled-header/main.cpp create mode 100644 tests/auto/api/testdata/precompiled-header/myobject.cpp create mode 100644 tests/auto/api/testdata/precompiled-header/myobject.h create mode 100644 tests/auto/api/testdata/precompiled-header/project.qbs create mode 100644 tests/auto/api/testdata/precompiled-header/stable.h create mode 100644 tests/auto/api/testdata/process-result/main.cpp create mode 100644 tests/auto/api/testdata/process-result/process-result.qbs create mode 100644 tests/auto/api/testdata/productNameWithDots/app.cpp create mode 100644 tests/auto/api/testdata/productNameWithDots/lib.cpp create mode 100644 tests/auto/api/testdata/productNameWithDots/project.qbs create mode 100644 tests/auto/api/testdata/project-data-after-product-invalidation/file.cpp create mode 100644 tests/auto/api/testdata/project-data-after-product-invalidation/main.cpp create mode 100644 tests/auto/api/testdata/project-data-after-product-invalidation/project-data-after-product-invalidation.qbs create mode 100644 tests/auto/api/testdata/project-editing/existingfile1.txt create mode 100644 tests/auto/api/testdata/project-editing/existingfile2.txt create mode 100644 tests/auto/api/testdata/project-editing/existingfile3.txt create mode 100644 tests/auto/api/testdata/project-editing/file.cpp create mode 100644 tests/auto/api/testdata/project-editing/file.h create mode 100644 tests/auto/api/testdata/project-editing/main.cpp create mode 100644 tests/auto/api/testdata/project-editing/newfile1.txt create mode 100644 tests/auto/api/testdata/project-editing/newfile2.txt create mode 100644 tests/auto/api/testdata/project-editing/newfile3.txt create mode 100644 tests/auto/api/testdata/project-editing/newfile4.txt create mode 100644 tests/auto/api/testdata/project-editing/project-with-no-files.qbs create mode 100644 tests/auto/api/testdata/project-editing/project.qbs create mode 100644 tests/auto/api/testdata/project-editing/subdir/file.txt create mode 100644 tests/auto/api/testdata/project-editing/test.wildcard create mode 100644 tests/auto/api/testdata/project-invalidation/project.early-error.qbs create mode 100644 tests/auto/api/testdata/project-invalidation/project.late-error.qbs create mode 100644 tests/auto/api/testdata/project-invalidation/project.no-error.qbs create mode 100644 tests/auto/api/testdata/project-locking/project.qbs create mode 100644 tests/auto/api/testdata/project-properties-by-name/main1.cpp create mode 100644 tests/auto/api/testdata/project-properties-by-name/main2.cpp create mode 100644 tests/auto/api/testdata/project-properties-by-name/project.qbs create mode 100644 tests/auto/api/testdata/project-with-properties-item/project.qbs create mode 100644 tests/auto/api/testdata/projectd create mode 100644 tests/auto/api/testdata/properties-blocks/main.cpp create mode 100644 tests/auto/api/testdata/properties-blocks/propertiesblocks.qbs create mode 100644 tests/auto/api/testdata/qt5-plugin/echointerface.h create mode 100644 tests/auto/api/testdata/qt5-plugin/echoplugin.cpp create mode 100644 tests/auto/api/testdata/qt5-plugin/echoplugin.h create mode 100644 tests/auto/api/testdata/qt5-plugin/echoplugin.json.source create mode 100644 tests/auto/api/testdata/qt5-plugin/echoplugin_dummy.cpp create mode 100644 tests/auto/api/testdata/qt5-plugin/project.qbs create mode 100644 tests/auto/api/testdata/rc/main.cpp create mode 100644 tests/auto/api/testdata/rc/rc.qbs create mode 100644 tests/auto/api/testdata/rc/test.rc create mode 100644 tests/auto/api/testdata/recursive-wildcards/dir/file1.txt create mode 100644 tests/auto/api/testdata/recursive-wildcards/dir/subdir/file2.txt create mode 100644 tests/auto/api/testdata/recursive-wildcards/recursive_wildcards.qbs create mode 100644 tests/auto/api/testdata/referenced-file-errors/ambiguousdir/p1.qbs create mode 100644 tests/auto/api/testdata/referenced-file-errors/ambiguousdir/p2.qbs create mode 100644 tests/auto/api/testdata/referenced-file-errors/cycle.qbs create mode 100644 tests/auto/api/testdata/referenced-file-errors/emptydir/.gitignore create mode 100644 tests/auto/api/testdata/referenced-file-errors/modules/brokenmodule/brokenmodule.qbs create mode 100644 tests/auto/api/testdata/referenced-file-errors/okay.qbs create mode 100644 tests/auto/api/testdata/referenced-file-errors/okay2.qbs create mode 100644 tests/auto/api/testdata/referenced-file-errors/referenced-file-errors.qbs create mode 100644 tests/auto/api/testdata/referenced-file-errors/wrongtype.qbs create mode 100644 tests/auto/api/testdata/references/invalid1.qbs create mode 100644 tests/auto/api/testdata/references/invalid2.qbs create mode 100644 tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject1.qbs create mode 100644 tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject2.qbs create mode 100644 tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject3.qbs create mode 100644 tests/auto/api/testdata/references/subdir-with-no-project/test.txt create mode 100644 tests/auto/api/testdata/references/subdir-with-one-project/p.qbs create mode 100644 tests/auto/api/testdata/references/subdir-with-one-project/test.txt create mode 100644 tests/auto/api/testdata/references/valid.qbs create mode 100644 tests/auto/api/testdata/relaxed-mode-recovery/relaxed-mode-recovery.qbs create mode 100644 tests/auto/api/testdata/remove-file-dependency/main.cpp create mode 100644 tests/auto/api/testdata/remove-file-dependency/removeFileDependency.qbs create mode 100644 tests/auto/api/testdata/remove-file-dependency/someheader.h create mode 100644 tests/auto/api/testdata/rename-product/lib.cpp create mode 100644 tests/auto/api/testdata/rename-product/main.cpp create mode 100644 tests/auto/api/testdata/rename-product/rename.qbs create mode 100644 tests/auto/api/testdata/rename-target-artifact/lib.cpp create mode 100644 tests/auto/api/testdata/rename-target-artifact/main.cpp create mode 100644 tests/auto/api/testdata/rename-target-artifact/rename.qbs create mode 100644 tests/auto/api/testdata/restored-warnings/file.cpp create mode 100644 tests/auto/api/testdata/restored-warnings/main.cpp create mode 100644 tests/auto/api/testdata/restored-warnings/restored-warnings.qbs create mode 100644 tests/auto/api/testdata/rule-conflict/main.cpp create mode 100644 tests/auto/api/testdata/rule-conflict/pch1.h create mode 100644 tests/auto/api/testdata/rule-conflict/pch2.h create mode 100644 tests/auto/api/testdata/rule-conflict/rule-conflict.qbs create mode 100644 tests/auto/api/testdata/same-base-name/lib.c create mode 100644 tests/auto/api/testdata/same-base-name/lib.cpp create mode 100644 tests/auto/api/testdata/same-base-name/lib.m create mode 100644 tests/auto/api/testdata/same-base-name/lib.mm create mode 100644 tests/auto/api/testdata/same-base-name/main.c create mode 100644 tests/auto/api/testdata/same-base-name/project.qbs create mode 100644 tests/auto/api/testdata/simple-probe/main.cpp create mode 100644 tests/auto/api/testdata/simple-probe/project.qbs create mode 100644 tests/auto/api/testdata/soft-dependency/main.cpp create mode 100644 tests/auto/api/testdata/soft-dependency/project.qbs create mode 100644 tests/auto/api/testdata/source-file-in-build-dir/file.cpp create mode 100644 tests/auto/api/testdata/source-file-in-build-dir/project.qbs create mode 100644 tests/auto/api/testdata/source-file-in-build-dir/qt-debug/moc_blubb.cpp create mode 100644 tests/auto/api/testdata/source-file-in-build-dir/qt-debug/qt-debug.bg create mode 100644 tests/auto/api/testdata/static-lib-deps/a1.cpp create mode 100644 tests/auto/api/testdata/static-lib-deps/a2.cpp create mode 100644 tests/auto/api/testdata/static-lib-deps/b.cpp create mode 100644 tests/auto/api/testdata/static-lib-deps/c.cpp create mode 100644 tests/auto/api/testdata/static-lib-deps/d.cpp create mode 100644 tests/auto/api/testdata/static-lib-deps/e.cpp create mode 100644 tests/auto/api/testdata/static-lib-deps/main.cpp create mode 100644 tests/auto/api/testdata/static-lib-deps/project.qbs create mode 100644 tests/auto/api/testdata/subprojects/resources/imports/LibraryType/type.js create mode 100644 tests/auto/api/testdata/subprojects/resources/modules/QtCoreDepender/qtcoredepender.qbs create mode 100644 tests/auto/api/testdata/subprojects/subproject1/main.cpp create mode 100644 tests/auto/api/testdata/subprojects/subproject2/subproject2.qbs create mode 100644 tests/auto/api/testdata/subprojects/subproject2/subproject3/subproject3.qbs create mode 100644 tests/auto/api/testdata/subprojects/subproject2/subproject3/testlib.cpp create mode 100644 tests/auto/api/testdata/subprojects/toplevelproject.qbs create mode 100644 tests/auto/api/testdata/transformers/main.cpp create mode 100644 tests/auto/api/testdata/transformers/transformers.qbs create mode 100644 tests/auto/api/testdata/two-default-property-values/modules/mymodule/mymodule.qbs create mode 100644 tests/auto/api/testdata/two-default-property-values/modules/myothermodule/myothermodule.qbs create mode 100644 tests/auto/api/testdata/two-default-property-values/project.qbs create mode 100644 tests/auto/api/testdata/two-default-property-values/test.txt create mode 100644 tests/auto/api/testdata/type-change/main.cpp create mode 100644 tests/auto/api/testdata/type-change/project.qbs create mode 100644 tests/auto/api/testdata/uic/bla.cpp create mode 100644 tests/auto/api/testdata/uic/bla.h create mode 100644 tests/auto/api/testdata/uic/ui.h create mode 100644 tests/auto/api/testdata/uic/ui.ui create mode 100644 tests/auto/api/testdata/uic/uic.qbs create mode 100644 tests/auto/api/tst_api.cpp create mode 100644 tests/auto/api/tst_api.h create mode 100644 tests/auto/auto.pri create mode 100644 tests/auto/auto.pro create mode 100644 tests/auto/auto.qbs create mode 100644 tests/auto/blackbox/blackbox-clangdb.pro create mode 100644 tests/auto/blackbox/blackbox-java.pro create mode 100644 tests/auto/blackbox/blackbox.pro create mode 100644 tests/auto/blackbox/blackbox.qbs create mode 100644 tests/auto/blackbox/find/find-android.qbs create mode 100644 tests/auto/blackbox/find/find-jdk.qbs create mode 100644 tests/auto/blackbox/find/find-nodejs.qbs create mode 100644 tests/auto/blackbox/find/find-typescript.qbs create mode 100644 tests/auto/blackbox/testdata-clangdb/project1/i like spaces.cpp create mode 100644 tests/auto/blackbox/testdata-clangdb/project1/project.qbs create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/multiple-apks-per-project.qbs create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/product1.qbs create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/java/io/qt/dummy/Dummy.java create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/jni/lib1.cpp create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/jni/lib2.cpp create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/res/values/strings.xml create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/product2.qbs create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/java/io/qt/dummy/Dummy.java create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/jni/lib1.cpp create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/jni/lib2.cpp create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/multiple-libs-per-apk.qbs create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/AndroidManifest.xml create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/java/io/qt/dummy/Dummy.java create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/jni/lib1.cpp create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/jni/lib2.cpp create mode 100644 tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/res/values/strings.xml create mode 100644 tests/auto/blackbox/testdata-java/android/no-native/no-native.qbs create mode 100644 tests/auto/blackbox/testdata-java/android/teapot/teapot.qbs create mode 100644 tests/auto/blackbox/testdata-java/java/Car.java create mode 100644 tests/auto/blackbox/testdata-java/java/Car8.java create mode 100644 tests/auto/blackbox/testdata-java/java/HelloWorld.java create mode 100644 tests/auto/blackbox/testdata-java/java/HelloWorld8.java create mode 100644 tests/auto/blackbox/testdata-java/java/Jet.java create mode 100644 tests/auto/blackbox/testdata-java/java/NoPackage.java create mode 100644 tests/auto/blackbox/testdata-java/java/RandomStuff.java create mode 100644 tests/auto/blackbox/testdata-java/java/Ship.java create mode 100644 tests/auto/blackbox/testdata-java/java/Vehicle.java create mode 100644 tests/auto/blackbox/testdata-java/java/Vehicles.java create mode 100644 tests/auto/blackbox/testdata-java/java/engine.c create mode 100644 tests/auto/blackbox/testdata-java/java/inner-class/InnerClass.java create mode 100644 tests/auto/blackbox/testdata-java/java/inner-class/inner-class.qbs create mode 100644 tests/auto/blackbox/testdata-java/java/vehicles.qbs create mode 100644 tests/auto/blackbox/testdata/QTBUG-51237/modules/mymodule/mymodule.qbs create mode 100644 tests/auto/blackbox/testdata/QTBUG-51237/qtbug-51237.qbs create mode 100644 tests/auto/blackbox/testdata/always-run/dummy.txt create mode 100644 tests/auto/blackbox/testdata/always-run/rule.qbs create mode 100644 tests/auto/blackbox/testdata/always-run/transformer.qbs create mode 100644 tests/auto/blackbox/testdata/archiver/archivable.qbs create mode 100644 tests/auto/blackbox/testdata/archiver/list.txt create mode 100644 tests/auto/blackbox/testdata/archiver/test.txt create mode 100644 tests/auto/blackbox/testdata/assembly/assembly.qbs create mode 100644 tests/auto/blackbox/testdata/assembly/testa.s create mode 100644 tests/auto/blackbox/testdata/assembly/testb.S create mode 100644 tests/auto/blackbox/testdata/assembly/testc.sx create mode 100644 tests/auto/blackbox/testdata/assembly/testd_x86.asm create mode 100644 tests/auto/blackbox/testdata/assembly/testd_x86_64.asm create mode 100644 tests/auto/blackbox/testdata/auto-qrc/auto-qrc.qbs create mode 100644 tests/auto/blackbox/testdata/auto-qrc/main.cpp create mode 100644 tests/auto/blackbox/testdata/auto-qrc/qrc-base/resource1.txt create mode 100644 tests/auto/blackbox/testdata/auto-qrc/qrc-base/subdir/resource2.txt create mode 100644 tests/auto/blackbox/testdata/auto-qrc/qrc-base/subdir/resource3.txt create mode 100644 tests/auto/blackbox/testdata/badInterpreter/badInterpreter.qbs create mode 100644 tests/auto/blackbox/testdata/badInterpreter/qbs/modules/script-test/script-test.qbs create mode 100755 tests/auto/blackbox/testdata/badInterpreter/script-interp-missing create mode 100755 tests/auto/blackbox/testdata/badInterpreter/script-interp-noexec create mode 100644 tests/auto/blackbox/testdata/badInterpreter/script-noexec create mode 100755 tests/auto/blackbox/testdata/badInterpreter/script-ok create mode 100644 tests/auto/blackbox/testdata/build-directories/project.qbs create mode 100644 tests/auto/blackbox/testdata/bundle-structure/bundle-structure.qbs create mode 100644 tests/auto/blackbox/testdata/bundle-structure/dummy.c create mode 100644 tests/auto/blackbox/testdata/bundle-structure/dummy.h create mode 100644 tests/auto/blackbox/testdata/bundle-structure/dummy_p.h create mode 100644 tests/auto/blackbox/testdata/bundle-structure/resource.txt create mode 100644 tests/auto/blackbox/testdata/change-in-disabled-product/project.qbs create mode 100644 tests/auto/blackbox/testdata/change-in-disabled-product/test1.txt create mode 100644 tests/auto/blackbox/testdata/change-in-disabled-product/test2.txt create mode 100644 tests/auto/blackbox/testdata/change-in-imported-file/change-in-imported-file.qbs create mode 100644 tests/auto/blackbox/testdata/change-in-imported-file/prepare.js create mode 100644 tests/auto/blackbox/testdata/change-in-imported-file/test.txt create mode 100644 tests/auto/blackbox/testdata/changed-files/file1.cpp create mode 100644 tests/auto/blackbox/testdata/changed-files/file2.cpp create mode 100644 tests/auto/blackbox/testdata/changed-files/main.cpp create mode 100644 tests/auto/blackbox/testdata/changed-files/project.qbs create mode 100644 tests/auto/blackbox/testdata/clean/clean.qbs create mode 100644 tests/auto/blackbox/testdata/clean/dep.cpp create mode 100644 tests/auto/blackbox/testdata/clean/main.cpp create mode 100755 tests/auto/blackbox/testdata/cli/HelloWorld.cs create mode 100755 tests/auto/blackbox/testdata/cli/Libby.cs create mode 100755 tests/auto/blackbox/testdata/cli/Libby2.cs create mode 100644 tests/auto/blackbox/testdata/cli/Module.cs create mode 100755 tests/auto/blackbox/testdata/cli/Module.vb create mode 100644 tests/auto/blackbox/testdata/cli/dotnettest.qbs create mode 100644 tests/auto/blackbox/testdata/cli/fshello.fs create mode 100644 tests/auto/blackbox/testdata/cli/fshello.qbs create mode 100644 tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs create mode 100644 tests/auto/blackbox/testdata/concurrent-executor/dummy1.input create mode 100644 tests/auto/blackbox/testdata/concurrent-executor/dummy2.input create mode 100644 tests/auto/blackbox/testdata/concurrent-executor/util.js create mode 100644 tests/auto/blackbox/testdata/conditional-export/conditional-export.qbs create mode 100644 tests/auto/blackbox/testdata/conditional-export/main.cpp create mode 100644 tests/auto/blackbox/testdata/conflicting-artifacts/conflicting-artifacts.qbs create mode 100644 tests/auto/blackbox/testdata/conflicting-artifacts/main.cpp create mode 100644 tests/auto/blackbox/testdata/dbus-adaptors/THIS.IS.A.STRANGE.FILENAME.CAR.XML create mode 100644 tests/auto/blackbox/testdata/dbus-adaptors/car.cpp create mode 100644 tests/auto/blackbox/testdata/dbus-adaptors/car.h create mode 100644 tests/auto/blackbox/testdata/dbus-adaptors/car.qbs create mode 100644 tests/auto/blackbox/testdata/dbus-adaptors/main.cpp create mode 100644 tests/auto/blackbox/testdata/dbus-interfaces/car.xml create mode 100644 tests/auto/blackbox/testdata/dbus-interfaces/controller.cpp create mode 100644 tests/auto/blackbox/testdata/dbus-interfaces/controller.h create mode 100644 tests/auto/blackbox/testdata/dbus-interfaces/controller.qbs create mode 100644 tests/auto/blackbox/testdata/dbus-interfaces/controller.ui create mode 100644 tests/auto/blackbox/testdata/dbus-interfaces/main.cpp create mode 100644 tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs create mode 100644 tests/auto/blackbox/testdata/dependenciesProperty/product2.cpp create mode 100644 tests/auto/blackbox/testdata/dependency-profile-mismatch/dependency-profile-mismatch.qbs create mode 100644 tests/auto/blackbox/testdata/deploymentTarget/deployment.qbs create mode 100644 tests/auto/blackbox/testdata/deploymentTarget/main.c create mode 100644 tests/auto/blackbox/testdata/deprecated-property/deprecated-property.qbs create mode 100644 tests/auto/blackbox/testdata/deprecated-property/modules/themodule/m.qbs create mode 100644 tests/auto/blackbox/testdata/dynamicMultiplexRule/dynamicMultiplexRule.qbs create mode 100644 tests/auto/blackbox/testdata/dynamicMultiplexRule/one.txt create mode 100644 tests/auto/blackbox/testdata/dynamicMultiplexRule/three.txt create mode 100644 tests/auto/blackbox/testdata/dynamicMultiplexRule/two.txt create mode 100644 tests/auto/blackbox/testdata/dynamicRuleOutputs/after/numbers.l create mode 100644 tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js create mode 100644 tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs create mode 100644 tests/auto/blackbox/testdata/dynamicRuleOutputs/before/numbers.l create mode 100644 tests/auto/blackbox/testdata/embedInfoPlist/embedInfoPlist.qbs create mode 100644 tests/auto/blackbox/testdata/embedInfoPlist/main.m create mode 100644 tests/auto/blackbox/testdata/enableExceptions/empty.m create mode 100644 tests/auto/blackbox/testdata/enableExceptions/empty.mm create mode 100644 tests/auto/blackbox/testdata/enableExceptions/emptymain.cpp create mode 100644 tests/auto/blackbox/testdata/enableExceptions/exceptions-objc.qbs create mode 100644 tests/auto/blackbox/testdata/enableExceptions/exceptions-objcpp-cpp.qbs create mode 100644 tests/auto/blackbox/testdata/enableExceptions/exceptions-objcpp.qbs create mode 100644 tests/auto/blackbox/testdata/enableExceptions/exceptions.qbs create mode 100644 tests/auto/blackbox/testdata/enableExceptions/main.cpp create mode 100644 tests/auto/blackbox/testdata/enableExceptions/main.m create mode 100644 tests/auto/blackbox/testdata/enableExceptions/none.qbs create mode 100644 tests/auto/blackbox/testdata/enableRtti/main.cpp create mode 100644 tests/auto/blackbox/testdata/enableRtti/rtti.qbs create mode 100644 tests/auto/blackbox/testdata/erroneous/nonexistentWorkingDir/project.qbs create mode 100644 tests/auto/blackbox/testdata/error-info/helper.js create mode 100644 tests/auto/blackbox/testdata/error-info/project.qbs create mode 100644 tests/auto/blackbox/testdata/export-rule/blubber.cpp create mode 100644 tests/auto/blackbox/testdata/export-rule/export-rule.qbs create mode 100644 tests/auto/blackbox/testdata/export-rule/myapp.blubb create mode 100644 tests/auto/blackbox/testdata/export-to-outside-searchpath/export-to-outside-searchpath.qbs create mode 100644 tests/auto/blackbox/testdata/export-to-outside-searchpath/qbs-resources/modules/aModule/aModule.qbs create mode 100644 tests/auto/blackbox/testdata/fileDependencies/awesomelib/awesome.h create mode 100644 tests/auto/blackbox/testdata/fileDependencies/awesomelib/magnificent.h create mode 100644 tests/auto/blackbox/testdata/fileDependencies/fileDependencies.qbs create mode 100644 tests/auto/blackbox/testdata/fileDependencies/src/narf.cpp create mode 100644 tests/auto/blackbox/testdata/fileDependencies/src/narf.h create mode 100644 tests/auto/blackbox/testdata/fileDependencies/src/zort.cpp create mode 100644 tests/auto/blackbox/testdata/find/find-cli.qbs create mode 100644 tests/auto/blackbox/testdata/frameworkStructure/BaseResource create mode 100644 tests/auto/blackbox/testdata/frameworkStructure/Widget.cpp create mode 100644 tests/auto/blackbox/testdata/frameworkStructure/Widget.h create mode 100644 tests/auto/blackbox/testdata/frameworkStructure/WidgetPrivate.h create mode 100644 tests/auto/blackbox/testdata/frameworkStructure/en.lproj/EnglishResource create mode 100644 tests/auto/blackbox/testdata/frameworkStructure/frameworkStructure.qbs create mode 100644 tests/auto/blackbox/testdata/generated-artifact-as-input-to-dynamic-rule/input.txt create mode 100644 tests/auto/blackbox/testdata/generated-artifact-as-input-to-dynamic-rule/p.qbs create mode 100644 tests/auto/blackbox/testdata/group-condition-change/group-condition-change.qbs create mode 100644 tests/auto/blackbox/testdata/group-condition-change/input_kaputt.txt create mode 100644 tests/auto/blackbox/testdata/group-location-warning/GroupInSameDir.qbs create mode 100644 tests/auto/blackbox/testdata/group-location-warning/group-location-warning.qbs create mode 100644 tests/auto/blackbox/testdata/group-location-warning/modules/gm/gm.qbs create mode 100644 tests/auto/blackbox/testdata/group-location-warning/referenced-from-group-in-other-dir-via-wildcard.wc create mode 100644 tests/auto/blackbox/testdata/group-location-warning/referenced-from-group-in-other-dir.txt create mode 100644 tests/auto/blackbox/testdata/group-location-warning/referenced-from-group-in-same-dir.txt create mode 100644 tests/auto/blackbox/testdata/group-location-warning/referenced-from-module.txt create mode 100644 tests/auto/blackbox/testdata/group-location-warning/referenced-from-parent-in-other-dir.txt create mode 100644 tests/auto/blackbox/testdata/group-location-warning/referenced-from-product.txt create mode 100644 tests/auto/blackbox/testdata/group-location-warning/referenced-via-absolute-path.txt create mode 100644 tests/auto/blackbox/testdata/group-location-warning/referenced-via-absolute-prefix.txt create mode 100644 tests/auto/blackbox/testdata/group-location-warning/subdir/AGroupInOtherDir.qbs create mode 100644 tests/auto/blackbox/testdata/group-location-warning/subdir/AndAnotherGroupInOtherDir.qbs create mode 100644 tests/auto/blackbox/testdata/group-location-warning/subdir/OtherGroupInOtherDir.qbs create mode 100644 tests/auto/blackbox/testdata/group-location-warning/subdir/ParentInOtherDir.qbs create mode 100644 tests/auto/blackbox/testdata/group-location-warning/subdir/YetAnotherGroupInOtherDir.qbs create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/groups-in-modules.qbs create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper/chunk.coal create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper/diamondc.c create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper/helper.qbs create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper2/helper2.c create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper2/helper2.qbs create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper3/helper3.c create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper3/helper3.qbs create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper4/helper4.c create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper4/helper4.qbs create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper5/helper5.c create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper5/helper5.qbs create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper6/helper6.c create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/modules/helper6/helper6.qbs create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/rock.coal create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/someotherfile.txt create mode 100644 tests/auto/blackbox/testdata/groups-in-modules/someotherfile2.txt create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/EmptyStoryboard.storyboard create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/MainMenu.xib create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/Storyboard.storyboard create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/assetcatalogempty.qbs create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16.png create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16@2x.png create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/other.imageset/Contents.json create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/other.imageset/icon_16x16.png create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/other.imageset/icon_16x16@2x.png create mode 100644 tests/auto/blackbox/testdata/ib/assetcatalog/main.c create mode 100644 tests/auto/blackbox/testdata/ib/empty-asset-catalogs/assetcatalog1.xcassets/.keep create mode 100644 tests/auto/blackbox/testdata/ib/empty-asset-catalogs/assetcatalog2.xcassets/.keep create mode 100644 tests/auto/blackbox/testdata/ib/empty-asset-catalogs/main.c create mode 100644 tests/auto/blackbox/testdata/ib/empty-asset-catalogs/multiple-asset-catalogs.qbs create mode 100644 tests/auto/blackbox/testdata/ib/iconset/iconset.qbs create mode 100644 tests/auto/blackbox/testdata/ib/iconset/white.iconset/icon_16x16.png create mode 100644 tests/auto/blackbox/testdata/ib/iconset/white.iconset/icon_16x16@2x.png create mode 100644 tests/auto/blackbox/testdata/ib/iconsetapp/iconsetapp.qbs create mode 100644 tests/auto/blackbox/testdata/ib/iconsetapp/main.c create mode 100644 tests/auto/blackbox/testdata/ib/iconsetapp/white.iconset/icon_16x16.png create mode 100644 tests/auto/blackbox/testdata/ib/iconsetapp/white.iconset/icon_16x16@2x.png create mode 100644 tests/auto/blackbox/testdata/ib/multiple-asset-catalogs/assetcatalog1.xcassets/other.imageset/Contents.json create mode 100644 tests/auto/blackbox/testdata/ib/multiple-asset-catalogs/assetcatalog1.xcassets/other.imageset/icon_16x16.png create mode 100644 tests/auto/blackbox/testdata/ib/multiple-asset-catalogs/assetcatalog1.xcassets/other.imageset/icon_16x16@2x.png create mode 100644 tests/auto/blackbox/testdata/ib/multiple-asset-catalogs/assetcatalog2.xcassets/other.imageset/Contents.json create mode 100644 tests/auto/blackbox/testdata/ib/multiple-asset-catalogs/assetcatalog2.xcassets/other.imageset/icon_16x16.png create mode 100644 tests/auto/blackbox/testdata/ib/multiple-asset-catalogs/assetcatalog2.xcassets/other.imageset/icon_16x16@2x.png create mode 100644 tests/auto/blackbox/testdata/ib/multiple-asset-catalogs/main.c create mode 100644 tests/auto/blackbox/testdata/ib/multiple-asset-catalogs/multiple-asset-catalogs.qbs create mode 100644 tests/auto/blackbox/testdata/import-in-properties-condition/import-in-properties-condition.qbs create mode 100644 tests/auto/blackbox/testdata/import-in-properties-condition/modules/amodule/m.qbs create mode 100644 tests/auto/blackbox/testdata/import-in-properties-condition/modules/depmodule/m.qbs create mode 100644 tests/auto/blackbox/testdata/importing-product/header.h.in create mode 100644 tests/auto/blackbox/testdata/importing-product/importing-product.qbs create mode 100644 tests/auto/blackbox/testdata/importing-product/main.cpp create mode 100644 tests/auto/blackbox/testdata/imports-conflict/imports-conflict.qbs create mode 100644 tests/auto/blackbox/testdata/imports-conflict/modules/themodule/m.qbs create mode 100644 tests/auto/blackbox/testdata/imports-conflict/modules/themodule/utils.js create mode 100644 tests/auto/blackbox/testdata/infoplist/infoplist.qbs create mode 100644 tests/auto/blackbox/testdata/infoplist/main.c create mode 100644 tests/auto/blackbox/testdata/innosetup/inc/qbsinc.iss create mode 100644 tests/auto/blackbox/testdata/innosetup/innosetup.qbs create mode 100644 tests/auto/blackbox/testdata/innosetup/test.iss create mode 100644 tests/auto/blackbox/testdata/inputs-from-dependencies/file1.txt create mode 100644 tests/auto/blackbox/testdata/inputs-from-dependencies/file2.txt create mode 100644 tests/auto/blackbox/testdata/inputs-from-dependencies/file3.txt create mode 100644 tests/auto/blackbox/testdata/inputs-from-dependencies/file4.txt create mode 100644 tests/auto/blackbox/testdata/inputs-from-dependencies/project.qbs create mode 100644 tests/auto/blackbox/testdata/install-duplicates/dir1/file1.txt create mode 100644 tests/auto/blackbox/testdata/install-duplicates/dir1/file2.txt create mode 100644 tests/auto/blackbox/testdata/install-duplicates/dir2/file1.txt create mode 100644 tests/auto/blackbox/testdata/install-duplicates/dir2/file2.txt create mode 100644 tests/auto/blackbox/testdata/install-duplicates/dir2/file3.txt create mode 100644 tests/auto/blackbox/testdata/install-duplicates/install-duplicates.qbs create mode 100644 tests/auto/blackbox/testdata/install-tree/data/foo.txt create mode 100644 tests/auto/blackbox/testdata/install-tree/data/subdir1/bar.txt create mode 100644 tests/auto/blackbox/testdata/install-tree/data/subdir2/baz.txt create mode 100644 tests/auto/blackbox/testdata/install-tree/main.cpp create mode 100644 tests/auto/blackbox/testdata/install-tree/project.qbs create mode 100644 tests/auto/blackbox/testdata/installable/installable.qbs create mode 100644 tests/auto/blackbox/testdata/installable/main.cpp create mode 100644 tests/auto/blackbox/testdata/installed-source-files/main.cpp create mode 100644 tests/auto/blackbox/testdata/installed-source-files/project.qbs create mode 100644 tests/auto/blackbox/testdata/installed-source-files/readme.txt create mode 100644 tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs create mode 100644 tests/auto/blackbox/testdata/installed_artifact/installed_artifact.qbs create mode 100644 tests/auto/blackbox/testdata/installed_artifact/main.cpp create mode 100644 tests/auto/blackbox/testdata/installpackage/installpackage.qbs create mode 100644 tests/auto/blackbox/testdata/installpackage/lib.cpp create mode 100644 tests/auto/blackbox/testdata/installpackage/lib.h create mode 100644 tests/auto/blackbox/testdata/installpackage/main.cpp create mode 100644 tests/auto/blackbox/testdata/invalid-command-property/input.txt create mode 100644 tests/auto/blackbox/testdata/invalid-command-property/invalid-command-property.qbs create mode 100644 tests/auto/blackbox/testdata/invalid-extension-instantiation/invalid-extension-instantiation.qbs create mode 100644 tests/auto/blackbox/testdata/invalid-library-names/invalid-library-names.qbs create mode 100644 tests/auto/blackbox/testdata/invalid-library-names/main.cpp create mode 100644 tests/auto/blackbox/testdata/jsextensions-file/file.qbs create mode 100644 tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs create mode 100644 tests/auto/blackbox/testdata/jsextensions-process/process.qbs create mode 100644 tests/auto/blackbox/testdata/jsextensions-propertylist/propertylist.qbs create mode 100644 tests/auto/blackbox/testdata/jsextensions-temporarydir/jsextensions-temporarydir.qbs create mode 100644 tests/auto/blackbox/testdata/jsextensions-textfile/textfile.qbs create mode 100644 tests/auto/blackbox/testdata/ld/coreutils.cpp create mode 100644 tests/auto/blackbox/testdata/ld/coreutils.h create mode 100644 tests/auto/blackbox/testdata/ld/ld.qbs create mode 100644 tests/auto/blackbox/testdata/ld/main.cpp create mode 100644 tests/auto/blackbox/testdata/lexyacc/one-grammar/lexer.l create mode 100644 tests/auto/blackbox/testdata/lexyacc/one-grammar/one-grammar.qbs create mode 100644 tests/auto/blackbox/testdata/lexyacc/one-grammar/parser.y create mode 100644 tests/auto/blackbox/testdata/lexyacc/one-grammar/types.h create mode 100644 tests/auto/blackbox/testdata/lexyacc/two-grammars/g1.l create mode 100644 tests/auto/blackbox/testdata/lexyacc/two-grammars/g1.y create mode 100644 tests/auto/blackbox/testdata/lexyacc/two-grammars/g2.l create mode 100644 tests/auto/blackbox/testdata/lexyacc/two-grammars/g2.y create mode 100644 tests/auto/blackbox/testdata/lexyacc/two-grammars/main.c create mode 100644 tests/auto/blackbox/testdata/lexyacc/two-grammars/two-grammars.qbs create mode 100644 tests/auto/blackbox/testdata/linkerMode/main.c create mode 100644 tests/auto/blackbox/testdata/linkerMode/main.cpp create mode 100644 tests/auto/blackbox/testdata/linkerMode/main.m create mode 100644 tests/auto/blackbox/testdata/linkerMode/main.mm create mode 100644 tests/auto/blackbox/testdata/linkerMode/main.s create mode 100644 tests/auto/blackbox/testdata/linkerMode/project.qbs create mode 100644 tests/auto/blackbox/testdata/linkerMode/staticlib.cpp create mode 100644 tests/auto/blackbox/testdata/linkerMode/staticmain.c create mode 100644 tests/auto/blackbox/testdata/linkerscripts/linkerscript1 create mode 100644 tests/auto/blackbox/testdata/linkerscripts/linkerscript2 create mode 100644 tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs create mode 100644 tests/auto/blackbox/testdata/linkerscripts/testlib.c create mode 100644 tests/auto/blackbox/testdata/list-properties-with-outer/dummy.txt create mode 100644 tests/auto/blackbox/testdata/list-properties-with-outer/modules/higher/higher.qbs create mode 100644 tests/auto/blackbox/testdata/list-properties-with-outer/modules/lower/lower.qbs create mode 100644 tests/auto/blackbox/testdata/list-properties-with-outer/project.qbs create mode 100644 tests/auto/blackbox/testdata/list-property-order/dummy.txt create mode 100644 tests/auto/blackbox/testdata/list-property-order/modules/higher1/higher1.qbs create mode 100644 tests/auto/blackbox/testdata/list-property-order/modules/higher2/higher2.qbs create mode 100644 tests/auto/blackbox/testdata/list-property-order/modules/higher3/higher3.qbs create mode 100644 tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs create mode 100644 tests/auto/blackbox/testdata/list-property-order/product.qbs create mode 100644 tests/auto/blackbox/testdata/loadablemodule/exported.cpp create mode 100644 tests/auto/blackbox/testdata/loadablemodule/exported.h create mode 100644 tests/auto/blackbox/testdata/loadablemodule/loadablemodule.qbs create mode 100644 tests/auto/blackbox/testdata/loadablemodule/main.cpp create mode 100644 tests/auto/blackbox/testdata/lrelease/de.ts create mode 100644 tests/auto/blackbox/testdata/lrelease/hu.ts create mode 100644 tests/auto/blackbox/testdata/lrelease/lrelease.qbs create mode 100644 tests/auto/blackbox/testdata/missing-dependency/main.cpp create mode 100644 tests/auto/blackbox/testdata/missing-dependency/missing-dependency.qbs create mode 100644 tests/auto/blackbox/testdata/mixed-build-variants/main.cpp create mode 100644 tests/auto/blackbox/testdata/mixed-build-variants/mixed-build-variants.qbs create mode 100644 tests/auto/blackbox/testdata/moc-flags/blubb.h create mode 100644 tests/auto/blackbox/testdata/moc-flags/main.cpp create mode 100644 tests/auto/blackbox/testdata/moc-flags/moc-flags.qbs create mode 100644 tests/auto/blackbox/testdata/multiple-changes/dummy.txt create mode 100644 tests/auto/blackbox/testdata/multiple-changes/project.qbs create mode 100644 tests/auto/blackbox/testdata/nested-groups/file1.cpp create mode 100644 tests/auto/blackbox/testdata/nested-groups/file1.h create mode 100644 tests/auto/blackbox/testdata/nested-groups/file2.cpp create mode 100644 tests/auto/blackbox/testdata/nested-groups/file2.h create mode 100644 tests/auto/blackbox/testdata/nested-groups/file3.cpp create mode 100644 tests/auto/blackbox/testdata/nested-groups/file3.h create mode 100644 tests/auto/blackbox/testdata/nested-groups/main.cpp create mode 100644 tests/auto/blackbox/testdata/nested-groups/main2.cpp create mode 100644 tests/auto/blackbox/testdata/nested-groups/main3.cpp create mode 100644 tests/auto/blackbox/testdata/nested-groups/modules/themodule/themodule.qbs create mode 100644 tests/auto/blackbox/testdata/nested-groups/nested-groups.qbs create mode 100644 tests/auto/blackbox/testdata/nested-groups/other.cpp create mode 100644 tests/auto/blackbox/testdata/nested-groups/other.h create mode 100644 tests/auto/blackbox/testdata/nested-properties/dummy.txt create mode 100644 tests/auto/blackbox/testdata/nested-properties/modules/higherlevel/higher-level.qbs create mode 100644 tests/auto/blackbox/testdata/nested-properties/modules/lowerlevel/lower-level.qbs create mode 100644 tests/auto/blackbox/testdata/nested-properties/product.qbs create mode 100644 tests/auto/blackbox/testdata/new-output-artifact/input.txt create mode 100644 tests/auto/blackbox/testdata/new-output-artifact/new-output-artifact.qbs create mode 100644 tests/auto/blackbox/testdata/nodejs/hello.js create mode 100644 tests/auto/blackbox/testdata/nodejs/hello.qbs create mode 100644 tests/auto/blackbox/testdata/non-broken-files-in-broken-product/broken.cpp create mode 100644 tests/auto/blackbox/testdata/non-broken-files-in-broken-product/fine.cpp create mode 100644 tests/auto/blackbox/testdata/non-broken-files-in-broken-product/project.qbs create mode 100644 tests/auto/blackbox/testdata/non-default-product/main.cpp create mode 100644 tests/auto/blackbox/testdata/non-default-product/project.qbs create mode 100644 tests/auto/blackbox/testdata/nsis/hello.bat create mode 100644 tests/auto/blackbox/testdata/nsis/hello.nsi create mode 100644 tests/auto/blackbox/testdata/nsis/hello.qbs create mode 100644 tests/auto/blackbox/testdata/objc-arc/arc.m create mode 100644 tests/auto/blackbox/testdata/objc-arc/arc.mm create mode 100644 tests/auto/blackbox/testdata/objc-arc/main.m create mode 100644 tests/auto/blackbox/testdata/objc-arc/mrc.m create mode 100644 tests/auto/blackbox/testdata/objc-arc/mrc.mm create mode 100644 tests/auto/blackbox/testdata/objc-arc/objc-arc.qbs create mode 100644 tests/auto/blackbox/testdata/output-artifact-auto-tagging/broken.cpp.in create mode 100644 tests/auto/blackbox/testdata/output-artifact-auto-tagging/main.cpp.in create mode 100644 tests/auto/blackbox/testdata/output-artifact-auto-tagging/output-artifact-auto-tagging.qbs create mode 100644 tests/auto/blackbox/testdata/overrideProjectProperties/helper_lib.qbs create mode 100644 tests/auto/blackbox/testdata/overrideProjectProperties/helperlib.cpp create mode 100644 tests/auto/blackbox/testdata/overrideProjectProperties/main.cpp create mode 100644 tests/auto/blackbox/testdata/overrideProjectProperties/main2.cpp create mode 100644 tests/auto/blackbox/testdata/overrideProjectProperties/project.qbs create mode 100644 tests/auto/blackbox/testdata/overrideProjectProperties/project_using_helper_lib.qbs create mode 100644 tests/auto/blackbox/testdata/pch-change-tracking/header1.h create mode 100644 tests/auto/blackbox/testdata/pch-change-tracking/header2.h create mode 100644 tests/auto/blackbox/testdata/pch-change-tracking/main.cpp create mode 100644 tests/auto/blackbox/testdata/pch-change-tracking/pch-change-tracking.qbs create mode 100644 tests/auto/blackbox/testdata/pch-change-tracking/pch.h create mode 100644 tests/auto/blackbox/testdata/pkg-config-probe-sysroot/modules/themodule/themodule.qbs create mode 100644 tests/auto/blackbox/testdata/pkg-config-probe-sysroot/pkg-config.qbs create mode 100644 tests/auto/blackbox/testdata/pkg-config-probe-sysroot/sysroot1/usr/share/pkgconfig/dummy.pc create mode 100644 tests/auto/blackbox/testdata/pkg-config-probe-sysroot/sysroot2/usr/share/pkgconfig/dummy.pc create mode 100644 tests/auto/blackbox/testdata/pkg-config-probe/dummy1/dummy1.pc create mode 100644 tests/auto/blackbox/testdata/pkg-config-probe/dummy2/dummy2.pc create mode 100644 tests/auto/blackbox/testdata/pkg-config-probe/modules/themodule/themodule.qbs create mode 100644 tests/auto/blackbox/testdata/pkg-config-probe/pkg-config.qbs create mode 100644 tests/auto/blackbox/testdata/plugin-meta-data/app.cpp create mode 100644 tests/auto/blackbox/testdata/plugin-meta-data/plugin-meta-data.qbs create mode 100644 tests/auto/blackbox/testdata/plugin-meta-data/theplugin.cpp create mode 100644 tests/auto/blackbox/testdata/probe-change-tracking/probe-change-tracking.qbs create mode 100644 tests/auto/blackbox/testdata/probe-in-exported-module/dependee.qbs create mode 100644 tests/auto/blackbox/testdata/probe-in-exported-module/dependency.qbs create mode 100644 tests/auto/blackbox/testdata/probe-in-exported-module/modules/depmodule/depmodule.qbs create mode 100644 tests/auto/blackbox/testdata/probe-in-exported-module/modules/mymodule/mymodule.qbs create mode 100644 tests/auto/blackbox/testdata/probe-in-exported-module/modules/myothermodule/myothermodule.qbs create mode 100644 tests/auto/blackbox/testdata/probe-in-exported-module/probe-in-exported-module.qbs create mode 100644 tests/auto/blackbox/testdata/probe-in-exported-module/test.in create mode 100644 tests/auto/blackbox/testdata/probe-in-exported-module/test2.in create mode 100644 tests/auto/blackbox/testdata/probeProperties/bin/tool create mode 100644 tests/auto/blackbox/testdata/probeProperties/main.c create mode 100644 tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs create mode 100644 tests/auto/blackbox/testdata/probes-and-array-properties/modules/mymodule/mymodule.qbs create mode 100644 tests/auto/blackbox/testdata/probes-and-array-properties/probes-and-array-properties.qbs create mode 100644 tests/auto/blackbox/testdata/probes-in-nested-modules/modules/inner/inner.qbs create mode 100644 tests/auto/blackbox/testdata/probes-in-nested-modules/modules/outer/outer.qbs create mode 100644 tests/auto/blackbox/testdata/probes-in-nested-modules/probes-in-nested-modules.qbs create mode 100644 tests/auto/blackbox/testdata/product-dependencies-by-type/main.cpp create mode 100644 tests/auto/blackbox/testdata/product-dependencies-by-type/project.qbs create mode 100644 tests/auto/blackbox/testdata/productproperties/app.qbs create mode 100644 tests/auto/blackbox/testdata/productproperties/blubb_header.h.in create mode 100644 tests/auto/blackbox/testdata/productproperties/header.qbs create mode 100644 tests/auto/blackbox/testdata/productproperties/main.cpp create mode 100644 tests/auto/blackbox/testdata/productproperties/project.qbs create mode 100644 tests/auto/blackbox/testdata/project_filepath_check/main.cpp create mode 100644 tests/auto/blackbox/testdata/project_filepath_check/project1.qbs create mode 100644 tests/auto/blackbox/testdata/project_filepath_check/project2.qbs create mode 100644 tests/auto/blackbox/testdata/proper quoting/main.cpp create mode 100644 tests/auto/blackbox/testdata/proper quoting/my static lib helper.cpp create mode 100644 tests/auto/blackbox/testdata/proper quoting/my static lib.cpp create mode 100644 tests/auto/blackbox/testdata/proper quoting/proper quoting.qbs create mode 100644 tests/auto/blackbox/testdata/proper quoting/some helper/some helper.cpp create mode 100644 tests/auto/blackbox/testdata/proper quoting/some helper/some helper.h create mode 100644 tests/auto/blackbox/testdata/properties-in-export-items/main1.cpp create mode 100644 tests/auto/blackbox/testdata/properties-in-export-items/main2.cpp create mode 100644 tests/auto/blackbox/testdata/properties-in-export-items/properties-in-export-items.qbs create mode 100644 tests/auto/blackbox/testdata/property-precedence/dep.qbs create mode 100644 tests/auto/blackbox/testdata/property-precedence/dummy.txt create mode 100644 tests/auto/blackbox/testdata/property-precedence/modules/leaf/leaf.qbs create mode 100644 tests/auto/blackbox/testdata/property-precedence/modules/nonleaf/nonleaf.qbs create mode 100644 tests/auto/blackbox/testdata/property-precedence/project.qbs create mode 100644 tests/auto/blackbox/testdata/propertyChanges/lib.cpp create mode 100644 tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs create mode 100644 tests/auto/blackbox/testdata/propertyChanges/project.qbs create mode 100644 tests/auto/blackbox/testdata/propertyChanges/ruletest.qbs create mode 100644 tests/auto/blackbox/testdata/propertyChanges/source1.cpp create mode 100644 tests/auto/blackbox/testdata/propertyChanges/source2.cpp create mode 100644 tests/auto/blackbox/testdata/propertyChanges/source3.cpp create mode 100644 tests/auto/blackbox/testdata/propertyChanges/test.in create mode 100644 tests/auto/blackbox/testdata/qbsVersion/qbs-version.qbs create mode 100644 tests/auto/blackbox/testdata/qml-debugging/main.cpp create mode 100644 tests/auto/blackbox/testdata/qml-debugging/project.qbs create mode 100644 tests/auto/blackbox/testdata/qobject-in-mm/main.mm create mode 100644 tests/auto/blackbox/testdata/qobject-in-mm/project.qbs create mode 100644 tests/auto/blackbox/testdata/qrc/bla.cpp create mode 100644 tests/auto/blackbox/testdata/qrc/bla.qrc create mode 100644 tests/auto/blackbox/testdata/qrc/i.qbs create mode 100644 tests/auto/blackbox/testdata/qrc/stuff.txt create mode 100644 tests/auto/blackbox/testdata/qtscxml/dummystatemachine.scxml create mode 100644 tests/auto/blackbox/testdata/qtscxml/main.cpp create mode 100644 tests/auto/blackbox/testdata/qtscxml/qtscxml.qbs create mode 100644 tests/auto/blackbox/testdata/rad-after-incomplete-build/dummy.txt create mode 100644 tests/auto/blackbox/testdata/rad-after-incomplete-build/project_with_rule.qbs create mode 100644 tests/auto/blackbox/testdata/recursive_renaming/dir/subdir/blubb.txt create mode 100644 tests/auto/blackbox/testdata/recursive_renaming/dir/wasser.txt create mode 100644 tests/auto/blackbox/testdata/recursive_renaming/recursive_renaming.qbs create mode 100644 tests/auto/blackbox/testdata/recursive_wildcards/dir/file1.txt create mode 100644 tests/auto/blackbox/testdata/recursive_wildcards/dir/subdir/file2.txt create mode 100644 tests/auto/blackbox/testdata/recursive_wildcards/recursive_wildcards.qbs create mode 100644 tests/auto/blackbox/testdata/referenceErrorInExport/main.c create mode 100644 tests/auto/blackbox/testdata/referenceErrorInExport/project.qbs create mode 100644 tests/auto/blackbox/testdata/renameDependency/after/lib2.cpp create mode 100644 tests/auto/blackbox/testdata/renameDependency/after/lib2.h create mode 100644 tests/auto/blackbox/testdata/renameDependency/before/lib.cpp create mode 100644 tests/auto/blackbox/testdata/renameDependency/before/lib.h create mode 100644 tests/auto/blackbox/testdata/renameDependency/before/main.cpp create mode 100644 tests/auto/blackbox/testdata/renameDependency/before/renameDependency.qbs create mode 100644 tests/auto/blackbox/testdata/reproducible-build/file1.cpp create mode 100644 tests/auto/blackbox/testdata/reproducible-build/file2.cpp create mode 100644 tests/auto/blackbox/testdata/reproducible-build/main.cpp create mode 100644 tests/auto/blackbox/testdata/reproducible-build/reproducible-build.qbs create mode 100644 tests/auto/blackbox/testdata/response-files/cat-response-file.cpp create mode 100644 tests/auto/blackbox/testdata/response-files/response-files.qbs create mode 100644 tests/auto/blackbox/testdata/rule-with-no-inputs/rule-with-no-inputs.qbs create mode 100644 tests/auto/blackbox/testdata/ruleConditions/foo.narf create mode 100644 tests/auto/blackbox/testdata/ruleConditions/main.cpp create mode 100644 tests/auto/blackbox/testdata/ruleConditions/modules/narfzort/narfzort.qbs create mode 100644 tests/auto/blackbox/testdata/ruleConditions/ruleConditions.qbs create mode 100644 tests/auto/blackbox/testdata/ruleConditions/templates/zorduct.qbs create mode 100644 tests/auto/blackbox/testdata/ruleCycle/happy.grass create mode 100644 tests/auto/blackbox/testdata/ruleCycle/ruleCycle.qbs create mode 100644 tests/auto/blackbox/testdata/separate-debug-info/foo.cpp create mode 100644 tests/auto/blackbox/testdata/separate-debug-info/main.cpp create mode 100644 tests/auto/blackbox/testdata/separate-debug-info/project.qbs create mode 100644 tests/auto/blackbox/testdata/soversion/lib.cpp create mode 100644 tests/auto/blackbox/testdata/soversion/soversion.qbs create mode 100644 tests/auto/blackbox/testdata/subprofile-change-tracking/main1.cpp create mode 100644 tests/auto/blackbox/testdata/subprofile-change-tracking/main2.cpp create mode 100644 tests/auto/blackbox/testdata/subprofile-change-tracking/subprofile-change-tracking.qbs create mode 100644 tests/auto/blackbox/testdata/successive-changes/input.in create mode 100644 tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs create mode 100644 tests/auto/blackbox/testdata/suspicious-calls/copy-command.qbs create mode 100644 tests/auto/blackbox/testdata/suspicious-calls/copy-eval.qbs create mode 100644 tests/auto/blackbox/testdata/suspicious-calls/copy-prepare.qbs create mode 100644 tests/auto/blackbox/testdata/suspicious-calls/copy-probe.qbs create mode 100644 tests/auto/blackbox/testdata/suspicious-calls/direntries-command.qbs create mode 100644 tests/auto/blackbox/testdata/suspicious-calls/direntries-eval.qbs create mode 100644 tests/auto/blackbox/testdata/suspicious-calls/direntries-prepare.qbs create mode 100644 tests/auto/blackbox/testdata/suspicious-calls/direntries-probe.qbs create mode 100644 tests/auto/blackbox/testdata/suspicious-calls/test.txt create mode 100644 tests/auto/blackbox/testdata/symlink-removal/symlink-removal.qbs create mode 100644 tests/auto/blackbox/testdata/system-run-paths/lib.cpp create mode 100644 tests/auto/blackbox/testdata/system-run-paths/main.cpp create mode 100644 tests/auto/blackbox/testdata/system-run-paths/system-run-paths.qbs create mode 100644 tests/auto/blackbox/testdata/toplevel-searchpath/qbs-resources/imports/MyProduct.qbs create mode 100644 tests/auto/blackbox/testdata/toplevel-searchpath/toplevel-searchpath.qbs create mode 100644 tests/auto/blackbox/testdata/trackAddFile/after/main.cpp create mode 100644 tests/auto/blackbox/testdata/trackAddFile/after/project.qbs create mode 100644 tests/auto/blackbox/testdata/trackAddFile/after/zort.cpp create mode 100644 tests/auto/blackbox/testdata/trackAddFile/after/zort.h create mode 100644 tests/auto/blackbox/testdata/trackAddFile/before/main.cpp create mode 100644 tests/auto/blackbox/testdata/trackAddFile/before/narf.cpp create mode 100644 tests/auto/blackbox/testdata/trackAddFile/before/narf.h create mode 100644 tests/auto/blackbox/testdata/trackAddFile/before/project.qbs create mode 100644 tests/auto/blackbox/testdata/trackAddMocInclude/after/main.cpp create mode 100644 tests/auto/blackbox/testdata/trackAddMocInclude/before/main.cpp create mode 100644 tests/auto/blackbox/testdata/trackAddMocInclude/before/test.qbs create mode 100644 tests/auto/blackbox/testdata/trackExternalProductChanges/environmentChange.cpp create mode 100644 tests/auto/blackbox/testdata/trackExternalProductChanges/fileList.js create mode 100644 tests/auto/blackbox/testdata/trackExternalProductChanges/hidden/hiddenheaderqbs.h create mode 100644 tests/auto/blackbox/testdata/trackExternalProductChanges/including.cpp create mode 100644 tests/auto/blackbox/testdata/trackExternalProductChanges/jsFileChange.cpp create mode 100644 tests/auto/blackbox/testdata/trackExternalProductChanges/main.cpp create mode 100644 tests/auto/blackbox/testdata/trackExternalProductChanges/project.qbs create mode 100644 tests/auto/blackbox/testdata/trackFileTags/after/main.cpp create mode 100644 tests/auto/blackbox/testdata/trackFileTags/after/project.qbs create mode 100644 tests/auto/blackbox/testdata/trackFileTags/before/main.cpp create mode 100644 tests/auto/blackbox/testdata/trackFileTags/before/project.qbs create mode 100644 tests/auto/blackbox/testdata/trackProducts/after/product3.qbs create mode 100644 tests/auto/blackbox/testdata/trackProducts/after/trackProducts.qbs create mode 100644 tests/auto/blackbox/testdata/trackProducts/after/zoo.cpp create mode 100644 tests/auto/blackbox/testdata/trackProducts/before/bar.cpp create mode 100644 tests/auto/blackbox/testdata/trackProducts/before/foo.cpp create mode 100644 tests/auto/blackbox/testdata/trackProducts/before/product1.qbs create mode 100644 tests/auto/blackbox/testdata/trackProducts/before/product2.qbs create mode 100644 tests/auto/blackbox/testdata/trackProducts/before/trackProducts.qbs create mode 100644 tests/auto/blackbox/testdata/trackQObjChange/bla.cpp create mode 100644 tests/auto/blackbox/testdata/trackQObjChange/bla_noqobject.h create mode 100644 tests/auto/blackbox/testdata/trackQObjChange/bla_qobject.h create mode 100644 tests/auto/blackbox/testdata/trackQObjChange/i.qbs create mode 100644 tests/auto/blackbox/testdata/transitive-optional-dependencies/modules/a/a.qbs create mode 100644 tests/auto/blackbox/testdata/transitive-optional-dependencies/modules/b/b.qbs create mode 100644 tests/auto/blackbox/testdata/transitive-optional-dependencies/transitive-optional-dependencies.qbs create mode 100644 tests/auto/blackbox/testdata/typescript/animals.ts create mode 100644 tests/auto/blackbox/testdata/typescript/extra.js create mode 100644 tests/auto/blackbox/testdata/typescript/foo.ts create mode 100644 tests/auto/blackbox/testdata/typescript/foo2.ts create mode 100644 tests/auto/blackbox/testdata/typescript/hello.ts create mode 100644 tests/auto/blackbox/testdata/typescript/main.ts create mode 100644 tests/auto/blackbox/testdata/typescript/typescript.qbs create mode 100644 tests/auto/blackbox/testdata/typescript/woosh/extra.ts create mode 100644 tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/custom1.in create mode 100644 tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/custom2.in create mode 100644 tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/project.qbs create mode 100644 tests/auto/blackbox/testdata/versioncheck/modules/higher/higher.qbs create mode 100644 tests/auto/blackbox/testdata/versioncheck/modules/lower/lower.qbs create mode 100644 tests/auto/blackbox/testdata/versioncheck/versioncheck.qbs create mode 100644 tests/auto/blackbox/testdata/versionscript/testlib.c create mode 100644 tests/auto/blackbox/testdata/versionscript/versionscript create mode 100644 tests/auto/blackbox/testdata/versionscript/versionscript.qbs create mode 100644 tests/auto/blackbox/testdata/wildcard_renaming/pioniere.txt create mode 100644 tests/auto/blackbox/testdata/wildcard_renaming/wildcard_renaming.qbs create mode 100644 tests/auto/blackbox/testdata/wildcards-and-rules/input1.inp create mode 100644 tests/auto/blackbox/testdata/wildcards-and-rules/project.qbs create mode 100644 tests/auto/blackbox/testdata/wix/ExampleScript.bat create mode 100644 tests/auto/blackbox/testdata/wix/QbsBootstrapper.wxs create mode 100644 tests/auto/blackbox/testdata/wix/QbsSetup.wxs create mode 100644 tests/auto/blackbox/testdata/wix/Qt.wxs create mode 100644 tests/auto/blackbox/testdata/wix/WiXInstallers.qbs create mode 100644 tests/auto/blackbox/testdata/wix/de.wxl create mode 100644 tests/auto/blackbox/testdata/xcode/xcode-project.qbs create mode 100644 tests/auto/blackbox/tst_blackbox.cpp create mode 100644 tests/auto/blackbox/tst_blackbox.h create mode 100644 tests/auto/blackbox/tst_blackboxbase.cpp create mode 100644 tests/auto/blackbox/tst_blackboxbase.h create mode 100644 tests/auto/blackbox/tst_blackboxjava.cpp create mode 100644 tests/auto/blackbox/tst_blackboxjava.h create mode 100644 tests/auto/blackbox/tst_clangdb.cpp create mode 100644 tests/auto/blackbox/tst_clangdb.h create mode 100644 tests/auto/buildgraph/buildgraph.pro create mode 100644 tests/auto/buildgraph/buildgraph.qbs create mode 100644 tests/auto/buildgraph/tst_buildgraph.cpp create mode 100644 tests/auto/cmdlineparser/cmdlineparser.pro create mode 100644 tests/auto/cmdlineparser/cmdlineparser.qbs create mode 100644 tests/auto/cmdlineparser/data/dirwithmultipleprojects/project.qbs create mode 100644 tests/auto/cmdlineparser/data/dirwithmultipleprojects/project2.qbs create mode 100644 tests/auto/cmdlineparser/data/dirwithnoprojects/.gitignore create mode 100644 tests/auto/cmdlineparser/data/dirwithoneproject/project.qbs create mode 100644 tests/auto/cmdlineparser/tst_cmdlineparser.cpp create mode 100644 tests/auto/language/language.pro create mode 100644 tests/auto/language/language.qbs create mode 100644 tests/auto/language/tst_language.cpp create mode 100644 tests/auto/shared.h create mode 100644 tests/auto/tools/tools.pro create mode 100644 tests/auto/tools/tools.qbs create mode 100644 tests/auto/tools/tst_tools.cpp create mode 100644 tests/benchmarker/activities.h create mode 100644 tests/benchmarker/benchmarker-main.cpp create mode 100644 tests/benchmarker/benchmarker.cpp create mode 100644 tests/benchmarker/benchmarker.h create mode 100644 tests/benchmarker/benchmarker.pro create mode 100644 tests/benchmarker/benchmarker.qbs create mode 100644 tests/benchmarker/commandlineparser.cpp create mode 100644 tests/benchmarker/commandlineparser.h create mode 100644 tests/benchmarker/exception.h create mode 100644 tests/benchmarker/runsupport.cpp create mode 100644 tests/benchmarker/runsupport.h create mode 100644 tests/benchmarker/valgrindrunner.cpp create mode 100644 tests/benchmarker/valgrindrunner.h create mode 100644 tests/fuzzy-test/commandlineparser.cpp create mode 100644 tests/fuzzy-test/commandlineparser.h create mode 100644 tests/fuzzy-test/fuzzy-test.pro create mode 100644 tests/fuzzy-test/fuzzy-test.qbs create mode 100644 tests/fuzzy-test/fuzzytester.cpp create mode 100644 tests/fuzzy-test/fuzzytester.h create mode 100644 tests/fuzzy-test/main.cpp create mode 100644 tests/manual/configure/configure.qbs create mode 100644 tests/manual/configure/main.cpp create mode 100644 tests/manual/configure/modules/definition/module.qbs create mode 100644 tests/manual/includeLookup/includeLookup.qbs create mode 100644 tests/manual/includeLookup/main.cpp create mode 100644 tests/manual/includeLookup/modules/definition/module.qbs create mode 100644 tests/manual/localDeployment/localDeployment.qbs create mode 100644 tests/manual/localDeployment/main.cpp create mode 100644 tests/manual/minimumSystemVersion/main.cpp create mode 100644 tests/manual/minimumSystemVersion/minimumSystemVersion.qbs create mode 100644 tests/manual/pkgconfig/main.cpp create mode 100644 tests/manual/pkgconfig/pkgconfig.qbs create mode 100644 tests/manual/run-qbs-tests.bat create mode 100755 tests/manual/run-qbs-tests.sh create mode 100644 tests/tests.pro diff --git a/LGPL_EXCEPTION.txt b/LGPL_EXCEPTION.txt new file mode 100644 index 00000000..918157a3 --- /dev/null +++ b/LGPL_EXCEPTION.txt @@ -0,0 +1,22 @@ +The Qt Company LGPL Exception version 1.1 + +As an additional permission to the GNU Lesser General Public License version +2.1, the object code form of a "work that uses the Library" may incorporate +material from a header file that is part of the Library. You may distribute +such object code under terms of your choice, provided that: + (i) the header files of the Library have not been modified; and + (ii) the incorporated material is limited to numerical parameters, data + structure layouts, accessors, macros, inline functions and + templates; and + (iii) you comply with the terms of Section 6 of the GNU Lesser General + Public License version 2.1. + +Moreover, you may apply this exception to a modified version of the Library, +provided that such modification does not involve copying material from the +Library into the modified Library's header files unless such material is +limited to (i) numerical parameters; (ii) data structure layouts; +(iii) accessors; and (iv) small macros, templates and inline functions of +five lines or less in length. + +Furthermore, you are not required to apply this additional permission to a +modified version of the Library. diff --git a/LICENSE.LGPLv21 b/LICENSE.LGPLv21 new file mode 100644 index 00000000..602bfc94 --- /dev/null +++ b/LICENSE.LGPLv21 @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/LICENSE.LGPLv3 b/LICENSE.LGPLv3 new file mode 100644 index 00000000..509cc641 --- /dev/null +++ b/LICENSE.LGPLv3 @@ -0,0 +1,173 @@ + GNU LESSER GENERAL PUBLIC LICENSE + + The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd. + Contact: http://www.qt.io/licensing + + You may use, distribute and copy the Qt GUI Toolkit under the terms of + GNU Lesser General Public License version 3, which is displayed below. + +------------------------------------------------------------------------- + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright © 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies of this +licensedocument, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + + As used herein, “this License” refers to version 3 of the GNU Lesser +General Public License, and the “GNU GPL” refers to version 3 of the +GNU General Public License. + + “The Library” refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An “Application” is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A “Combined Work” is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the “Linked +Version”. + + The “Minimal Corresponding Source” for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The “Corresponding Application Code” for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort + to ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this + license document. + +4. Combined Works. + + You may convey a Combined Work under terms of your choice that, taken +together, effectively do not restrict modification of the portions of +the Library contained in the Combined Work and reverse engineering for +debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this + license document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of + this License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with + the Library. A suitable mechanism is one that (a) uses at run + time a copy of the Library already present on the user's + computer system, and (b) will operate properly with a modified + version of the Library that is interface-compatible with the + Linked Version. + + e) Provide Installation Information, but only if you would + otherwise be required to provide such information under section 6 + of the GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the Application + with a modified version of the Linked Version. (If you use option + 4d0, the Installation Information must accompany the Minimal + Corresponding Source and Corresponding Application Code. If you + use option 4d1, you must provide the Installation Information in + the manner specified by section 6 of the GNU GPL for conveying + Corresponding Source.) + +5. Combined Libraries. + + You may place library facilities that are a work based on the Library +side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of + it is a work based on the Library, and explaining where to find + the accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +as you received it specifies that a certain numbered version of the +GNU Lesser General Public License “or any later version” applies to +it, you have the option of following the terms and conditions either +of that published version or of any later version published by the +Free Software Foundation. If the Library as you received it does not +specify a version number of the GNU Lesser General Public License, +you may choose any version of the GNU Lesser General Public License +ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the Library. + diff --git a/README b/README new file mode 100644 index 00000000..f3614ca2 --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +Qbs +=== +Qbs is a cross-platform build tool. +The project's homepage is http://wiki.qt.io/qbs + +Supported Platforms +=================== + +Windows XP SP2 or later +macOS 10.7 or later +Linux (tested on Debian 6/7 and Ubuntu 13) + +Building the sources requires Qt 5.4.0 or later. + +Build Instructions +================== +Prerequisites: + * Qt 5.4.0 or later + * On Windows: + - MinGW or Visual Studio + * On macOS: Xcode + +The installed toolchains have to match the one Qt was compiled with. + +You can build Qbs with + + cd $SOURCE_DIRECTORY + qmake -r + make (or mingw32-make or nmake or jom, depending on your platform) + +Installation ("make install") is not needed. It is however possible, using + + make install INSTALL_ROOT=$INSTALL_DIRECTORY + diff --git a/bin/ibmsvc.xml b/bin/ibmsvc.xml new file mode 100644 index 00000000..cea5990c --- /dev/null +++ b/bin/ibmsvc.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/bin/ibqbs.bat b/bin/ibqbs.bat new file mode 100644 index 00000000..0e1cb2bd --- /dev/null +++ b/bin/ibqbs.bat @@ -0,0 +1 @@ +@xgConsole /profile=%~dp0\ibmsvc.xml /command="qbs -j 20 %*" diff --git a/dist/dist.qbs b/dist/dist.qbs new file mode 100644 index 00000000..c5c247ca --- /dev/null +++ b/dist/dist.qbs @@ -0,0 +1,176 @@ +import qbs +import qbs.FileInfo +import qbs.ModUtils +import qbs.Process +import qbs.TextFile +import QbsFunctions + +Product { + Depends { name: "qbs-config" } + Depends { name: "qbs-config-ui" } + Depends { name: "qbs-qmltypes" } + Depends { name: "qbs-setup-android" } + Depends { name: "qbs-setup-qt" } + Depends { name: "qbs-setup-toolchains" } + Depends { name: "qbs_app" } + Depends { name: "qbscore" } + Depends { name: "qbsqtprofilesetup" } + Depends { name: "qbs_cpp_scanner" } + Depends { name: "qbs_qt_scanner" } + Depends { name: "documentation" } + Depends { name: "qbs resources" } + + Depends { name: "archiver" } + Depends { name: "Qt.core" } + + readonly property bool hasQt56: { + if (Qt.core.versionMajor === 5) + return Qt.core.versionMinor >= 6; + return Qt.core.versionMajor > 5; + } + + property stringList windeployqtArgs: [ + "--no-svg", + "--no-system-d3d-compiler", + "--no-angle", + "--no-compiler-runtime", + ].concat(hasQt56 ? ["--no-opengl-sw"] : []) + + // List of path prefixes to be excluded from the generated archive + property stringList excludedPathPrefixes: [ + "bin/icudt", + "bin/icuin", + "bin/icuuc", + "bin/iconengines/", + "bin/imageformats/", + ] + property bool includeTopLevelDir: false + + condition: qbs.targetOS.contains("windows") + builtByDefault: false + type: ["archiver.archive"] + targetName: "qbs-windows-" + qbs.architecture + "-" + QbsFunctions.qbsVersion() + destinationDirectory: project.buildDirectory + + archiver.type: "zip" + Properties { + condition: includeTopLevelDir + archiver.workingDirectory: qbs.installRoot + "/.." + } + archiver.workingDirectory: qbs.installRoot + + Rule { + multiplex: true + inputsFromDependencies: ["installable"] + + Artifact { + filePath: "windeployqt.json" + fileTags: ["dependencies.json"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "windeployqt"; + cmd.outputFilePath = output.filePath; + cmd.installRoot = product.moduleProperty("qbs", "installRoot"); + cmd.windeployqtArgs = product.windeployqtArgs; + cmd.binaryFilePaths = inputs.installable.filter(function (artifact) { + return artifact.fileTags.contains("application") + || artifact.fileTags.contains("dynamiclibrary"); + }).map(function(a) { return ModUtils.artifactInstalledFilePath(a); }); + cmd.extendedDescription = FileInfo.joinPaths( + product.moduleProperty("Qt.core", "binPath"), "windeployqt") + ".exe " + + ["--json"].concat(cmd.windeployqtArgs).concat(cmd.binaryFilePaths).join(" "); + cmd.sourceCode = function () { + var out; + var process; + try { + process = new Process(); + process.exec(FileInfo.joinPaths(product.moduleProperty("Qt.core", "binPath"), + "windeployqt"), ["--json"] + .concat(windeployqtArgs).concat(binaryFilePaths), true); + out = process.readStdOut(); + } finally { + if (process) + process.close(); + } + + var tf; + try { + tf = new TextFile(outputFilePath, TextFile.WriteOnly); + tf.write(out); + } finally { + if (tf) + tf.close(); + } + }; + return [cmd]; + } + } + + Rule { + multiplex: true + inputs: ["dependencies.json"] + inputsFromDependencies: ["installable"] + + Artifact { + filePath: "list.txt" + fileTags: ["archiver.input-list"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.excludedPathPrefixes = product.excludedPathPrefixes; + cmd.inputFilePaths = inputs.installable.map(function(a) { + return ModUtils.artifactInstalledFilePath(a); + }); + cmd.outputFilePath = output.filePath; + cmd.baseDirectory = product.moduleProperty("archiver", "workingDirectory"); + cmd.sourceCode = function() { + var tf; + for (var i = 0; i < inputs["dependencies.json"].length; ++i) { + try { + tf = new TextFile(inputs["dependencies.json"][i].filePath, + TextFile.ReadOnly); + inputFilePaths = inputFilePaths.concat( + JSON.parse(tf.readAll())["files"].map(function (obj) { + return FileInfo.joinPaths( + FileInfo.fromWindowsSeparators(obj.target), + FileInfo.fileName( + FileInfo.fromWindowsSeparators( + obj.source))); + })); + } finally { + if (tf) + tf.close(); + } + } + + inputFilePaths.sort(); + + try { + tf = new TextFile(outputFilePath, TextFile.ReadWrite); + for (var i = 0; i < inputFilePaths.length; ++i) { + var ignore = false; + var relativePath = FileInfo.relativePath(baseDirectory, inputFilePaths[i]); + for (var j = 0; j < excludedPathPrefixes.length; ++j) { + if (relativePath.startsWith(excludedPathPrefixes[j])) { + ignore = true; + break; + } + } + + if (!ignore) + tf.writeLine(relativePath); + } + } finally { + if (tf) + tf.close(); + } + }; + + return [cmd]; + } + } +} diff --git a/doc/classic.css b/doc/classic.css new file mode 100644 index 00000000..f97bdbea --- /dev/null +++ b/doc/classic.css @@ -0,0 +1,295 @@ +BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { + font-family: Arial, Geneva, Helvetica, sans-serif; +} +H1 { + text-align: center; + font-size: 160%; +} +H2 { + font-size: 120%; +} +H3 { + font-size: 100%; +} + +h3.fn,span.fn +{ + background-color: #eee; + border-width: 1px; + border-style: solid; + border-color: #ddd; + font-weight: bold; + padding: 6px 0px 6px 10px; + margin: 42px 0px 0px 0px; +} + +hr { + border: 0; + color: #a0a0a0; + background-color: #ccc; + height: 1px; + width: 100%; + text-align: left; + margin: 34px 0px 34px 0px; +} + +table.valuelist { + border-width: 1px 1px 1px 1px; + border-style: solid; + border-color: #dddddd; + border-collapse: collapse; + background-color: #f0f0f0; +} + +table.indextable { + border-width: 1px 1px 1px 1px; + border-collapse: collapse; + background-color: #f0f0f0; + border-color:#555; + font-size: 110%; +} + +table td.largeindex { + border-width: 1px 1px 1px 1px; + border-collapse: collapse; + background-color: #f0f0f0; + border-color:#555; + font-size: 120%; +} + +table.valuelist th { + border-width: 1px 1px 1px 2px; + padding: 4px; + border-style: solid; + border-color: #666; + color:white; + background-color:#666; +} + +th.titleheader { + border-width: 1px 0px 1px 0px; + padding: 4px; + border-style: solid; + border-color: #444; + color:white; + background-color:#555555; + font-size: 110%; +} + +th.largeheader { + border-width: 1px 0px 1px 0px; + padding: 4px; + border-style: solid; + border-color: #444; + color:white; + background-color:#555555; + font-size: 120%; +} + +p { + + margin-left: 4px; + margin-top: 8px; + margin-bottom: 8px; +} + +a:link +{ + color: #0046ad; + text-decoration: none +} + +a:visited +{ + color: #672967; + text-decoration: none +} + +a.obsolete +{ + color: #661100; + text-decoration: none +} + +a.compat +{ + color: #661100; + text-decoration: none +} + +a.obsolete:visited +{ + color: #995500; + text-decoration: none +} + +a.compat:visited +{ + color: #995500; + text-decoration: none +} + +body +{ + background: #ffffff; + color: black +} + +table.generic, table.annotated +{ + border-width: 1px; + border-color:#bbb; + border-style:solid; + border-collapse:collapse; +} + +table td.memItemLeft { + width: 180px; + padding: 2px 0px 0px 8px; + margin: 4px; + border-width: 1px; + border-color: #E0E0E0; + border-style: none; + font-size: 100%; + white-space: nowrap +} + +table td.memItemRight { + padding: 2px 8px 0px 8px; + margin: 4px; + border-width: 1px; + border-color: #E0E0E0; + border-style: none; + font-size: 100%; +} + +table tr.odd { + background: #f0f0f0; + color: black; +} + +table tr.even { + background: #e4e4e4; + color: black; +} + +table.annotated th { + padding: 3px; + text-align: left +} + +table.annotated td { + padding: 3px; +} + +table tr pre +{ + padding-top: 0px; + padding-bottom: 0px; + padding-left: 0px; + padding-right: 0px; + border: none; + background: none +} + +tr.qt-style +{ + background: #96E066; + color: black +} + +body pre +{ + padding: 0.2em; + border: #e7e7e7 1px solid; + background: #f1f1f1; + color: black +} + +table tr.qt-code pre +{ + padding: 0.2em; + border: #e7e7e7 1px solid; + background: #f1f1f1; + color: black +} + +span.preprocessor, span.preprocessor a +{ + color: darkblue; +} + +span.comment +{ + color: darkred; + font-style: italic +} + +span.string,span.char +{ + color: darkgreen; +} + +.title +{ + text-align: center +} + +.subtitle +{ + font-size: 0.8em +} + +.small-subtitle +{ + font-size: 0.65em +} + +.qmlitem { + padding: 0; +} + +.qmlname { + white-space: nowrap; + font-weight: bold; + font-size: 125%; +} + +.qmltype { + font-weight: bold; + font-size: 125%; +} + +.qmlproto, .qmldoc { + // border-top: 1px solid #84b0c7; +} + +.qmlproto { + padding: 0; + //background-color: #e4e4e4;//#d5e1e8; + //font-weight: bold; + //-webkit-border-top-left-radius: 8px; + //-webkit-border-top-right-radius: 8px; + //-moz-border-radius-topleft: 8px; + //-moz-border-radius-topright: 8px; +} + +.qmldoc { + border-top: 1px solid #e4e4e4; + //padding: 2px 5px; + //background-color: #eef3f5; + //border-top-width: 0; + //-webkit-border-bottom-left-radius: 8px; + //-webkit-border-bottom-right-radius: 8px; + //-moz-border-radius-bottomleft: 8px; + //-moz-border-radius-bottomright: 8px; +} + +.qmldoc p, .qmldoc dl, .qmldoc ul { + //margin: 6px 0; +} + +*.qmlitem p { + //margin-top: 0px; + //margin-bottom: 0px; +} diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf new file mode 100644 index 00000000..57ff7274 --- /dev/null +++ b/doc/config/macros.qdocconf @@ -0,0 +1,2 @@ +macro.QBS = "Qbs" +macro.qbsversion = $QBS_VERSION diff --git a/doc/config/qbs-project.qdocconf b/doc/config/qbs-project.qdocconf new file mode 100644 index 00000000..06808580 --- /dev/null +++ b/doc/config/qbs-project.qdocconf @@ -0,0 +1,39 @@ +include($QT_INSTALL_DOCS/global/macros.qdocconf) +include($QT_INSTALL_DOCS/global/qt-cpp-defines.qdocconf) +include($QT_INSTALL_DOCS/global/compat.qdocconf) +include($QT_INSTALL_DOCS/global/fileextensions.qdocconf) + +project = "Qbs" +description = "Qbs Manual" + +headerdirs = +sourcedirs = $SRCDIR +imagedirs = $SRCDIR/images $SRCDIR/templates/images +outputdir = $OUTDIR +exampledirs = $SRCDIR + +include(macros.qdocconf) +sources.fileextensions = "*.qdoc" + + +qhp.projects = Qbs +qhp.Qbs.file = qbs.qhp +qhp.Qbs.namespace = org.qt-project.qbs.$QBS_VERSION_TAG +qhp.Qbs.virtualFolder = doc +qhp.Qbs.indexTitle = Qbs +qhp.Qbs.filterAttributes = qbs $QBS_VERSION +qhp.Qbs.customFilters.Qbs.name = Qbs $QBS_VERSION +qhp.Qbs.customFilters.Qbs.filterAttributes = qbs $QBS_VERSION +qhp.Qbs.indexRoot = + +qhp.Qbs.subprojects = manual +qhp.Qbs.subprojects.manual.title = Qbs Manual +qhp.Qbs.subprojects.manual.indexTitle = Qbs Manual +qhp.Qbs.subprojects.manual.type = manual + +# Doxygen compatibility commands +macro.see = "\\sa" +macro.function = "\\fn" + +navigation.homepage = "Qbs Manual" +buildversion = "Qbs $QBS_VERSION" diff --git a/doc/config/style/qt5-sidebar.html b/doc/config/style/qt5-sidebar.html new file mode 100644 index 00000000..fb15b30f --- /dev/null +++ b/doc/config/style/qt5-sidebar.html @@ -0,0 +1,13 @@ +
+
+ +

Qbs Manual

+
+ diff --git a/doc/doc.pri b/doc/doc.pri new file mode 100644 index 00000000..4a728058 --- /dev/null +++ b/doc/doc.pri @@ -0,0 +1,66 @@ +include(../src/install_prefix.pri) + +defineReplace(targetPath) { + return($$replace(1, /, $$QMAKE_DIR_SEP)) +} + +QDOC_BIN = $$targetPath($$[QT_INSTALL_BINS]/qdoc) +QDOC_MAINFILE = $$PWD/qbs.qdocconf +HELPGENERATOR = $$targetPath($$[QT_INSTALL_BINS]/qhelpgenerator) + +VERSION_TAG = $$replace(QBS_VERSION, "[-.]", ) + +HTML_DOC_PATH=$$OUT_PWD/doc/html +equals(QMAKE_DIR_SEP, /) { # unix, mingw+msys + QDOC = SRCDIR=$$PWD OUTDIR=$$HTML_DOC_PATH QBS_VERSION=$$QBS_VERSION QBS_VERSION_TAG=$$VERSION_TAG QT_INSTALL_DOCS=$$[QT_INSTALL_DOCS] $$QDOC_BIN +} else:win32-g++* { # just mingw + # The lack of spaces in front of the && is necessary! + QDOC = set SRCDIR=$$PWD&& set OUTDIR=$$HTML_DOC_PATH&& set QBS_VERSION=$$QBS_VERSION&& set QBS_VERSION_TAG=$$VERSION_TAG&& set QT_INSTALL_DOCS=$$[QT_INSTALL_DOCS]&& $$QDOC_BIN +} else { # nmake + QDOC = set SRCDIR=$$PWD $$escape_expand(\\n\\t) \ + set OUTDIR=$$HTML_DOC_PATH $$escape_expand(\\n\\t) \ + set QBS_VERSION=$$QBS_VERSION $$escape_expand(\\n\\t) \ + set QBS_VERSION_TAG=$$VERSION_TAG $$escape_expand(\\n\\t) \ + set QT_INSTALL_DOCS=$$[QT_INSTALL_DOCS] $$escape_expand(\\n\\t) \ + $$QDOC_BIN +} + +QHP_FILE = $$HTML_DOC_PATH/qbs.qhp +QCH_FILE = $$OUT_PWD/doc/qbs.qch + +HELP_DEP_FILES = $$PWD/qbs.qdoc \ + $$QDOC_MAINFILE + +html_docs.commands = $$QDOC $$PWD/qbs.qdocconf +html_docs.depends += $$HELP_DEP_FILES + +html_docs_online.commands = $$QDOC $$PWD/qbs-online.qdocconf +html_docs_online.files = $$QHP_FILE + +qch_docs.commands = $$HELPGENERATOR -o $$shell_quote($$QCH_FILE) $$QHP_FILE +qch_docs.depends += html_docs + +docs_online.depends = html_docs_online +QMAKE_EXTRA_TARGETS += html_docs_online docs_online + +inst_qch_docs.files = $$QCH_FILE +inst_qch_docs.path = $${QBS_INSTALL_PREFIX}/share/doc/qbs +inst_qch_docs.CONFIG += no_check_exist no_default_install +INSTALLS += inst_qch_docs + +inst_html_docs.files = $$HTML_DOC_PATH +inst_html_docs.path = $$inst_qch_docs.path +inst_html_docs.CONFIG += no_check_exist no_default_install directory +INSTALLS += inst_html_docs + +install_docs.depends = install_inst_qch_docs install_inst_html_docs +QMAKE_EXTRA_TARGETS += install_docs + +docs.depends = qch_docs +QMAKE_EXTRA_TARGETS += html_docs qch_docs docs + +fixnavi.commands = \ + cd $$targetPath($$PWD) && \ + perl fixnavi.pl -Dqcmanual -Dqtquick \ + qbs.qdoc +QMAKE_EXTRA_TARGETS += fixnavi diff --git a/doc/doc.qbs b/doc/doc.qbs new file mode 100644 index 00000000..f7efdd23 --- /dev/null +++ b/doc/doc.qbs @@ -0,0 +1,35 @@ +import qbs 1.0 +import QbsFunctions + +Product { + name: "documentation" + builtByDefault: false + type: "qch" + Depends { name: "Qt.core" } + + files: [ + "qbs.qdoc", + "config/*.qdocconf", + "reference/**/*", + ] + Group { + name: "main qdocconf file" + files: "qbs.qdocconf" + fileTags: "qdocconf-main" + } + + property string versionTag: QbsFunctions.qbsVersion().replace(/\.|-/g, "") + Qt.core.qdocEnvironment: [ + "QBS_VERSION=" + QbsFunctions.qbsVersion(), + "SRCDIR=" + path, + "QT_INSTALL_DOCS=" + Qt.core.docPath, + "QBS_VERSION_TAG=" + versionTag + ] + + Group { + fileTagsFilter: ["qdoc-output"] + qbs.install: true + qbs.installDir: "share/doc/qbs/html" + qbs.installSourceBase: Qt.core.qdocOutputDir + } +} diff --git a/doc/fixnavi.pl b/doc/fixnavi.pl new file mode 100644 index 00000000..3c4dc134 --- /dev/null +++ b/doc/fixnavi.pl @@ -0,0 +1,182 @@ +#! /usr/bin/perl -w + +############################################################################# +## +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qbs. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/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 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# +use strict; + +my @files = (); +my %defines = (); +for (@ARGV) { + if (/^-D(.*)$/) { + $defines{$1} = 1; + } elsif (/^-/) { + printf STDERR "Unknown option '".$_."'\n"; + exit 1; + } else { + push @files, $_; + } +} + +int(@files) or die "usage: $0 [-D]... \n"; + +my @toc = (); +my %title2page = (); +my $doctitle = ""; +my $curpage = ""; +my $intoc = 0; +my %prev_skips = (); +my %next_skips = (); +my %define_skips = (); +my %polarity_skips = (); +my $prev_skip = ""; +my $next_skip = ""; +my $define_skip = ""; +my $polarity_skip = 0; +for my $file (@files) { + my $skipping = 0; # no nested ifs! + open FILE, $file or die "File $file cannot be opened.\n"; + while () { + if (/^\h*\\if\h+defined\h*\(\h*(\H+)\h*\)/) { + $skipping = !defined($defines{$1}); + if (!$intoc) { + $define_skip = $1; + $polarity_skip = $skipping; + } + } elsif (/^\h*\\else/) { + $skipping = 1 - $skipping; + } elsif (/^\h*\\endif/) { + $skipping = 0; + } elsif (keys(%title2page) == 1 && /^\h*\\list/) { + $intoc++; + } elsif (!$intoc) { + if ($skipping && /^\h*\\previouspage\h+(\H+)/) { + $prev_skip = $1; + } elsif ($skipping && /^\h*\\nextpage\h+(\H+)/) { + $next_skip = $1; + } elsif (/^\h*\\page\h+(\H+)/) { + $curpage = $1; + } elsif (/^\h*\\title\h+(.+)$/) { + if ($curpage eq "") { + die "Title '$1' appears in no \\page.\n"; + } + if (length($define_skip)) { + $define_skips{$1} = $define_skip; + $polarity_skips{$1} = $polarity_skip; + $prev_skips{$1} = $prev_skip; + $next_skips{$1} = $next_skip; + $define_skip = $prev_skip = $next_skip = ""; + } + $title2page{$1} = $curpage; + $doctitle = $1 if (!$doctitle); + $curpage = ""; + } + } else { + if (/^\h*\\endlist/) { + $intoc--; + } elsif (!$skipping && /^\h*\\(?:o|li)\h+\\l\h*{(.*)}$/) { + push @toc, $1; + } + } + } + close FILE; +} + +my %prev = (); +my %next = (); +my $last = $doctitle; +for my $title (@toc) { + $next{$last} = $title2page{$title}; + $prev{$title} = $title2page{$last}; + $last = $title; +} + +for my $file (@files) { + open IN, $file or die "File $file cannot be opened a second time?!\n"; + open OUT, '>'.$file.".out" or die "File $file.out cannot be created.\n"; + my $cutting = 0; + while () { + if (!$cutting) { + if (/^\h*\\contentspage/) { + $cutting = 1; + } + } else { + if (/^\h*\\title\h+(.+)$/) { + if (defined($define_skips{$1})) { + print OUT " \\if defined(".$define_skips{$1}.")\n"; + if ($polarity_skips{$1}) { + print OUT " \\previouspage ".$prev_skips{$1} if ($prev_skips{$1}); + print OUT " \\else\n"; + } + } + print OUT " \\previouspage ".$prev{$1} if ($prev{$1}); + if (defined($define_skips{$1})) { + if (!$polarity_skips{$1}) { + print OUT " \\else\n"; + print OUT " \\previouspage ".$prev_skips{$1} if ($prev_skips{$1}); + } + print OUT " \\endif\n"; + } + print OUT " \\page ".$title2page{$1}; + if (defined($define_skips{$1})) { + print OUT " \\if defined(".$define_skips{$1}.")\n"; + if ($polarity_skips{$1}) { + print OUT " \\nextpage ".$next_skips{$1} if ($next_skips{$1}); + print OUT " \\else\n"; + } + } + print OUT " \\nextpage ".$next{$1} if ($next{$1}); + if (defined($define_skips{$1})) { + if (!$polarity_skips{$1}) { + print OUT " \\else\n"; + print OUT " \\nextpage ".$next_skips{$1} if ($next_skips{$1}); + } + print OUT " \\endif\n"; + } + print OUT "\n"; + $cutting = 0; + } else { + next; + } + } + print OUT $_; + } + close OUT; + close IN; + rename($file.".out", $file) or die "Cannot replace $file with new version.\n"; +} diff --git a/doc/qbs-online.qdocconf b/doc/qbs-online.qdocconf new file mode 100644 index 00000000..6ca41627 --- /dev/null +++ b/doc/qbs-online.qdocconf @@ -0,0 +1,19 @@ +include(config/qbs-project.qdocconf) + +HTML.footer = \ + "
\n" \ + "

\n" \ + " © 2016 The Qt Company Ltd.\n" \ + " Documentation contributions included herein are the copyrights of\n" \ + " their respective owners. " \ + " The documentation provided herein is licensed under the terms of the" \ + " GNU Free Documentation" \ + " License version 1.3 as published by the Free Software Foundation. " \ + " Qt and respective logos are trademarks of The Qt Company Ltd " \ + " in Finland and/or other countries worldwide. All other trademarks are property\n" \ + " of their respective owners.

\n" + +include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf) + +# Add an .html file with sidebar content, used in the online style +HTML.stylesheets += config/style/qt5-sidebar.html diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc new file mode 100644 index 00000000..0d3d576f --- /dev/null +++ b/doc/qbs.qdoc @@ -0,0 +1,872 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// ********************************************************************** +// NOTE: the sections are not ordered by their logical order to avoid +// reshuffling the file each time the index order changes (i.e., often). +// Run the fixnavi.pl script to adjust the links to the index order. +// ********************************************************************** + + +/*! + \contentspage{index.html}{Qbs} + \page index.html + \nextpage overview.html + + \title Qbs Manual + + \section1 Version \qbsversion + + \QBS is a tool that helps simplify the build process for + developing projects across multiple platforms. \QBS can be used for any + software project, regardless of programming language, toolkit, or libraries used. + + \QBS is an all-in-one tool that generates a build graph from a high-level + project description (like qmake or CMake) and additionally undertakes the + task of executing the commands in the low-level build graph (like make). + + \note Please report bugs and suggestions to the + \l{http://bugreports.qt.io/}{Qt Bug Tracker}. + + \list + \li \l{Introduction} + \li \l{Setup} + \list + \li \l{System Requirements} + \li \l{Building} + \li \l{Configuring} + \li \l{Managing Qt Versions} + \endlist + \li \l{Usage} + \list + \li \l{Language Introduction} + \li \l{Building Applications} + \li \l{Running Applications} + \li \l{Installing Files} + \li \l{Using the Shell} + \li \l{Custom Modules and Items} + \endlist + \li \l{Reference} + \endlist +*/ + + +/*! + \contentspage index.html + \previouspage index.html + \page overview.html + \nextpage setup.html + + \title Introduction + + \QBS builds applications based on the information in a project file that you + specify in a QML dialect. Each project file specifies one project that can + contain several \l{Product Item}{products}. You specify the type of the product: application, + library, and so on. +*/ + + +/*! + \contentspage index.html + \previouspage overview.html + \page setup.html + \nextpage system-requirements.html + + \title Setup + + \list + \li \l{System Requirements} + \li \l{Building} + \li \l{Configuring} + \li \l{Managing Qt Versions} + \endlist +*/ + + +/*! + \contentspage index.html + \previouspage setup.html + \page system-requirements.html + \nextpage building.html + + \title System Requirements + + To build \QBS from the source, you need Qt 5.4.0, or later + +*/ + + +/*! + \contentspage index.html + \previouspage system-requirements.html + \page building.html + \nextpage configuring.html + + \title Building + + To build \QBS, enter the following command: + + \code + qmake -r qbs.pro && make + \endcode + + \section1 Configure Options + + \QBS recognizes the following qmake CONFIG options to customize the build: + + \table + \header \li Option \li Notes + \row \li qbs_enable_unit_tests \li Enable additional autotests. + \row \li qbs_disable_rpath \li Disable the use of rpath. This can be used when packaging + \QBS for distributions which do not permit the use of rpath, + such as Fedora. + \row \li qbs_no_dev_install \li Exclude header files from installation, that is, perform a + non-developer build. + \row \li qbs_enable_project_file_updates \li Enable API for updating project files. This + implies a dependency to the Qt GUI module. + \endtable + +*/ + + +/*! + \contentspage index.html + \previouspage building.html + \page configuring.html + \nextpage qt-versions.html + + \title Configuring + + Open a build shell (on Windows open an MSVC command prompt, + on other platforms you can usually open the default shell): + \code + qbs setup-toolchains --detect + \endcode + + The tool chain detector automatically sets up a profile for each detected tool chain. + You can list the existing profiles by running: + \code + qbs config --list profiles + \endcode + + Now you should be ready to build your first project with \QBS. + Go into qbs/tests/manual/hello and type: + + \code + qbs profile: + \endcode + + If you want to build projects that use Qt, additional steps are necessary. Please refer to + \l{Managing Qt Versions} for more information. + +*/ + +/*! + \contentspage index.html + \previouspage configuring.html + \page qt-versions.html + \nextpage usage.html + + \title Managing Qt Versions + + \section1 Introduction + + To let \QBS know where the Qt build or Qt version is that you want to use, + you must register it. + + Register a Qt version like this: + \code + qbs setup-qt /usr/bin/qmake myqt + \endcode + + This will create the \c myqt profile which can then be used on + the command line: + + \code + qbs profile:myqt + \endcode + + \note If the \c setup-toolchains command has found more than one toolchain, you will need + to manually link your Qt profile to one of them, like this: + \code + qbs config profiles.myqt.baseProfile + \endcode + + + \section1 Multiple Qt Builds + + To support multiple Qt builds, or in fact any combination of related settings, you need to + create several profiles. The following example illustrates how to set up + three different profiles, each for a different Qt build: + + \code + qbs setup-qt ~/dev/qt/4.7/bin/qmake qt47 + qbs setup-qt ~/dev/qt/4.8/bin/qmake qt48 + qbs setup-qt ~/dev/qt/5.0/qtbase/bin/qmake qt5 + \endcode + + You can set the default Qt build like this: + + \code + qbs config defaultProfile qt5 + \endcode + + To choose a Qt build that is different from the default, use: + + \code + qbs build profile:qt48 + \endcode + + You can set other properties in a profile (not just Qt ones), in the same way + you override them from the command line. For example: + + \code + qbs setup-qt C:\Qt\5.0.0\qtbase\bin\qmake.exe qt5 + qbs config profiles.qt5.qbs.architecture x86_64 + qbs config profiles.qt5.baseProfile msvc2010 + \endcode + + The last example uses the inheritance feature of profiles. All settings in the profile + set as \c baseProfile are known in the derived profile as well. + They can of course be overridden there. +*/ + +/*! + \contentspage index.html + \previouspage qt-versions.html + \page usage.html + \nextpage language-introduction.html + + \title Usage + + \list + \li \l{Language Introduction} + \li \l{Building Applications} + \li \l{Running Applications} + \li \l{Installing Files} + \li \l{Using the Shell} + \li \l{Custom Modules and Items} + \endlist + +*/ + + +/*! + \contentspage index.html + \previouspage usage.html + \page language-introduction.html + \nextpage building-applications.html + + \title Language Introduction + + \QBS uses project files (*.qbs) to describe the contents of a project. + A project contains one or more \l{Product Item}{products}. A product is the target of a build + process, typically an application, library or maybe a tar ball. + \note \QBS source files are assumed to be UTF-8 encoded. + + \section1 The Obligatory Hello World Example + + \QBS project files are written using a QML dialect. + A very simple C++ hello world project looks like this: + \code + ---helloworld.qbs--- + import qbs 1.0 + + Application { + name: "helloworld" + files: "main.cpp" + Depends { name: "cpp" } + } + \endcode + + The import statement gives us access to some built-in types and specifies the + used language version. + + \a Application describes the product we want to build. In this case, an + application. This is just a shortcut for writing + \code + Product { + type: "application" + // ... + } + \endcode + + The \a name is the name of the product. In this case it is also the + name of the produced executable (on Windows, the ".exe" extension is added by default). + + In the property \a files, we specify the source files for our product. + Unlike QML, the right-hand side can be either a string or a string list. + A single string is converted to a stringlist containing just one element. + So we could have also written + + \code + files: [ "main.cpp" ] + \endcode + + \a Depends adds the dependency to the module \l{Module cpp}{cpp}. This is necessary to let \QBS know that + we have a C++ project and want to compile main.cpp with a C++ compiler. For more information + about \QBS modules, see \l{Modules}. + + + \section1 Reusing Project File Code + QML-like inheritance works also in \QBS. + + \code + ---CrazyProduct.qbs--- + import qbs 1.0 + + Product { + property string craziness: "low" + } + + ---hellocrazyworld.qbs--- + import "CrazyProduct.qbs" as CrazyProduct + + CrazyProduct { + craziness: "enormous" + name: "hellocrazyworld" + // ... + } + \endcode + + You can put JS code into separate \c{.js} files and then import them. + \code + ---helpers.js--- + function planetsCorrectlyAligned() + { + // implementation + } + + ---myproject.qbs--- + import qbs 1.0 + import "helpers.js" as Helpers + + Product { + name: "myproject" + Group { + condition: Helpers.planetsCorrectlyAligned() + file: "magic_hack.cpp" + } + // ... + } + \endcode + + + \section1 Modules + + A \e module is a collection of properties and language items that are used for + building a product if the product depends on (or loads) the module. + + For example, the \a cpp module looks like this (simplified): + \code + Module { + name: "cpp" + property string warningLevel + property string optimization + property bool debugInformation + property path precompiledHeader + // ... + FileTagger { + patterns: "*.cpp" + fileTags: ["cpp"] + } + Rule {...} // compiler + Rule {...} // application linker + Rule {...} // static lib linker + Rule {...} // dynamic lib linker + } + \endcode + + The properties that can be set for the \a cpp module are used to control the behavior of + your C++ tool chain. + In addition, you can use FileTaggers and Rules that are explained later. + + As soon as your product depends on a module, it can set the properties of the + module. You specify the optimization level for your product (and all build variants) like this: + + \code ---helloworld.qbs--- + import qbs 1.0 + + Application { + name: "helloworld" + files: ["main.cpp"] + cpp.optimization: "ludicrousSpeed" + Depends { name: "cpp" } + } + \endcode + + A module can implicitly depend on other modules. For example, the \c Qt.core module depends + on \c{cpp}. But to set the properties of a module you must make the dependency explicit. + + \code + // THIS DOES NOT WORK + Application { + name: "helloworld" + files: ["main.cpp"] + Depends { name: "Qt.core" } + cpp.optimization: "ludicrousSpeed" + // ERROR! We do not know about "cpp" here, + // though "Qt.core" depends on "cpp". + } + + // THIS WORKS + Application { + name: "helloworld" + files: ["main.cpp"] + Depends { name: "Qt.core" } + Depends { name: "cpp" } + cpp.optimization: "ludicrousSpeed" + } + \endcode + + \section2 Different Properties for a Single File + + Not only the product, but all the source files of the product can have their own + set of module properties. For example, assume you have some files that are known to crash + your compiler if you turn on optimizations. You want to turn off + optimizations for just these files and this is how you do it: + + \code + Application { + name: "helloworld" + files: "main.cpp" + Group { + files: ["bad_file.cpp", "other_bad_file.cpp"] + cpp.optimization: "none" + } + Depends { name: "cpp" } + } + \endcode + + \section2 Selecting Files by Properties + + Sometimes you have a file that is only going to be compiled on a certain platform. + This is how you do it: + \code + Group { + condition: qbs.targetOS.contains("windows") + files: [ + "harddiskdeleter_win.cpp", + "blowupmonitor_win.cpp", + "setkeyboardonfire_win.cpp" + ] + } + Group { + condition: qbs.targetOS.contains("linux") + files: [ + "harddiskdeleter_linux.cpp", + "blowupmonitor_linux.cpp", + "setkeyboardonfire_linux.cpp" + ] + } + \endcode + + In the above example, \a qbs.targetOS is a property of the \a target of the the \a qbs + module. The \a qbs module is always implicitly loaded. Its main properties + are: + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li buildVariant + \li string + \li "debug" + \li Name of the current build variant. By default, "debug" and "release" + are valid values but the user can add more in a project file. + \row + \li hostOS + \li stringlist + \li platform-dependent + \li The host operating system. + May contain "windows", "linux", "macos", "darwin", "unix", etc. + \note Do not confuse this with the \c{qbs.targetOS} property, which represents the + operating system on which the binaries produced by \QBS will run. + \row + \li targetOS + \li stringlist + \li platform-dependent + \li The target operating system. + May contain "windows", "linux", "macos", "darwin", "unix", + "ios", "android", "blackberry", "qnx", etc. + \endtable + + You can set these properties on the command line or by using a profile. + + \code + $ qbs # qbs.buildVariant:debug, profile: + $ qbs release # qbs.buildVariant:release, profile: + $ qbs profile:Maemo # qbs.buildVariant:debug, profile:Maemo + $ qbs debug release # builds two configurations of the project + \endcode + + To select files by build variant: + \code + Group { + condition: qbs.buildVariant == "debug" + files: "debughelper.cpp" + } + \endcode + + To set properties for a build variant: + \code + Properties { + condition: qbs.buildVariant == "debug" + cpp.debugInformation: true + cpp.optimization: "none" + } + \endcode + Or, to use a more QML-like style: + \code + cpp.debugInformation: qbs.buildVariant == "debug" ? true : false + cpp.optimization: qbs.buildVariant == "debug" ? "none" : "fast" + \endcode + + \section1 Property Types + + While properties in \QBS generally work the same way as in QML, the set of possible property + types has been adapted to reflect the specific needs of a build tool. The supported types + are as follows: + + \table + \header + \li Property type + \li Example + \li Description + \row + \li \c bool + \li \c{property bool someBoolean: false} + \li The usual boolean values. + \row + \li \c int + \li \c{property int theAnswer: 42} + \li Integral numbers. + \row + \li \c path + \li \c{property path aFile: "file.txt"} + \li File paths resolved relative to the directory the product they are associated with + is located in. + \row + \li \c pathList + \li \c{property pathList twoFiles: ["file1.txt", "./file2.txt"]} + \li A list of \c path values. + \row + \li \c string + \li \c{property string parentalAdvisory: "explicit lyrics"} + \li JavaScript strings. + \row + \li \c stringList + \li \c{property stringList realWorldExample: ["no", "not really"]} + \li A list of JavaScript strings. + \row + \li \c var + \li \c{property var aMap: ({ key1: "value1", key2: "value2" })} + \li Generic data, as in QML. + \endtable + + + \section1 File Tags and Taggers + + \QBS itself knows nothing about C++ files or file extensions. All source files + in a product are handled equally. However, you can assign \a{file tags} to an artifact + to act as a marker or to specify a file type. + + An artifact can have multiple file tags. + For example, you can use the \a Group item to group files with the same file tags (or a set of + properties). + + \code + Product { + Group { + files: ["file1.cpp", "file2.cpp"] + fileTags: ["cpp"] + } + Group { + files: "mydsl_scanner.l" + fileTags: ["flex", "foobar"] + } + // ... + } + \endcode + + When you load the \a cpp module, you also load the following item: + \code + FileTagger { + patterns: "*.cpp" + fileTags: ["cpp"] + } + \endcode + This construct means that each source file that matches the pattern \c{*.cpp} (and + has not explicitly set a file tag) gets the file tag \c{cpp}. + + The above example can be simplified to + \code + Product { + Depends: "cpp" + files: ["file1.cpp", "file2.cpp"] + Group { + files: "mydsl_scanner.l" + fileTags: ["flex", "foobar"] + } + // ... + } + \endcode + + The \a FileTagger from the \a cpp module automatically assigns the \c cpp + file tag to the source files. Groups that just contain the \a files + property can be more simply expressed by using the \a files property of the product. + + File tags are used by \a rules to transform one type of artifact into + another. For instance, the C++ compiler rule transforms artifacts with the file tag + \c cpp to artifacts with the file tag \c{obj}. + + In addition, it is possible to use file taggers to tag files and specify custom file tags: + \code + Product { + Depends: "cpp" + Group { + overrideTags: false // The overrideTags property defaults to true. + fileTags: ["foobar"] + files: ["main.cpp"] // Gets the file tag "cpp" through a FileTagger item and + // "foobar" from this group's fileTags property. + } + // ... + } + \endcode + + \section1 Rules + + \QBS applies a \e rule to a pool of artifacts (in the beginning it is just the set of + source files of the project) and chooses the ones that match the input file + tags specified by the rule. Then it creates output artifacts in the build graph that have other + filenames and file tags. It also creates a script that transforms the input artifacts into the + output artifacts. Artifacts created by one rule can (and typically do) serve as inputs to + another rule. In this way, rules are connected to one another via their input and output + file tags. + + For examples of rules, see the share/qbs/modules directory in the \QBS + repository. + + You can define rules in your own module to be provided along with + your project. Or you can put a rule directly into your project file. + + For more information, see \l{Rule Item}. +*/ + + +/*! + \contentspage index.html + \previouspage language-introduction.html + \page building-applications.html + \nextpage running-applications.html + + \title Building Applications + + To build applications from the command line, enter the following commands: + + \code + cd tests/manual/collidingmice + qbs + \endcode + + The application is built using the default build profile that is set up + in your \QBS configuration. + To build with other profiles, specify options for the build + command. For example, to build debug and release configurations with a + profile named "Android", enter the following command: + + \code + qbs build profile:Android debug release + \endcode + + The position of the property assignment is important. In the example + above, the profile property is set for all build variants that come + afterwards. + + To set a property just for one build variant, place the assignment after + the build variant name. + In the following example, the property \c cpp.treatWarningsAsErrors is + set to \c true for debug only and \c cpp.optimization is set to + \c small for release only. + + \code + qbs build debug cpp.treatWarningsAsErrors:true release cpp.optimization:small + \endcode +*/ + +/*! + \contentspage index.html + \previouspage running-applications.html + \page installing-files.html + \nextpage shell.html + + \title Installing Files + + To install your project, specify the necessary information in the project file: + + \code + Application { + Group { + name: "Runtime resources" + files: "*.qml" + qbs.install: true + qbs.installDir: "share/myproject" + } + Group { + name: "The App itself" + fileTagsFilter: "application" + qbs.install: true + qbs.installDir: "bin" + } + } + \endcode + + In this example, we want to install a couple of QML files and an executable. + The actual installation is then done like this (using the default profile): + + \code + qbs --clean-install-root qbs.installRoot:/tmp/myProjectRoot + \endcode + + Here, we want the installDir properties from the project file to be interpreted relative + to the directory \c{/tmp/myProjectRoot}, and we want that directory to be removed first. + If the \c{qbs.installRoot} property is not given, a default is used, namely + \c{/install-root}. +*/ + +/*! + \contentspage index.html + \previouspage building-applications.html + \page running-applications.html + \nextpage installing-files.html + + \title Running Applications + + Running ./targets/debug/CollidingMice fails if Qt 4.8 is not in your PATH + (in Windows) or LD_LIBRARY_PATH (in Linux). + + Therefore, enter the following command to run an application: + + \code + qbs run --products CollidingMice + \endcode + + This command also builds and installs the given product, if necessary. +*/ + + +/*! + \contentspage index.html + \previouspage installing-files.html + \page shell.html + \nextpage custom-modules.html + + \title Using the Shell + + To use the \QBS shell, enter the following command: + + \code + qbs shell + \endcode + + This is mainly a debugging tool. It opens a shell with the same environment that qbs uses + when building the project, so you can, for example, inspect which environment variables + will be set up. + +*/ + +/*! + \contentspage index.html + \previouspage shell.html + \page custom-modules.html + \nextpage reference.html + + \title Custom Modules and Items + + Users of \QBS are not limited to the pre-defined \l{List of Modules}{modules} and + \l{List of Language Items}{items}, they can also create their own. Here we describe how + to set up custom modules and items so that \QBS will find them. + + \section1 File System Layout + + Items and modules are located under a common base directory, whose name and location is + completely arbitrary. We will refer to it as \c search-path here. This directory has two + subdirectories \c modules and \c imports, which contain \QBS modules and items, respectively. + + + \section1 Custom Modules + + To introduce a custom module \c mymodule, create a directory \c{search-path/modules/mymodule/}. + \note Module names are case-sensitive, and this also goes for the corresponding directory name. + + Then, put a file containing an instance of the \l{Module Item} in there and give it the \c{.qbs} + extension. This module will be pulled in if a + \l{Product Item}{product} declares a \l{Depends Item}{dependency} on \c mymodule. + + + \section1 Custom Items + + To introduce a custom item \c MyItem, create the file \c{search-path/imports/MyItem.qbs}. + + + \section1 Making \QBS Aware of Custom Modules and Items + + To be able to use your custom modules and items, you need to make them known to \QBS. You can + do this per project or globally. + + \section2 Project-specific Modules and Items + + Let's assume you have a project that is located in \c{project_dir} and you have created some + modules in \c{project_dir/custom-stuff/modules/} as well as some items in + \c{project_dir/custom-stuff/imports/} that you want to use in the project. + To achieve this, your top-level project file should look like this: + \code + // ... + Project { + // .. + qbsSearchPaths: "custom-stuff" + // .. + } + \endcode + \note For technical reasons, the custom modules and items will not be available in the file + that contains the \c qbsSearchPaths property. Any product that wants + to make use of them needs to be in a different file that is pulled in + via the \c references property, for example. This is not a serious limitation, since + every well-structured project will be split up in this manner. + + \section2 Making Custom Modules and Items Available Across Projects + + What if your modules and items are generally useful and you want to access them in several + projects? In this case, it's best to add the location to your preferences. For example: + \code + qbs config preferences.qbsSearchPaths /usr/local/share/custom-qbs-extensions + \endcode + +*/ diff --git a/doc/qbs.qdocconf b/doc/qbs.qdocconf new file mode 100644 index 00000000..9a1b2f6a --- /dev/null +++ b/doc/qbs.qdocconf @@ -0,0 +1,2 @@ +include(config/qbs-project.qdocconf) +include($QT_INSTALL_DOCS/global/qt-html-templates-offline.qdocconf) diff --git a/doc/reference/commands.qdoc b/doc/reference/commands.qdoc new file mode 100644 index 00000000..8e8229ca --- /dev/null +++ b/doc/reference/commands.qdoc @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// TODO: "\c" markup is used for all properties in table due to QTBUG-35505. + +/*! + \contentspage reference.html + \page commands.html + + \title Command and JavaScriptCommand + \brief Types of commands to be used in rules + + A \e command is what \QBS executes at build time. It is represented in the language by an object + of type \c Command, which runs a process, or \c JavaScriptCommand, which executes arbitrary + JavaScript code. A command is always created in the prepare script of a \c Rule. + + \section1 Command + + A \c Command represents a process that will be invoked at build time. Its constructor + arguments are the binary to run and a list of command-line arguments. For instance: + \code + var insaneCommand = new Command("rm", ["-r", "/"]); + \endcode + The \l{Rule Item} documentation shows a \c Command in context. + + \section1 JavaScriptCommand + + A \c JavaScriptCommand represents a chunk of JavaScript code that is run at build time. + For instance: + \code + var cmd = new JavaScriptCommand(); + cmd.apology = "Sorry."; + cmd.sourceCode = function() { + console.info("I'm a rather pointless command."); + console.info(apology); + }; + \endcode + + Within the source code, the special identifiers \c project and \c product + (giving access to project and product properties, respectively) as well as \c inputs and + \c outputs are available. As the example shows, arbitrary properties can be set on the command + object and then used within the source code. This technique is typically used to forward values + from the prepare script to the command. + The \l{Rule Item} documentation shows a \c JavaScriptCommand in context. + + \section1 Properties + + \section2 Common Properties + The following properties are available in both \c Command and \c JavaScriptCommand. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li \c description + \li string + \li empty + \li A message that is displayed when the command is executed. + \row + \li \c extendedDescription + \li string + \li empty + \li A detailed description that is displayed when the command is executed. + \row + \li \c highlight + \li string + \li empty + \li A tag that can be used to influence how the \c description is displayed. In principle, + the values are arbitrary. The \QBS command-line tool understands the following values and + maps them to different colors if the output device is a terminal: + \list + \li "compiler" indicates that the command processes source code + \li "linker" indicates that the command links objects + \li "codegen" indicates that the command generates source code + \li "filegen" indicates that the command creates arbitrary files + \endlist + All other values are mapped to the default color. + \row + \li \c silent + \li bool + \li false + \li A flag that controls whether the \c description is printed. Set it to \c true for commands that + users need not know about. \note If this property is \c false, then \c description must + not be empty. + \endtable + + \section2 Command Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li \c arguments + \li stringList + \li empty + \li The list of arguments to invoke the command with. Explicitly setting this property + overrides an argument list provided when instantiating the object. + \row + \li \c environment + \li stringList + \li empty + \li A list of environment variables that are added to the common build environment. + They are provided as a list of strings in the form "varName=value". + \row + \li \c maxExitCode + \li int + \li 0 + \li The maximum exit code from the process to interpret as success. Setting this should + rarely be necessary, as all well-behaved applications use values other than zero + to indicate failure. + \row + \li \c program + \li string + \li undefined + \li The binary to invoke. Explicitly setting this property overrides a path provided when + instantiating the object. + \row + \li \c responseFileThreshold + \li int + \li 32000 on Windows, -1 elsewhere + \li If this value is greater than zero and less than the length of the full command line, + and if \c responseFileUsagePrefix is not empty, the contents of the command line are + moved to a temporary file, whose path becomes the entire contents of the + argument list. The program is then supposed to read the full argument list from that + file. This mechanism is mainly useful to work around Windows limitations regarding + the maximum length of the command line and will only work with programs that explicitly + support it. + \row + \li \c responseFileArgumentIndex + \li int + \li 0 + \li Index of the first argument to include in the response file. For example this may be + used in conjunction with a compiler wrapper where the first argument (the path to the + compiler) must be included on the raw command line. + \row + \li \c responseFileUsagePrefix + \li string + \li empty + \li The prefix that informs \c program that the rest of the argument + is a path to a file containing the actual command line. + \row + \li \c stderrFilterFunction + \li function + \li undefined + \li A function that takes as input the command's actual standard error output and returns a string + that is presented to the user as the command's standard error output. + If it is not set, the output is shown unfiltered. + \row + \li \c stdoutFilterFunction + \li function + \li undefined + \li A function that takes as input the command's actual standard output and returns a string + that is presented to the user as the command's standard output. + If it is not set, the output is shown unfiltered. + \row + \li \c workingDirectory + \li string + \li empty + \li The program's working directory. + \row + \li \c stdoutFilePath + \li string + \li undefined + \li Redirects the filtered standard output content to \c stdoutFilePath. If \c stdoutFilePath is undefined, + the filtered standard output is forwarded to \QBS, possibly to be printed to the console. + \row + \li \c stderrFilePath + \li string + \li undefined + \li Redirects the filtered standard error output content to \c stderrFilePath. If \c stderrFilePath is undefined, + the filtered standard error output is forwarded to \QBS, possibly to be printed to the console. + \endtable + + \section2 JavaScriptCommand Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li \c sourceCode + \li function + \li undefined + \li The JavaScript function to execute. + \endtable +*/ diff --git a/doc/reference/items/convenience/androidapk.qdoc b/doc/reference/items/convenience/androidapk.qdoc new file mode 100644 index 00000000..17496bbc --- /dev/null +++ b/doc/reference/items/convenience/androidapk.qdoc @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page androidapk-item.html + \nextpage application-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title AndroidApk Item + \brief Represents an Android application package. + + An \c AndroidApk item is a \c Product of type \e android.apk. It has a dependency on + the \l{Module Android.sdk}{Android.sdk module}. The final build result is an Android + application package (APK) file. + Here is what the project file could look like for the BasicMediaDecoder + example that comes with the Android SDK: + \code + import qbs + + AndroidApk { + name: "Basic Media Decoder" + packageName: "com.example.android.basicmediadecoder" + + property string sourcesPrefix: "Application/src/main/" + + resourcesDir: sourcesPrefix + "/res" + sourcesDir: sourcesPrefix + "/java" + manifestFile: sourcesPrefix + "/AndroidManifest.xml" + } + \endcode + + \section1 AndroidApk Properties + The following properties are available in addition to the ones inherited from \c Product. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li assetsDir + \li path + \li \c "assets" + \li The base directory for Android assets. + \note Android requires that the file name of this directory is always "assets". + \row + \li automaticSources + \li bool + \li \c true + \li If this is enabled, Java sources as well as Android resources, assets and the manifest + file will be automatically included in the product via wildcards. Set this property + to \c false if you want to specify these files manually. + \row + \li manifestFile + \li path + \li \c "AndroidManifest.xml" + \li The file path to the Android manifest file. + \note Android requires that the file name is always "AndroidManifest.xml". + \row + \li packageName + \li string + \li \c name + \li The package name as given in the manifest file. + \row + \li resourcesDir + \li path + \li \c "res" + \li The base directory for Android resources. + \note Android requires that the file name of this directory is always "res". + \row + \li sourcesDir + \li path + \li \c "src" + \li The base directory for Java sources. This property is only relevant if + \c automaticSources is enabled. + \endtable +*/ diff --git a/doc/reference/items/convenience/application.qdoc b/doc/reference/items/convenience/application.qdoc new file mode 100644 index 00000000..1a13724b --- /dev/null +++ b/doc/reference/items/convenience/application.qdoc @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page application-item.html + \previouspage androidapk-item.html + \nextpage applicationextension-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title Application Item + \brief Represents a product of application type. + + An \c Application item is a \c Product that has its type set to "application" + It exists for the convenience of project file authors. + + \note On Android, an \c Application item instead builds a shared library for + products whose \c consoleApplication property is set to \c false. +*/ diff --git a/doc/reference/items/convenience/applicationextension.qdoc b/doc/reference/items/convenience/applicationextension.qdoc new file mode 100644 index 00000000..dd7b4346 --- /dev/null +++ b/doc/reference/items/convenience/applicationextension.qdoc @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page applicationextension-item.html + \previouspage application-item.html + \nextpage autotestrunner-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title ApplicationExtension Item + \brief Represents a product that is an iOS, macOS, tvOS, or watchOS Application Extension. + + An \c ApplicationExtension item is a convenience item based on \c XPCService that + sets some properties required for iOS, macOS, tvOS, or watchOS Application Extensions. +*/ diff --git a/doc/reference/items/convenience/autotestrunner.qdoc b/doc/reference/items/convenience/autotestrunner.qdoc new file mode 100644 index 00000000..d73d442a --- /dev/null +++ b/doc/reference/items/convenience/autotestrunner.qdoc @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page autotestrunner-item.html + \previouspage applicationextension-item.html + \nextpage cppapplication-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title AutotestRunner Item + \brief A product that runs all autotests in the project. + + An \c AutotestRunner has a dependency to all products with the type "autotest". "Building" + the \c AutotestRunner product will then run the respective executables. The \c builtByDefault + property of an \c AutotestRunner is set to \c false by default, so running the autotests + has to be explicitly requested. The default name of the product is "autotest-runner". + To use this feature, do the following: + \list A + \li + Attach the "autotest" type to your autotests: + \code + CppApplication { + name: "tst_mytest" + type: ["application", "autotest"] + // ... + } + \endcode + \li + Instantiate exactly one \c AutotestRunner in your project, typically at the top level: + \code + Project { + // ... + AutotestRunner { } + // ... + } + \endcode + \li + Trigger the autotest execution by "building" the product: + \code + qbs build -p autotest-runner + \endcode + \endlist + + \section1 AutotestRunner Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li arguments + \li stringList + \li empty + \li The list of arguments to invoke the autotest with. + \row + \li environment + \li stringList + \li set internally + \li A list of environment variables that are added to the run environment. + They are provided as a list of strings in the form "varName=value". + By default, DYLD_LIBRARY_PATH, DYLD_FRAMEWORK_PATH, and DYLD_ROOT_PATH + are set on macOS, or an empty list for other platforms. + \row + \li limitToSubProject + \li bool + \li \c true + \li By default, only those autotests are considered that are in the same sub-project that + the \c AutotestRunner was instantiated in. If you want to run all autotests regardless + of their location in the project hierarchy, set this property to \c false. + \row + \li wrapper + \li stringList + \li empty + \li Wrapper binary and its arguments for wrapping autotest calls. + This is useful for tools like Valgrind and alike. + \endtable + +*/ diff --git a/doc/reference/items/convenience/cppapplication.qdoc b/doc/reference/items/convenience/cppapplication.qdoc new file mode 100644 index 00000000..85338ed4 --- /dev/null +++ b/doc/reference/items/convenience/cppapplication.qdoc @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page cppapplication-item.html + \previouspage autotestrunner-item.html + \nextpage dynamiclibrary-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title CppApplication Item + \brief Represents a product of application type with a dependency to the cpp module. + + A \c CppApplication is a convenience item that is entirely equivalent to the following: + \code + Application { + Depends { name: "cpp" } } + \endcode +*/ diff --git a/doc/reference/items/convenience/dynamiclibrary.qdoc b/doc/reference/items/convenience/dynamiclibrary.qdoc new file mode 100644 index 00000000..f73a9a72 --- /dev/null +++ b/doc/reference/items/convenience/dynamiclibrary.qdoc @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page dynamiclibrary-item.html + \previouspage cppapplication-item.html + \nextpage innosetup-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title DynamicLibrary Item + \brief Represents a product that is a dynamic library. + + A \c DynamicLibrary item is a \c Product that has its type set to "dynamiclibrary". It exists + for the convenience of project file authors. + + For Android targets, the following applies: + \list + \li The product type contains "android.nativelibrary" in addition to "dynamiclibrary". + \li There is a dependency on the \l{Module cpp}{cpp} and \l{Module Android.ndk}{Android.ndk} + modules. + \endlist + + \section1 DynamicLibrary Properties + The following properties are available in addition to the ones inherited from \c Product. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li architectures + \li stringList + \li see below + \li This property is only relevant for Android targets. There, it specifies for which + architectures to build, with the default value \c{["armeabi"]}. + This information influences the \c profiles property. + \row + \li bundleExtension + \li string + \li empty + \li The extension to use for the product's bundle. Only relevant on Darwin systems. + \endtable +*/ diff --git a/doc/reference/items/convenience/innosetup.qdoc b/doc/reference/items/convenience/innosetup.qdoc new file mode 100644 index 00000000..8dc15330 --- /dev/null +++ b/doc/reference/items/convenience/innosetup.qdoc @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page innosetup-item.html + \previouspage dynamiclibrary-item.html + \nextpage installpackage-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title InnoSetup Item + \brief Represents a product that is an Inno Setup installer executable. + + A \c InnoSetup item is a convenience item that has a dependency on the + \l{Module innosetup}{Inno Setup module} and whose type is \c{["innosetup.exe"]}. +*/ diff --git a/doc/reference/items/convenience/installpackage.qdoc b/doc/reference/items/convenience/installpackage.qdoc new file mode 100644 index 00000000..7a8f6130 --- /dev/null +++ b/doc/reference/items/convenience/installpackage.qdoc @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page installpackage-item.html + \previouspage innosetup-item.html + \nextpage javaclasscollection-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title InstallPackage Item + \brief Represents an archive of an installed project. + + An \c InstallPackage item is a product of type \c{archiver.archive}. It is used to produce an + archive from a set of installable files via the \l{Module archiver}{archiver Module}. + Consider the following example project: + + \code + Project { + CppApplication { + name: "myapp" + Depends { name: "mylib" } + files: ["main.cpp"] + Group { + fileTagsFilter: product.type + qbs.install: true + qbs.installDir: "bin" + } + } + DynamicLibrary { + name: "mylib" + files: ["mylib.cpp"] + Group { + name: "public headers" + files: ["mylib.h"] + qbs.install: true + qbs.installDir: "include" + } + Group { + fileTagsFilter: product.type + qbs.install: true + qbs.installDir: "lib" + } + } + + InstallPackage { + archiver.type: "tar" + name: "tar-package" + Depends { name: "myapp" } + Depends { name: "mylib" } + } + } + \endcode + + Building the product "tar-package" on a Unix system will result in a tar file with these + contents: + \code + include/mylib.h + lib/libmylib.so + bin/myapp + \endcode + +*/ diff --git a/doc/reference/items/convenience/javaclasscollection.qdoc b/doc/reference/items/convenience/javaclasscollection.qdoc new file mode 100644 index 00000000..f16f7a81 --- /dev/null +++ b/doc/reference/items/convenience/javaclasscollection.qdoc @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page javaclasscollection-item.html + \previouspage installpackage-item.html + \nextpage javajarfile-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title JavaClassCollection Item + \brief Represents a collection of Java class files not bundled in a jar file. + + A \c JavaClassCollection item is a convenience item that has a dependency on the + \l{Module java}{Java module} and whose type is \c{["java.class"]}. If the files + should end up in a jar file, you should use a \l{JavaJarFile Item}{JavaJarFile} instead. + +*/ diff --git a/doc/reference/items/convenience/javajarfile.qdoc b/doc/reference/items/convenience/javajarfile.qdoc new file mode 100644 index 00000000..2f596ef1 --- /dev/null +++ b/doc/reference/items/convenience/javajarfile.qdoc @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page javajarfile-item.html + \previouspage javaclasscollection-item.html + \nextpage loadablemodule-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title JavaJarFile Item + \brief Represents a collection of Java class files bundled in a jar file. + + A \c JavaJarCollection item is product of type \c{java.jar}. It is used to produce a jar + archive from a set of Java sources. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li entryPoint + \li string + \li undefined + \li The entry point for an executable jar file. + \endtable + +*/ diff --git a/doc/reference/items/convenience/loadablemodule.qdoc b/doc/reference/items/convenience/loadablemodule.qdoc new file mode 100644 index 00000000..191b7409 --- /dev/null +++ b/doc/reference/items/convenience/loadablemodule.qdoc @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page loadablemodule-item.html + \previouspage javajarfile-item.html + \nextpage qtapplication-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title LoadableModule Item + \brief Represents a product that is a loadable module. + + A \c LoadableModule item is a \c Product that has its type set to "loadablemodule". It exists + for the convenience of project file authors. +*/ diff --git a/doc/reference/items/convenience/qtapplication.qdoc b/doc/reference/items/convenience/qtapplication.qdoc new file mode 100644 index 00000000..b5d8a408 --- /dev/null +++ b/doc/reference/items/convenience/qtapplication.qdoc @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page qtapplication-item.html + \previouspage loadablemodule-item.html + \nextpage qtguiapplication-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title QtApplication Item + \brief Represents a product of application type with a dependency to the Qt Core module. + + A \c QtApplication is a convenience item that is entirely equivalent to the following: + \code + CppApplication { + Depends { name: "Qt.core" } } + } + \endcode + +*/ diff --git a/doc/reference/items/convenience/qtguiapplication.qdoc b/doc/reference/items/convenience/qtguiapplication.qdoc new file mode 100644 index 00000000..7a113126 --- /dev/null +++ b/doc/reference/items/convenience/qtguiapplication.qdoc @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page qtguiapplication-item.html + \previouspage qtapplication-item.html + \nextpage staticlibrary-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title QtGuiApplication Item + \brief Represents a product of application type with a dependency to the Qt GUI module. + + A \c QtGuiApplication is a convenience item that extends the \c QtApplication item by loading + the \c Qt.gui module and possibly also the default QPA plugin. + + \section1 QtGuiApplication Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li linkDefaultQpaPlugin + \li bool + \li Qt.core.staticBuild == true + \li Whether or not to add a depencency to the default QPA plugin. The default is to + do that in the case of a static build, where it is normally required. + \endtable + +*/ diff --git a/doc/reference/items/convenience/staticlibrary.qdoc b/doc/reference/items/convenience/staticlibrary.qdoc new file mode 100644 index 00000000..20ddb82e --- /dev/null +++ b/doc/reference/items/convenience/staticlibrary.qdoc @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page staticlibrary-item.html + \previouspage qtguiapplication-item.html + \nextpage xpcservice-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title StaticLibrary Item + \brief Represents a product that is a static library. + + A \c StaticLibrary item is a convenience item that is normally entirely equivalent to + the following: + \code + Product { + type: "staticlibrary" + } + \endcode + + For Android targets, the following applies: + \list + \li There is a dependency on the \l{Module cpp}{cpp} and \l{Module Android.ndk}{Android.ndk} + modules. + \li There is an additional list property "architectures" that specifies for which + architectures to build, with the default value \c{["armeabi"]}. + This information influences the \c profiles property. + \endlist + +*/ diff --git a/doc/reference/items/convenience/xpcservice.qdoc b/doc/reference/items/convenience/xpcservice.qdoc new file mode 100644 index 00000000..7ed89b8f --- /dev/null +++ b/doc/reference/items/convenience/xpcservice.qdoc @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-convenience-items.html + \page xpcservice-item.html + \previouspage staticlibrary-item.html + \ingroup list-of-convenience-items + \ingroup list-of-items + + \title XPCService Item + \brief Represents a product that is a macOS, iOS, tvOS, or watchOS XPC service. + + An \c XPCService item is a convenience item based on \c Application that + sets some properties required for macOS, iOS, tvOS, or watchOS XPC services. +*/ diff --git a/doc/reference/items/language/artifact.qdoc b/doc/reference/items/language/artifact.qdoc new file mode 100644 index 00000000..9bda03e5 --- /dev/null +++ b/doc/reference/items/language/artifact.qdoc @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \page artifact-item.html + \nextpage depends-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Artifact Item + \brief Describes a file produced by a \c Rule. + + An \c Artifact represents a single file produced by a \l{Rule Item}{Rule}. + + For example, if a rule produces three files, it needs to contain three Artifact items. + + In addition to the properties listed in the section below, you can also set module properties + within an \c Artifact item: + \code + Artifact { + filePath: "somefile.cpp" + fileTags: ["cpp"] + cpp.cxxLanguageVersion: "c++11" + // ... + } + \endcode + + \section1 Artifact Properties + + \note The code on the right-hand side of these properties has access to the set of input + artifacts, that is, it can refer to the \c inputs map and, if the rule is not a multiplex rule, + the \c input variable. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li alwaysUpdated + \li bool + \li true + \li Setting this to \c false means the file is not necessarily always written to by any + command run by the rule. The timestamps of such artifacts are therefore not checked to + find out whether they are up to date. Consequently, if all artifacts of a rule have this + property set to \c false, the commands of the rule are always executed. + \row + \li filePath + \li string + \li undefined + \li The file path of the target artifact. + \row + \li fileTags + \li list + \li empty list + \li The tags to attach to the target file. These can then be matched by a rule. + \endtable + +*/ diff --git a/doc/reference/items/language/depends.qdoc b/doc/reference/items/language/depends.qdoc new file mode 100644 index 00000000..5d944cba --- /dev/null +++ b/doc/reference/items/language/depends.qdoc @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage artifact-item.html + \page depends-item.html + \nextpage export-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Depends Item + \brief Represents dependencies between products and modules. + + A \c Depends item can appear inside a \l{Product Item} or \l{Module Item}. + + For example, the following product will load the \c cpp module. In addition, it will try + to load modules that may or may not exist, and in the latter case use a fallback. + \code + Product { + Depends { name: "cpp" } + Depends { + name: "awesome_module" + versionAtLeast: "2.0" + required: false + } + Depends { + name: "adequate_module" + condition: !awesome_module.present + required: false + } + Depends { + name: "crappy_module" + condition: !awesome_module.present && !adequate_module.present + } + + // ... + } + \endcode + + + \section1 Depends Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li condition + \li bool + \li true + \li Determines whether the dependency will actually be applied. + \row + \li versionAtLeast + \li string + \li undefined + \li The minimum value that the dependency's \c version property needs to have. If the + actual version is lower than that, loading the dependency will fail. + The value consists of integers separated by dots. + \row + \li versionBelow + \li string + \li undefined + \li A value that the dependency's \c version property must be lower than. If the + actual version is equal to or higher than that, loading the dependency will fail. + The value consists of integers separated by dots. + \row + \li productTypes + \li stringList + \li undefined + \li A list of product types. Any enabled product in the project that has a matching type + will become a dependency of the product containing the \c Depends item. + This property is mutually exclusive with the \c name and \c submodules properties. + The \c required and \c profiles properties are ignored if \c productTypes is set. + \row + \li required + \li bool + \li \c true + \li Setting this property to \c false creates a \e{soft dependency}, meaning that it is not + considered an error if the given module cannot be found. In such a case, an instance of + the respective module will be created, but only the \c present property will be + available for querying, and it will be set to \c false. + \row + \li name + \li string + \li undefined + \li The name of the dependent product or module. + \row + \li profiles + \li stringList + \li \c{[product.profile]} + \li If the dependency is on a product and that product is going to be built for more than + one profile, then you can specify here which instance of the product the dependency is on. + See the \c profiles property of the \c Product item for more information. + An empty list means a dependency on all instances of the product with the given name, + regardless of their profile. + \row + \li limitToSubProject + \li bool + \li \c false + \li If \c productTypes is set and this property is \c true, then only products that + are in the same sub-project as the product containing the \c Depends item are + considered. + \row + \li submodules + \li stringList + \li undefined + \li The submodules of \c module to depend on, if applicable. + \endtable + +*/ diff --git a/doc/reference/items/language/export.qdoc b/doc/reference/items/language/export.qdoc new file mode 100644 index 00000000..16d073df --- /dev/null +++ b/doc/reference/items/language/export.qdoc @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage depends-item.html + \page export-item.html + \nextpage filetagger-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Export Item + \brief Exports dependencies and properties to other products. + + An \c Export item can appear inside a \l{Product Item}. It defines a Module with the product's + name that can be depended on by other products. + The properties attached to the Export item will take effect in all products that depend on the + product inside which the Export item is defined. + As an example, consider these two products: + \code + Product { + name: "A" + Export { + Depends { name: "cpp" } + cpp.includePaths: product.sourceDirectory + cpp.defines: ["USING_" + product.name.toUpperCase()] + } + } + + Product { + name: "B" + Depends { name: "A" } + } + \endcode + + The sources in product B will be able to use headers from product A without specifiying + the full path to them, because the include path has been made known to the compiler via + A's Export item. Additionally, product B will be compiled with the define \c{USING_A}. + + \note This relationship is transitive, so a product C depending on product B will also + get the include paths and preprocessor macros via A's Export item. + + In contrast to Module items, \c{product} within Export items refers to the product which defines + the Export item. Use the \c{importingProduct} variable to refer to the product that + pulls in the resulting module. +*/ diff --git a/doc/reference/items/language/filetagger.qdoc b/doc/reference/items/language/filetagger.qdoc new file mode 100644 index 00000000..578dad21 --- /dev/null +++ b/doc/reference/items/language/filetagger.qdoc @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage export-item.html + \page filetagger-item.html + \nextpage group-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title FileTagger Item + \brief Maps file patterns to tags. + + This item maps file patterns to tags. It can be attached to a product or a module. + In the latter case, its effect is the same as if it had been attached to all products having + a dependency on the respective module. For instance, the cpp module of \QBS has, among others, + the following file tagger: + \code + FileTagger { + patterns: "*.cpp" + fileTags: ["cpp"] + } + \endcode + + As a result, the "cpp" tag is automatically attached to all files ending with ".cpp" in + products depending on the cpp module. This causes them to be compiled, because a C++ + compiler rule has "cpp" in its list of matching input tags. + + File taggers are disabled if file tags are set explicitly in a product or group. + For example, the "cpp" tag is not attached to the cpp files in the following product: + + \code + Product { + Depends { name: "cpp" } + Group { + files: "*.cpp" + fileTags: "other" + } + } + \endcode + + \section1 FileTagger Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li patterns + \li stringList + \li none + \li The patterns to match against. Supports the usual wildcards '*', '?' and '[]'. + Neither the list itself nor any of its elements may be empty. + \row + \li fileTags + \li list + \li empty list + \li Tags to attach to a product's files. These can then be matched by a rule. + \endtable +*/ diff --git a/doc/reference/items/language/group.qbs b/doc/reference/items/language/group.qbs new file mode 100644 index 00000000..4b478bfa --- /dev/null +++ b/doc/reference/items/language/group.qbs @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 + +Project { + Product { +//! [0] + Group { + name: "Word processing documents" + files: ["*.doc", "*.rtf"] + prefix: "**/" + qbs.install: true + qbs.installDir: "share" + excludeFiles: "do_not_install_this_file.*" + } +//! [0] + } +} diff --git a/doc/reference/items/language/group.qdoc b/doc/reference/items/language/group.qdoc new file mode 100644 index 00000000..16eeb3ea --- /dev/null +++ b/doc/reference/items/language/group.qdoc @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage list-of-language-items.html + \previouspage filetagger-item.html + \page group-item.html + \nextpage module-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Group Item + \brief Groups files in a product. + + This item is attached to a product and used to group files that have something in common. + For example: + + \code + Application { + Group { + name: "common files" + files: ["myclass.h", "myclass_common_impl.cpp"] + } + Group { + name: "Windows files" + condition: targetOS.contains("windows") + files: "myclass_win_impl.cpp" + } + Group { + name: "Unix files" + condition: targetOS.contains("unix") + files: "unixhelper.cpp" + Group { + name: "Linux files" + condition: targetOS.contains("linux") + files: "myclass_linux_impl.cpp" + } + Group { + name: "FreeBSD files" + condition: targetOS.contains("freebsd") + files: "myclass_freebsd_impl.cpp" + } + } + Group { + name: "Files to install" + qbs.install: true + qbs.installDir: "share" + files: "runtime_resource.txt" + } + } + \endcode + When specifying files, you can use the wildcards "*", "?" and "[]", which have their usual meaning. + By default, matching files are only picked up directly from the parent directory, but you can tell \QBS to + consider the whole directory tree. It is also possible to exclude certain files from the list. + The pattern ** used in a pathname expansion context will match all files and zero or more + directories and subdirectories. + For example: + \snippet reference/items/language/group.qbs 0 + + A group can also be used to attach properties to build artifacts such as executables or + libraries. In the following example, an application is installed to "/bin". + \code + Application { + Group { + fileTagsFilter: "application" + qbs.install: true + qbs.installDir: "bin" + } + } + \endcode + + Groups may also appear in modules, which causes the respective sources to be added to the + products depending on said module. + Groups can be nested. In this case, child groups inherit the module properties and the file + tags of their parent group. The condition of a child group gets logically ANDed with the one + of its parent group. + + \section1 Group Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li name + \li string + \li "Group x", where x is a unique number among all the groups in the product + \li The name of the group. Not used internally; mainly useful for IDEs. + \row + \li files + \li list + \li empty list + \li The files in the group. Mutually exclusive with fileTagsFilter. + \row + \li prefix + \li string + \li empty string + \li A string to prepend to all files. Slashes are allowed and have directory semantics. + \row + \li fileTagsFilter + \li list + \li empty list + \li Artifact file tags to match. Any properties set in this group will be applied + to the product's artifacts whose file tags intersect with the ones + listed here. Mutually exclusive with files. + \row + \li condition + \li bool + \li true + \li Determines whether the files in the group are actually considered part of the project. + \row + \li fileTags + \li list + \li empty list + \li Tags to attach to the group's files. These can then be matched by a rule. + Note that file taggers are never applied to a file that has this property set. + \row + \li overrideTags + \li bool + \li true + \li Determines how tags on files that are listed both at the top level of + a product (or the parent group, if there is one) and a group are handled. + If this property is true, then the file tags set via the group + replace the ones set via the product or parent group. + If it is false, the "group tags" are added to the "parent tags". + \row + \li excludeFiles + \li list + \li empty list + \li For use with wildcards; the files in this list are "subtracted" from the files list. + \endtable +*/ diff --git a/doc/reference/items/language/module.qdoc b/doc/reference/items/language/module.qdoc new file mode 100644 index 00000000..69d9d889 --- /dev/null +++ b/doc/reference/items/language/module.qdoc @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage group-item.html + \page module-item.html + \nextpage probe-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Module Item + \brief Represents a collection of properties and items that can be loaded into a product. + + A \c Module item is a collection of properties and language items that are used for building a + product if the product has a \l{Depends Item}{dependency} on the module. + The following (somewhat artificial) module pre-processes text files by removing certain + characters from them: + + \code + import qbs + import qbs.FileInfo + import qbs.TextFile + + Module { + property stringList unwantedCharacters: [] + FileTagger { + patterns: ["*.raw"] + fileTags: ["raw-txt"] + } + Rule { + inputs: ["raw-txt"] + Artifact { + filePath: FileInfo.relativePath(input.filePath, product.sourceDirectory) + + "/" + input.fileName + ".processed" + fileTags: ["processed-txt"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Processing " + input.fileName; + cmd.sourceCode = function() { + var inFile = new TextFile(input.filePath, TextFile.ReadOnly); + var content = inFile.readAll(); + inFile.close(); + var unwantedChars = product.moduleProperty("txt_processor", + "unwantedCharacters"); + for (var c in unwantedChars) + content = content.replace(unwantedChars[c], ""); + var outFile = new TextFile(output.filePath, TextFile.WriteOnly); + outFile.write(content); + outFile.close(); + }; + return cmd; + } + } + } + \endcode + + And this is how a product would use the module: + + \code + Product { + type: "processed-txt" + Depends { name: "txt_processor" } + txt_processor.unwantedCharacters: ["\r"] + files: [ + "file1.raw", + "file2.raw" + ] + } + \endcode + + Of course, normally the pre-processed files would not be the target artifacts of the product, + but rather serve as inputs to a different rule that will often come from a different module. + + How you make your own modules available to \QBS is explained + \l{Custom Modules and Items}{here}. + + \section1 Special Property Values + + For every property defined in a module, \QBS provides the following special built-in values: + \section2 \c base + This value is useful when making use of inheritance. It stands for the value of the respective + property in the item one level up in the inheritance chain. For instance: + \code + Product { // defined in MyProduct.qbs + Depends { name: "mymodule" } + mymodule.someProperty: ["value1"] + } + ------ some other file ------ + MyProduct { + mymodule.someProperty: base.concat(["value2"]) // => ["value1", "value2"] + } + \endcode + + \section2 \c original + This is the value of the property in the module itself (possibly overridden from a profile or + the command line). Use it to set a module property conditionally: + \code + Module { // This is mymodule + property string aProperty: "z" + } + ---------- + Product { + Depends { name: "mymodule" } + Depends { name: "myothermodule" } + mymodule.aProperty: myothermodule.anotherProperty === "x" ? "y" : original // => "y" if myothermodule.anotherProperty is "x", "z" otherwise + \endcode + + \section2 \c outer + This value is used in nested items, where it refers to the value of the respective property + in the surrounding item. It is often encountered in \l{Group Item}{groups}: + \code + Product { + Depends { name: "mymodule" } + mymodule.someProperty: ["value1"] + Group { + name: "special files" + files: ["somefile1", "somefile2"] + mymodule.someProperty: outer.concat(["value"]) // => ["value1", "value2"] + } + } + \endcode + + + \section1 Module Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li additionalProductTypes + \li string list + \li empty list + \li The elements of this list will be added to the \c type property of a product + that has a dependency on the module. + \row + \li condition + \li bool + \li \c true + \li Controls whether the module is enabled. If this property is \c false, the + surrounding \c Module item will not be considered in the module look-up. + \row + \li present + \li bool + \li \c true + \li This property is read-only. Its value is \c false if and only if the respective + \c Depends item had its \c required property set to \c false and the module was + not found. + \row + \li setupBuildEnvironment + \li script + \li \c undefined + \li Script for setting up the environment in which the project is built. + Use the \c putEnv, \c getEnv, and \c unsetEnv functions to alter the environment. + The return value of this script is ignored. + \row + \li setupRunEnvironment + \li script + \li \c setupBuildEnvironment + \li Script for setting up the environment in which the project is run. + \row + \li validate + \li script + \li \c undefined + \li Script that is run after the module is loaded. It can be used to check property + values and throw errors in unexpected cases. The return value is ignored. + \row + \li version + \li string + \li \c undefined + \li The module's version. It consists of integer values separated by dots. You can check + for specific values of this property in a \l{Depends item}{Depends} item. + \endtable +*/ diff --git a/doc/reference/items/language/probe.qdoc b/doc/reference/items/language/probe.qdoc new file mode 100644 index 00000000..b7f61a91 --- /dev/null +++ b/doc/reference/items/language/probe.qdoc @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage module-item.html + \page probe-item.html + \nextpage product-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Probe Item + \brief Locates files outside the project. + + A \c Probe item can appear inside a \l{Product Item} and is run prior to building products in + order to locate dependent headers, libraries, and other files outside the project directory + whose locations are not known ahead of time. \c Probes are similar to configure scripts. + \note Because Probes often invoke external processes, which is relatively expensive compared + to evaluating normal properties, their results are cached. To force re-evaluation + of a Probe, you can supply the \c{--force-probe-execution} command-line option. + + \section1 Probe Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li condition + \li bool + \li true + \li Determines whether the probe will actually be run. + \row + \li found + \li bool + \li undefined + \li Indicates whether the probe was run successfully. Set by \c configure. + \row + \li configure + \li script + \li undefined + \li Script that is executed when the probe is run. + \endtable + +*/ diff --git a/doc/reference/items/language/product.qdoc b/doc/reference/items/language/product.qdoc new file mode 100644 index 00000000..4dd3089a --- /dev/null +++ b/doc/reference/items/language/product.qdoc @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage probe-item.html + \page product-item.html + \nextpage project-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Product Item + \brief Represents the result of a build process. + + A \e product typically represents the result of a build process. It specifies a set of + input and output files and a way to transform the former into the latter. For example, the + following product sets up a very simple C++ application: + \code + Product { + name: "helloworld" + type: "application" + files: "main.cpp" + Depends { name: "cpp" } + } + \endcode + The \c type property specifies what will be built (an executable). The \c files property specifies + the input files (one C++ source file), and the \c Depends item pulls in the logic from the \c cpp module + about how to do the necessary transformations. + For some often-used types of products, \QBS pre-defines special derived items that save + users some typing. These are: + \list + \li Application + \li CppApplication + \li DynamicLibrary + \li StaticLibrary + \endlist + Therefore, the above example could also be written like this: + \code + CppApplication { + name: "helloworld" + files: "main.cpp" + } + \endcode + Any property \c prop attached to this item is available in sub-items as \c product.prop, as + well as in modules that are loaded from this product. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li builtByDefault + \li bool + \li true + \li If false, the product will only be built if this is explicitly requested, + either by listing the product name as an argument to \c --products or by giving + the \c build command the \c --all-products option. + \row + \li condition + \li bool + \li true + \li If false, the product will not be built. + \row + \li name + \li string + \li empty string + \li The name of the product. Used to identify the product in a \c Depends item, for + example. The value of this property must be a simple JavaScript expression that + does not depend on module properties or values that are non-local to this product. + \code + CppApplication { + name: "hello" + "world" // valid + } + CppApplication { + name: "app_for_" + qbs.targetOS.join("_") // invalid + } + \endcode + To change the name of your product's target artifact, modify \c{Product.targetName} + instead. + \row + \li profiles + \li stringList + \li \c{[project.profile]} + \li The profiles for which the product should be built. For each profile listed here, + one instance of the product will be built according to the properties set in + the respective profile. + This property is only relevant for projects that require products being built for + different architectures. Otherwise it can be left at its default value. + \row + \li type + \li stringList + \li empty list + \li The file tags matching the product's target artifacts. + \row + \li targetName + \li string + \li \c{name} with illegal file name characters replaced by underscores + \li The base file name of the product's target artifacts. + \row + \li destinationDirectory + \li string + \li product.buildDirectory + \li The directory where the target artifacts will be located. If a relative path is + given, the base directory will be \c project.buildDirectory. + \row + \li files + \li stringList + \li empty list + \li A list of source files. Syntactic sugar to save a \c Group item for simple products. + \row + \li excludeFiles + \li stringList + \li empty list + \li A list of source files not to include. Useful with wildcards. + For more information, see \l {Group Item}. + + \row + \li consoleApplication + \li bool + \li linker-dependent + \li If true, a console application is generated. If false, a GUI application is generated. + Only takes effect on Windows. + This property also influences the default application type on Apple platforms. + If true, an application bundle is generated. If false, a normal executable is + generated. + \row + \li qbsSearchPaths + \li stringList + \li project.qbsSearchPaths + \li See the documentation of the \l {Project Item} property of the same name. + The value set here will be merged with the one inherited from + the project. + \row + \li version + \li string + \li undefined + \li The version number of the product. Used in shared library filenames and generated + Info.plist files in Apple application and framework bundles, for example. + \endtable + + The following properties are automatically set by \QBS and cannot be changed by the user: + + \table + \header + \li Property + \li Type + \li Description + \row + \li buildDirectory + \li path + \li The build directory for this product. This is the directory where generated files + are placed. + \row + \li profile + \li string + \li The profile for building this particular instance of the product. Derived + automatically from the \c profiles property. + \row + \li sourceDirectory + \li path + \li The source directory for this product. This is the directory of the file where this + product is defined. + \endtable +*/ diff --git a/doc/reference/items/language/project.qdoc b/doc/reference/items/language/project.qdoc new file mode 100644 index 00000000..33d4eb50 --- /dev/null +++ b/doc/reference/items/language/project.qdoc @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage product-item.html + \page project-item.html + \nextpage properties-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Project Item + \brief Represents a collection of products and properties. + + A \c Project item represents a collection of of products. In a + non-trivial project, these products are typically defined in their own files and + referenced in the main project file: + \code + Project { + references: [ + "product1/product1.qbs", + "product2/product2.qbs" + ] + } + \endcode + Any property \c prop attached to this item is available in sub-items as \c project.prop. + + While the root of the item hierarchy is always a \c Project, this kind of item can also + appear further down the hierarchy. Such sub-projects are ususally introduced to group products. + See the \l{SubProject Item} for details. + + \note If your project consists of only one product, the \c Project item can be omitted. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li buildDirectory + \li path + \li n/a + \li The build directory of the top-level project. This property is read-only. + \row + \li name + \li string + \li basename of the file the project is defined in + \li The project name. Only relevant for e.g. displaying a project tree in an IDE. + \row + \li profile + \li string + \li n/a + \li The top-level profile for building the project. This property is read-only and + is set by \QBS when the project is being set up. + \row + \li condition + \li bool + \li true + \li Whether the project is enabled. If false, no products or sub-projects will be + collected. + \row + \li qbsSearchPaths + \li stringList + \li empty + \li These paths are searched for imports and modules in addition to the ones listed + in \c{preferences.qbsSearchPaths}. The value set here is merged with the value + inherited from the parent project, if there is one. The result is inherited by + all products in the project. + \row + \li references + \li path list + \li empty + \li A list of files from which to import products. This is equivalent to defining + the respective \c Product items directly under this \c Project item. + \row + \li sourceDirectory + \li path + \li n/a + \li The directory where the file containing the top-level \c Project item is located. + This property is read-only. + \row + \li minimumQbsVersion + \li string + \li "1.3.0" + \li The minimum version of qbs that is needed to build this project. + \endtable +*/ diff --git a/doc/reference/items/language/properties.qdoc b/doc/reference/items/language/properties.qdoc new file mode 100644 index 00000000..4e2cb345 --- /dev/null +++ b/doc/reference/items/language/properties.qdoc @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage project-item.html + \page properties-item.html + \nextpage propertyoptions-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Properties Item + \brief Provides conditional setting of properties. + + \note This documents the \c Properties item in the context of products. Usage within + a \c SubProject item is described \l{SubProject Item}{here}. + + The \c Properties item is an auxiliary item for setting multiple property values conditionally. + + In the following example, two properties are set if the project is built for Windows: + \code + Product { + Properties { + condition: qbs.targetOS.contains("windows") + cpp.defines: ["ON_WINDOWS"] + cpp.includePaths: ["extraWindowsIncludes"] + } + } + \endcode + + Multiple \c Properties items can be specified to set properties dependent on different + conditions. The order of appearance is important. Semantics are similar to if-else-chains. + The following example + \code + Product { + Properties { + condition: qbs.targetOS.contains("windows") + cpp.defines: ["ON_WINDOWS"] + cpp.includePaths: ["myWindowsIncludes"] + } + Properties { + condition: qbs.targetOS.contains("linux") + cpp.defines: ["ON_LINUX"] + cpp.includePaths: ["myLinuxIncludes"] + } + cpp.defines: ["ON_UNKNOWN_PLATFORM"] + } + \endcode + is equivalent to + \code + Product { + cpp.defines: { + if (qbs.targetOS.contains("windows")) + return ["ON_WINDOWS"]; + if (qbs.targetOS.contains("linux")) + return ["ON_LINUX"]; + return ["ON_UNKNOWN_PLATFORM"]; + } + cpp.includePaths: { + if (qbs.targetOS.contains("windows")) + return ["myWindowsIncludes"]; + if (qbs.targetOS.contains("linux")) + return ["myLinuxIncludes"]; + return base; + } + } + \endcode + + We suggest to use the \c Properties item for mutually exclusive conditions only. It is + especially useful if there are several properties to set, based on the same condition. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li condition + \li bool + \li none - must be specified + \li The condition to be used for the other bindings in this item. + \row + \li overrideListProperties + \li bool + \li \c false + \li List properties set within this item will override the values coming from + modules, rather than getting merged with them, which is the default behavior. + Use this in the rare case that a module you depend on inserts a value into + a list property that is problematic for some product. + \endtable +*/ diff --git a/doc/reference/items/language/propertyoptions.qdoc b/doc/reference/items/language/propertyoptions.qdoc new file mode 100644 index 00000000..701f5ab4 --- /dev/null +++ b/doc/reference/items/language/propertyoptions.qdoc @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage properties-item.html + \page propertyoptions-item.html + \nextpage rule-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title PropertyOptions Item + \brief Provides inline documentation for properties within product and module items. + + A \c PropertyOptions item can appear inside a \l{Product Item} or \l{Module Item} to provide + inline documentation for properties. + + \section1 PropertyOptions Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li allowedValues + \li stringList + \li undefined + \li A list of the values permitted by the property. \c{undefined} indicates that any value + is permitted. + \row + \li description + \li string + \li undefined + \li A brief description of the property. + \row + \li name + \li string + \li undefined + \li The name of the property to document. + \endtable + +*/ diff --git a/doc/reference/items/language/rule.qdoc b/doc/reference/items/language/rule.qdoc new file mode 100644 index 00000000..8d53e384 --- /dev/null +++ b/doc/reference/items/language/rule.qdoc @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage propertyoptions-item.html + \page rule-item.html + \nextpage scanner-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Rule Item + \brief Creates transformers for input tags. + + In \QBS, rules create \e transformers that produce output files from input files. + The term \e transformer refers to a list of \l{Command and JavaScriptCommand}{commands}. + These commands are created in a rule's \e {prepare script}. They do the actual work, either + directly or by executing external commands. + + \section1 A Simple Example + + The following rule takes text files and replaces Windows-style line endings with their + Unix-style counterparts. We will look at it one piece at a time. + + \code + Rule { + multiplex: false + \endcode + A \e {multiplex rule} creates one transformer that takes all input artifacts with the + matching input file tag and creates one or more output artifacts. We are setting the + respective property to \c false here, indicating that we want to create one transformer + per input file. + \note This is actually the default, so the above assignment is not required. + \code + inputs: ["txt_input"] + \endcode + Here we are specifying that our rule is interested in input files that have the tag + "txt_input". Such files could be source files, in which case you would tag them + using a \l{Group Item}{Group}. Or they could in turn get generated by a different rule, + in which case that rule would assign the file tag. + The files matching the tag will be available in the prepare script under the name + \c inputs (see \l{inputs and outputs}{The inputs and outputs Variables}). + \code + Artifact { + filePath: input.fileName + ".out" + fileTags: ["txt_output"] + } + \endcode + Here we are specifying that for every input file, we want to create one output file + whose name is the same as the input file, but with an additional extension. Because we are + giving a relative path, \QBS will prepend that path by the product's build directory. + + In addition, we tell \QBS that the output files should get the file tag "txt_output". This + enables other rules to use these files as inputs. You must always assign suitable file tags + to your output artifacts, or the rule will not be run. + See \l{Rules and Product Types} for details. + + If you want to create more than one output file per input file, you simply provide multiple + \c Artifact items. The set of output artifacts will be available in the prepare script + under the name \c outputs (see \l{inputs and outputs}{The inputs and outputs Variables}). + + \code + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = input.fileName + "->" + output.fileName; + cmd.highlight = "codegen"; + cmd.sourceCode = function() { + var file = new TextFile(input.filePath); + var content = file.readAll(); + file.close() + content = content.replace(/\r\n/g, "\n"); + file = new TextFile(output.filePath, TextFile.WriteOnly); + file.write(content); + file.close(); + } + return [cmd]; + } + } + \endcode + The prepare script shown above puts everything together by creating the command that does + the actual transformation of the file contents, employing the help of the + \l{TextFile Service}{TextFile} class. + + As you can see, the return value is an array, meaning you can provide several commands to + implement the rule's functionality. For instance, if we had provided two \c Artifact items, + we might have also provided two commands, each of them creating one output file. + + For the \c input and \c output variables used in the code, see the next section. + + \target inputs and outputs + \section1 The \c inputs and \c outputs Variables + + We already mentioned that the input and output artifacts are available in the prepare script + via the variables \c inputs and \c outputs, respectively. These variables are JavaScript + objects whose property keys are file tags and whose property values are lists of objects + representing the artifacts matching these tags. In our example, the \c inputs variable + has a single property \c txt_input, whose value is a list with one element. Similarly, the + \c outputs variable also has one single property \c txt_output, again with a list containing + one element. + + The actual artifact objects have the following properties: + \table + \header + \li Property + \li Description + \row + \li \c baseName + \li The file name without any extension. + \row + \li \c completeBaseName + \li The file name without the last extension. + \row + \li \c fileName + \li The name of the file (that is, \c filePath without any directory components). + \row + \li \c filePath + \li The full file path. + \row + \li \c fileTags + \li The list of the artifact's file tags. + \row + \li \c moduleProperty + \li A function taking two parameters. The first one is the name of a module, + the second one is the name of a property in that module. For instance, for an + artifact in a C++ product, a call to \c{moduleProperty("cpp", "defines")} returns the + list of defines that will be passed when compiling the respective file. + \endtable + + But what about the variables \c input and \c output that appeared in our example? These + are simply convenience variables which are available in the case that the \c inputs + and \c outputs variables contain only one artifact, respectively. So in our example, instead + of \c input we also could have written \c {inputs.txt_input[0]}, which is considerably + more verbose. + + \section1 Rules and Product Types + + It is important to know that when figuring out which rules to execute, \QBS starts at the + product type and then looks for a way to produce artifacts with matching file tags from + source files, using a chain of rules that are connected by their respective input and output + tags. For instance, consider this simple C++ project: + \code + Product { + type: ["application"] + Depends { name: "cpp" } + files: ["main.cpp"] + } + \endcode + Here's how this product is built: + \list 1 + \li \QBS looks for a rule that can produce artifacts with the file tag + \c{"application"}. Such a rule is found in the \c cpp module (namely, the rule that + invokes the linker). + \li Since the rule found in the previous step takes inputs of type \c{"obj"}, \QBS now + looks for a rule that produces artifacts of that type. Again, such a rule is found in + the \c cpp module (the rule that runs the compiler). + \li The rule found in the previous step takes inputs of type \c{"cpp"}. No rule is found + that creates such artifacts, but we do have a source file with a matching type (because + the \c cpp module contains a \l{FileTagger item}{FileTagger} which attached that type + to \c{"main.cpp"} due to its file extension). + \li Now that there is a chain of rules leading from a source file tag to the product type, + the commands of these rules are executed one after the other until we end up with + our executable. + \endlist + + \section1 Rule Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li multiplex + \li bool + \li false + \li Determines whether this is a multiplex rule. + \row + \li inputs + \li string list + \li undefined + \li File tags the input artifacts must match. + All output artifacts will depend on all artifacts in the product with + the given input file tags. Also these artifacts are available in the + inputs variable of the prepare script. + \row + \li auxiliaryInputs + \li string list + \li undefined + \li A list of file tags. This rule will be dependent on every other rule + that produces artifacts that are compatible with \a{auxiliaryInputs}. + Unlike \a{inputs}, the property \a{auxiliaryInputs} has no effect on the content of the + \a{inputs} variable in the \a{prepare} script. + \row + \li excludedAuxiliaryInputs + \li string list + \li undefined + \li A list of file tags. Connections to rules that produce these file tags are prevented. + This property has no effect on the content of the \a{inputs} variable in the \a{prepare} + script. + \row + \li inputsFromDependencies + \li string list + \li undefined + \li File tags the artifacts of product dependencies must match. + For example, the product \a foo might appear as follows in the current product: + \code + Depends { + name: "foo" + } + \endcode + All artifacts of \a foo that match the given + file tags will appear in the \a inputs variable of the prepare + script. Also, each output artifact of this rule will be dependent on + those artifacts. + \row + \li outputArtifacts + \li array of objects + \li undefined + \li An array of output artifacts, specified as JavaScript objects. + Example: + \code + outputArtifacts: [{ + filePath: "myfile.cpp", + fileTags: ["cpp"], + cpp: { cxxLanguageVersion: "c++11" } + }] + \endcode + For a description of the possible properties, see the documentation of the + \l{Artifact item}. + Output artifacts can be specified either by \c{Rule.outputArtifacts} or by \c{Artifact} + items. Use \c{Rule.outputArtifacts} if the set of outputs is not fixed but dependent on + the input's content. If no file tags are provided, \QBS will apply all + \l{FileTagger Item}{file taggers} known in the current context to the output file name. + The user may set the property \c{explicitlyDependsOn} on artifact objects, which is + similar to \c{Rule.explicitlyDependsOn}. + \row + \li outputFileTags + \li string list + \li undefined + \li If output artifacts are specified by \c{Rule.outputArtifacts}, then + \c{Rule.outputFileTags} must be a list of file tags the rule potentially produces. + \row + \li condition + \li bool + \li true + \li If true, the rule is enabled, otherwise it does nothing. + \row + \li explicitlyDependsOn + \li string list + \li undefined + \li Each artifact that matches the file tags in \a explicitlyDependsOn + is added to the dependencies of each output node. + \row + \li prepare + \li script + \li undefined + \li Script that prepares the commands to transform the inputs to outputs. + The code in this script is treated as a function with the signature + \c{function(project, product, inputs, outputs, input, output)}. + The argument \c{input} is \c{undefined} if there's more than one input artifact for this + rule. Similarly, \c{output} is only defined if there's exactly one output artifact. + \row + \li alwaysRun + \li bool + \li false + \li If true, the rule's commands are always executed, even if all output artifacts + are up to date. + \endtable + +*/ diff --git a/doc/reference/items/language/scanner.qdoc b/doc/reference/items/language/scanner.qdoc new file mode 100644 index 00000000..44c93d16 --- /dev/null +++ b/doc/reference/items/language/scanner.qdoc @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage rule-item.html + \page scanner-item.html + \nextpage subproject-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title Scanner Item + \brief Creates custom dependency scanners in modules. + + An \c Scanner item can appear inside a \l{Module Item}, and allows to define + artifacts dependency, according to the artifacts contents. + For example scanner for "qrc" files: + \code + import qbs.Xml + + Module { + Scanner { + condition: true + inputs: 'qrc' + scan: { + var xml = new XmlDomDocument(input.filePath); + dependencies = []; + // do something with the xml + return dependencies; + } + } + } + \endcode + + \section1 Scanner Properties + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li condition + \li bool + \li true + \li If true, the scanner is enabled, otherwise it does nothing. + \row + \li inputs + \li string list + \li undefined + \li File tags the input artifacts must match. + \row + \li recursive + \li bool + \li false + \li Determines whether to scan dependencies recursively. + \row + \li searchPaths + \li script + \li undefined + \li Script that returns paths to look for dependencies. + The code in this script is treated as a function with the signature + \c{function(project, product, input)}. + \row + \li scan + \li script + \li undefined + \li Script that reads the input artifact and returns string list with dependencies. + The code in this script is treated as a function with the signature + \c{function(project, product, input)}. + \endtable +*/ diff --git a/doc/reference/items/language/subproject.qdoc b/doc/reference/items/language/subproject.qdoc new file mode 100644 index 00000000..644b600a --- /dev/null +++ b/doc/reference/items/language/subproject.qdoc @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage list-of-language-items.html + \previouspage scanner-item.html + \page subproject-item.html + \ingroup list-of-language-items + \ingroup list-of-items + + \title SubProject Item + \brief Adds a project from a different file. + + A \c SubProject item is used to add a project defined in another file as a sub-project to + the surrounding project: + \code + SubProject { + filePath: "subdir/project.qbs" + Properties { + name: "A sub-project" + } + } + \endcode + + If you don't need to set any properties on the sub-project, you can also use the \c references + property, the same way you would do for a product: + \code + references: "subdir/project.qbs" + \endcode + + It is also possible to nest \c Project items directly in the same file. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li filePath + \li path + \li empty + \li The file path of the project to add as a sub-project. If the top-level item in this + file is a \c Product, it gets wrapped automatically in a new project. + \row + \li inheritProperties + \li bool + \li true + \li Whether the sub-project should inherit the properties of the surrounding project. + You can use this feature to share "global" settings between (sub-)projects. + \endtable +*/ diff --git a/doc/reference/jsextensions/jsextension-environment.qdoc b/doc/reference/jsextensions/jsextension-environment.qdoc new file mode 100644 index 00000000..8579ecee --- /dev/null +++ b/doc/reference/jsextensions/jsextension-environment.qdoc @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page jsextension-environment.html + \ingroup list-of-builtin-services + + \title Environment Service + \brief Provides operations on the system environment. + + The \c Environment service offers access to the system environment or process environment. + + \section1 Available Operations + + \section2 currentEnv + \code + Environment.currentEnv(): { [key: string]: string; } + \endcode + Returns the environment of \QBS in the current context as an object whose properties are + the environment variables. + + \section2 getEnv + \code + Environment.getEnv(key: string): string + \endcode + Tries to find a variable with the given name in the current context's environment and returns + its value. If no such variable could be found, \c undefined is returned. + + \section2 putEnv + \code + Environment.putEnv(key: string, value: string): void + \endcode + Sets the value of the environment variable with the given name in the build or run environment. + This method is only available in the \c Module.setupBuildEnvironment and + \c Module.setupRunEnvironment scripts. + + \section2 unsetEnv + \code + Environment.unsetEnv(key: string): void + \endcode + Unsets the environment variable with the given name from the build or run environment. + This method is only available in the \c Module.setupBuildEnvironment and + \c Module.setupRunEnvironment scripts. +*/ diff --git a/doc/reference/jsextensions/jsextension-file.qdoc b/doc/reference/jsextensions/jsextension-file.qdoc new file mode 100644 index 00000000..c12fcc4e --- /dev/null +++ b/doc/reference/jsextensions/jsextension-file.qdoc @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page jsextension-file.html + \ingroup list-of-builtin-services + + \title File Service + \brief Provides operations on the file system. + + The \c File service offers limited access to the file system for operations such as copying + or removing files. + + \section1 Available Operations + + \section2 copy + \code + File.copy(sourceFilePath: string, targetFilePath: string): boolean + \endcode + Copies \c sourceFilePath to \c targetFilePath. Any directory components in \c targetFilePath + that do not yet exist will be created. If \c sourceFilePath is a directory, a recursive + copy will be made. If an error occurs, a JavaScript exception will be thrown. + \note \c targetFilePath must be the counterpart of \c sourceFilePath at the new location, + \b{not} the new parent directory. This allows the copy to have a different name and is true + even if \c sourceFilePath is a directory. + + \section2 exists + \code + File.exists(filePath: string): boolean + \endcode + Returns true if and only if there is a file at \c filePath. + + \section2 directoryEntries + \code + File.directoryEntries(path: string, filter: File.Filter): string[] + \endcode + Returns a sorted list of the directory \c{path}'s contents non-recursively, + filtered by \c filter. The values of \c filter are equivalent to Qt's \c QDir::Filter. + + \section2 lastModified + \code + File.lastModified(filePath: string): number + \endcode + Returns the time of last modification for the file at \c filePath. The concrete semantics of the + returned value are platform-specific. You should only rely on the property that a smaller value + indicates an older timestamp. + + \section2 makePath + \code + File.makePath(path: string): boolean + \endcode + Makes the directory at \c path, creating intermediate directories if necessary. + Conceptually equivalent to \c{mkdir -p} + + \section2 move + \code + File.move(oldPath: string, newPath: string, overwrite: boolean = true): boolean + \endcode + Renames the file \c oldPath to \c newPath. + Returns \c true if successful; otherwise returns \c false. + If a file with the name \c newPath already exists, and \c overwrite is \c false, + \c move() returns \c false (that is, the file will not be overwritten). + + \section2 remove + \code + File.remove(filePath: string): boolean + \endcode + Removes the file at \c filePath. In case of a directory, it will be removed recursively. +*/ diff --git a/doc/reference/jsextensions/jsextension-fileinfo.qdoc b/doc/reference/jsextensions/jsextension-fileinfo.qdoc new file mode 100644 index 00000000..1dbd7564 --- /dev/null +++ b/doc/reference/jsextensions/jsextension-fileinfo.qdoc @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page jsextension-fileinfo.html + \ingroup list-of-builtin-services + + \title FileInfo Service + \brief Provides operations on file paths. + + The \c FileInfo service offers various operations on file paths, such as turning absolute + paths into relative ones, splitting a path into its components, and so on. + + \section1 Available Operations + + \section2 baseName + \code + FileInfo.baseName(filePath: string): string + \endcode + Returns the file name of \c filePath up to (but not including) the first '.' character. + + \section2 completeBaseName + \code + FileInfo.completeBaseName(filePath: string): string + \endcode + Returns the file name of \c filePath up to (but not including) the last '.' character. + + \section2 fileName + \code + FileInfo.fileName(filePath: string): string + \endcode + Returns the last component of \c filePath, that is, everything after the + last '/' character. + + \section2 fromWindowsSeparators + \code + FileInfo.fromWindowsSeparators(filePath: string): string + \endcode + Returns \c filePath with all '\\' characters replaced by '/'. + + \section2 isAbsolutePath + \code + FileInfo.isAbsolutePath(filePath: string, hostOS?: string[]): boolean + \endcode + Returns true if \c filePath is an absolute path and false if it is a relative one. + If \c hostOS is specified, treats \c filePath as a file path of the kind found on that platform. + This parameter defaults to the host OS on which \QBS is running and should normally be omitted. + + \section2 joinPaths + \code + FileInfo.joinPaths(...paths: string[]): string + \endcode + Concatenates the given paths using the '/' character. + + \section2 path + \code + FileInfo.path(filePath: string, hostOS?: string[]): string + \endcode + Returns the part of \c filePath that is not the file name, that is, + everything up to + (but not including) the last '/' character. If \c filePath is just a file name, then '.' + is returned. If \c filePath ends with a '/' character, then the file name is assumed to be empty + for the purpose of the above definition. + If \c hostOS is specified, treats \c filePath as a file path of the kind found on that platform. + This parameter defaults to the host OS on which \QBS is running and should normally be omitted. + + \section2 relativePath + \code + FileInfo.relativePath(dirPath: string, filePath: string): string + \endcode + Returns a relative path so that joining \c dirPath and the returned path results in \c filePath. + If necessary, '..' components are inserted. + The function assumes \c dirPath and \c filePath to be absolute paths and \c dirPath to + be a directory. + + \section2 toWindowsSeparators + \code + FileInfo.toWindowsSeparators(filePath: string): string + \endcode + Returns \c filePath with all '/' characters replaced by '\\'. +*/ diff --git a/doc/reference/jsextensions/jsextension-process.qdoc b/doc/reference/jsextensions/jsextension-process.qdoc new file mode 100644 index 00000000..a14155f0 --- /dev/null +++ b/doc/reference/jsextensions/jsextension-process.qdoc @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page jsextension-process.html + \ingroup list-of-builtin-services + + \title Process Service + \brief Allows you to start external processes. + + The \c Process service allows you to start processes, track their output, + and so on. + + \section1 Available Operations + + \section2 Constructor + \code + Process() + \endcode + Allocates and returns a new Process object. + + \section2 close + \code + close(): void + \endcode + Frees the resources associated with the process. It is recommended to always call this function as + soon as you are finished with the process. + + \section2 closeWriteChannel + \code + closeWriteChannel(): void + \endcode + Schedules the stdin channel of process to be closed. The channel will close once all data has been + written to the process. After calling this function, any attempts to write to the process will do + nothing. See \c QProcess::closeWriteChannel() for more details. + + \section2 exec + \code + exec(filePath: string, arguments: string[], throwOnError: boolean): number + \endcode + Executes the program at \c filePath with the given argument list and blocks until the + process is finished. If an error occurs (for example, there is no executable + file at \c filePath) + and \c throwOnError is true, then a JavaScript exception will be thrown. Otherwise + (the default), -1 will be returned in case of an error. The normal return code is the exit code + of the process. + + \section2 exitCode + \code + exitCode(): number + \endcode + Returns the exit code of the process. This is needed for retrieving the exit code from + processes started via \c start(), rather than \c exec(). + + \section2 getEnv + \code + getEnv(varName: string): string + \endcode + Returns the value of the variable \c varName in the process' environment. + + \section2 kill + \code + kill(): void + \endcode + Kills the process, causing it to exit immediately. + + \section2 readLine + \code + readLine(): string + \endcode + Reads and returns one line of text from the process output, without the newline character(s). + + \section2 readStdErr + \code + readStdErr(): string + \endcode + Reads and returns all data from the process' standard error channel. + + \section2 readStdOut + \code + readStdOut(): string + \endcode + Reads and returns all data from the process' standard output channel. + + \section2 setCodec + \code + setCodec(codec) + \endcode + Sets the text codec to \c codec. The codec is used for reading and writing from and to + the process, respectively. The supported codecs are the same as for \c QTextCodec, for example: + "UTF-8", "UTF-16", and "ISO 8859-1". + + \section2 setEnv + \code + setEnv(varName: string, varValue: string): string + \endcode + Sets the value of variable \c varName to \c varValue in the process environment. + This only has an effect if called before the process is started. + + \section2 setWorkingDirectory + \code + setWorkingDirectory(path: string): void + \endcode + Sets the directory the process will be started in. + This only has an effect if called before the process is started. + + \section2 start + \code + start(filePath: string, arguments: string[]): boolean + \endcode + Starts the program at \c filePath with the given list of arguments. Returns \c{true} if the + process could be started and \c{false} otherwise. + \note This call returns right after starting the process and should be used only if you need + to interact with the process while it is running. Most of the time, you want to use \c exec() + instead. + + \section2 terminate + \code + terminate(): void + \endcode + Tries to terminate the process. This is not guaranteed to make the process exit immediately; + if you need that, use \c kill(). + + \section2 waitForFinished + \code + waitForFinished(timeout: number): boolean + \endcode + Blocks until the process has finished or \c timeout milliseconds have passed (default is 30000). + Returns true if the process has finished and false if the operation has timed out. + Calling this function only makes sense for processes started via \c start() (as opposed to + \c exec()). + + \section2 workingDirectory + \code + workingDirectory(): string + \endcode + Returns the directory the process will be started in. + + \section2 write + \code + write(data: string): void + \endcode + Writes \c data into the process' input channel. + + \section2 writeLine + \code + writeLine(data: string): void + \endcode + Writes \c data, followed by the newline character(s), into the process' input channel. +*/ diff --git a/doc/reference/jsextensions/jsextension-propertylist.qdoc b/doc/reference/jsextensions/jsextension-propertylist.qdoc new file mode 100644 index 00000000..869873ae --- /dev/null +++ b/doc/reference/jsextensions/jsextension-propertylist.qdoc @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page jsextension-propertylist.html + \ingroup list-of-builtin-services + + \title PropertyList Service + \brief Provides read, write and convert operations on property list files. + + The \c PropertyList service allows you to read and write property list files in + all formats supported by the Core Foundation API: XML, binary, JSON, and OpenStep (read-only). + + This service is only available on Darwin platforms such as iOS, macOS, tvOS, and watchOS. + + \section1 Available operations + + \section2 Constructor + \code + PropertyList() + \endcode + Allocates and returns a new PropertyList object. + + \section2 clear + \code + clear(): void + \endcode + Voids the property list by deleting its internal object reference. + + \section2 isEmpty + \code + isEmpty(): boolean + \endcode + Returns true if the property list has no internal object reference set, otherwise false. + + \section2 format + \code + format(): string + \endcode + Returns the data format that the property list data was deserialized from. This property is set + after calling \c readFromString or \c readFromFile. + Possible return values include: \c "binary1", \c "json", \c "openstep", and \c "xml1". + If the property list object is empty or the input format could not be determined, + returns \c undefined. + + \section2 readFromFile + \code + readFromFile(filePath: string): void + \endcode + Parses the file and stores the result in the property list. + Throws an exception if an I/O error occurs or the input is in an invalid format. + + \section2 readFromObject + \code + readFromObject(obj: any): void + \endcode + Sets the given object as the property list's internal object. + \c format() will return \c undefined as this method does not deserialize a storage format. + + \section2 readFromString + \code + readFromString(input: string): void + \endcode + Parses \c input and stores the result in the property list. + This is most useful for initializing a property list object from the result of a + \c JSON.stringify call. + Throws an exception if the input is in an invalid format. + + \section2 toObject + \code + toObject(): any + \endcode + Returns an object representing the property list. + + \section2 toJSON + \code + toJSON(style: string = "compact"): string + \endcode + Returns a string representation of the property list in JSON format. + Possible values for \c style include \c "pretty" and \c "compact". The default is compact. + + \section2 toString + \code + toString(format: string): string + \endcode + Returns a string representation of the property list in the specified format. + Possible values for \c format include: \c "json" (compact), \c "json-compact", \c "json-pretty", + and \c "xml1". Currently, the OpenStep format is not supported. + Throws an exception if the object cannot be written in the given format. + + \section2 toXMLString + \code + toXMLString(): string + \endcode + Returns a string representation of the property list in XML format. + This function is a synonym for \c toString("xml1"). + + \section2 writeToFile + \code + writeToFile(filePath: string, format: string): void + \endcode + Writes the property list to the file in the given format. + Possible values for \c format include: \c "binary1", \c "json" (compact), \c "json-compact", + \c "json-pretty", and \c "xml1". Currently, the OpenStep format is not supported for writing. + Throws an exception if an I/O error occurs or the object cannot be written in the given format. +*/ diff --git a/doc/reference/jsextensions/jsextension-temporarydir.qdoc b/doc/reference/jsextensions/jsextension-temporarydir.qdoc new file mode 100644 index 00000000..fedb42d2 --- /dev/null +++ b/doc/reference/jsextensions/jsextension-temporarydir.qdoc @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page jsextension-temporarydir.html + \ingroup list-of-builtin-services + + \title TemporaryDir Service + \brief Creates a unique directory for temporary use. + + The \c PropertyList service creates a unique directory for temporary use. + + \section1 Available Operations + + \section2 Constructor + \code + TemporaryDir() + \endcode + Allocates and returns a new TemporaryDir object. + This method creates the temporary directory. + + \section2 isValid + \code + isValid(): boolean + \endcode + Returns \c true if the temporary directory was created successfully. + + \section2 path + \code + path(): string + \endcode + Returns the path to the temporary directory. + Empty if the temporary directory could not be created. + + \section2 remove + \code + remove(): boolean + \endcode + Removes the temporary directory, including all its contents. + Returns \c true if removing was successful. + It is recommended to always call this function as soon as you are finished with the temporary + directory. The directory will not be removed automatically. +*/ diff --git a/doc/reference/jsextensions/jsextension-textfile.qdoc b/doc/reference/jsextensions/jsextension-textfile.qdoc new file mode 100644 index 00000000..2dec89e2 --- /dev/null +++ b/doc/reference/jsextensions/jsextension-textfile.qdoc @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page jsextension-textfile.html + \ingroup list-of-builtin-services + + \title TextFile Service + \brief Provides read and write operations on text files. + + The \c TextFile service allows you to read from and write into text files. + + \section1 Related Declarations + + \section2 TextFile.OpenMode + \code + enum TextFile.OpenMode { ReadOnly, WriteOnly, ReadWrite } + \endcode + List of modes that a file may be opened in. + + \section1 Available operations + + \section2 Constructor + \code + TextFile(filePath: string, openMode: OpenMode = TextFile.ReadOnly) + \endcode + Opens the file at \c filePath in the given mode and returns the object representing the file. + \note The mode influences which of the operations listed below can actually be used on the file. + + \section2 atEof + \code + atEof(): boolean + \endcode + Returns \c{true} if no more data can be read from the file, \c{false} otherwise. + + \section2 close + \code + close(): void + \endcode + Closes the file. It is recommended to always call this function as soon as you are finished + with the file, in order to keep the number of in-flight file descriptors as low as possible. + + \section2 readAll + \code + readAll(): string + \endcode + Reads all data from the file and returns it. + + \section2 readLine + \code + readLine(): string + \endcode + Reads one line of text from the file and returns it. The returned string does not contain + the newline characters. + + \section2 setCodec + \code + setCodec(codec: string): void + \endcode + Sets the text codec to \c codec. The supported codecs are the same as for \c QTextCodec, + for example: "UTF-8", "UTF-16", and "ISO 8859-1". + + \section2 truncate + \code + truncate(): void + \endcode + Truncates the file, that is, gives it the size of zero, removing all content. + + \section2 write + \code + write(data: string): void + \endcode + Writes \c data into the file at the current position. + + \section2 writeLine + \code + writeLine(data: string): void + \endcode + Writes \c data into the file at the current position and appends the newline character(s). +*/ diff --git a/doc/reference/jsextensions/jsextension-utilities.qdoc b/doc/reference/jsextensions/jsextension-utilities.qdoc new file mode 100644 index 00000000..a933894e --- /dev/null +++ b/doc/reference/jsextensions/jsextension-utilities.qdoc @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page jsextension-utilities.html + \ingroup list-of-builtin-services + + \title Utilities Service + \brief Provides miscellaneous operations. + + The \c Utilities service offers miscellaneous operations. + + \section1 Available Operations + + \section2 getHash + \code + Utilities.getHash(key: string): string + \endcode + Calculates a 16-byte hash of the input and returns it. + Rules in modules should use this function to find unique locations for output + artifacts in the build directory without duplicating the whole directory structure of + the respective input file (to deal with the case of two files with the same name in different + subdirectories of the same product). + + \section2 rfc1034Identifier + \code + Utilities.rfc1034Identifier(str: string): string + \endcode + Returns an RFC 1034 compliant identifier based on the given string by replacing each character + that is not Latin alphanumeric or \c{.} with \c{-}. +*/ diff --git a/doc/reference/jsextensions/jsextensions-general.qdoc b/doc/reference/jsextensions/jsextensions-general.qdoc new file mode 100644 index 00000000..ed7b26f8 --- /dev/null +++ b/doc/reference/jsextensions/jsextensions-general.qdoc @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage index.html + \page jsextensions-general.html + \ingroup list-of-builtin-services + + \title General Services + \brief Provides various operations. + + These are operations that do not fit into any of the other categories. + They are automatically available in any \QBS project file or JavaScript file. + + \section1 Available Operations + + \section2 loadFile + \code + loadFile(filePath: string): any + \endcode + Loads a JavaScript file and returns an object that contains the evaluated context of this file. + This function is only available in JavaScript files. + For example: + \code + var MyFunctions = loadFile("myfunctions.js"); + MyFunctions.doSomething(); + \endcode + + \section2 loadExtension + \code + loadExtension(extensionName: string): any + \endcode + Loads a \QBS extension and returns an object that contains all functions of that extension. + This function is only available in JavaScript files. + For example: + \code + var FileInfo = loadExtension("qbs.FileInfo"); + var fileName = FileInfo.fileName(filePath); + \endcode + + + \section1 Extensions to JavaScript Built-in Objects + + \section2 Array.contains + \code + Array.contains(e: any): boolean + \endcode + Returns \c{true} if the array contains the element \c{e}. Returns \c{false} otherwise. + + \section2 Array.containsAll + \code + Array.containsAll(other: any[]): boolean + \endcode + Returns \c{true} if the array contains every element in the \c{other} array. + Returns \c{false} otherwise. + + \section2 Array.containsAny + \code + Array.containsAny(other: any[]): boolean + \endcode + Returns \c{true} if the array contains some element(s) in the \c{other} array. + Returns \c{false} otherwise. + + \section2 Array.uniqueConcat + \code + Array.uniqueConcat(other: any[]): any[] + \endcode + Returns a copy of this array joined with the array \c{other}. + Duplicates that would originate from the concatenation are removed. + The order of elements is preserved. + + \section2 String.contains + \code + String.contains(s: string): boolean + \endcode + Returns \c{true} if the string contains the substring \c{s}. Returns \c{false} otherwise. + + \section2 startsWith + \code + String.startsWith(s: string): boolean + \endcode + Returns \c{true} if the string starts with the substring \c{s}. Returns \c{false} otherwise. + + \section2 endsWith + \code + String.endsWith(s: string): boolean + \endcode + Returns \c{true} if the string ends with the substring \c{s}. Returns \c{false} otherwise. + + + \section1 Console API + + \QBS provides a subset of the non-standard Console API available in most ECMAScript runtimes. + + The output of each of these functions will only be displayed if the logging level is at least + the level which the function outputs at. Logging levels from lowest to highest are: + 'error', 'warning', 'info', 'debug', and 'trace'. The default is 'info'. + + \warning The contents of this section are subject to change in order to align with future + \l{https://www.w3.org/2011/08/browser-testing-charter.html}{standardization} + \l{https://github.com/DeveloperToolsWG/console-object/blob/master/api.md}{processes}. + + \section2 console.debug + \code + console.debug(s: string): void + \endcode + This method is an alias for \c{console.log()}. + + \section2 console.error + \code + console.error(s: string): void + \endcode + Logs an \c{error} level message. + Outputs to stderr when the logger output is a terminal. + The string will be prefixed with \c{"ERROR: "} and colored red when the logger output is a + color-capable terminal. + + \section2 console.info + \code + console.info(s: string): void + \endcode + Logs an \c{info} level message. + Outputs to stdout when the logger output is a terminal. + + \section2 console.log + \code + console.log(s: string): void + \endcode + Logs a \c{debug} level message. + Outputs to stderr when the logger output is a terminal. + + \section2 console.warn + \code + console.warn(s: string): void + \endcode + Logs a \c{warning} level message. + Outputs to stderr when the logger output is a terminal. + The string will be prefixed with \c{"WARNING: "} and colored yellow when the logger output is a + color-capable terminal. +*/ diff --git a/doc/reference/list-of-tools.qdoc b/doc/reference/list-of-tools.qdoc new file mode 100644 index 00000000..710cc3c4 --- /dev/null +++ b/doc/reference/list-of-tools.qdoc @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage reference.html + \page list-of-tools.html + + \title List of Command-line Tools + \brief Auxiliary tools + + In addition to the \c qbs command itself, a number of auxiliary tools are provided. Their file + names follow the pattern \c{qbs-}, and they can be invoked either using that file + name or as \c{qbs }. + + This page is intended to give a short overview of these tools. For the supported parameters, + see the respective help screen, which you get by calling \c{qbs help }. + + \section1 config + + Manages \QBS settings like preferences and profiles. + + \section1 config-ui + + Like \c config, but with a graphical user interface. + + \section1 qmltypes + + Dumps information about the QML types supplied by \QBS. This is not intended as documentation + for users, but as tooling support. + + \section1 setup-android + + Creates \QBS profiles for Android SDK and NDK installations. + + \section1 setup-qt + + Creates \QBS profiles for Qt installations. + + \section1 setup-toolchains + + Creates \QBS profiles for toolchains like GCC or MSVC. + +*/ diff --git a/doc/reference/modules/android-ndk-module.qdoc b/doc/reference/modules/android-ndk-module.qdoc new file mode 100644 index 00000000..e341ddb3 --- /dev/null +++ b/doc/reference/modules/android-ndk-module.qdoc @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page android-ndk-module.html + \ingroup list-of-modules + + \title Module Android.ndk + \since 1.4 + \brief Provides support for building native Android libraries. + + The \c Android.ndk module contains the properties and rules to create native libraries + for use in \l{AndroidApk Item}{Android application packages}. + + Normally, you will not use this module directly, but instead work + with the \l{DynamicLibrary Item}{DynamicLibrary} and \l{StaticLibrary Item}{StaticLibrary} + items that \QBS provides. + Here is what the project file for the "hello-jni" example that comes with the NDK could look + like: + \code + import qbs + + Project { + DynamicLibrary { + name: "hello-jni" + architectures: ["mips", "x86"] + files: ["jni/hello-jni.c"] + } + + AndroidApk { + name: "HelloJni" + packageName: "com.example.hellojni" + Depends { productTypes: ["android.nativelibrary"] } + } + } + \endcode + + \section1 Android.ndk Properties + + These properties are set automatically when creating an Android profile via the + \c setup-android tool. + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li abi + \li string + \li 1.4 + \li undefined + \li The ABI name as it appears under "lib/" in the application package. + Corresponds to APP_ABI in Android.mk. + \row + \li appStl + \li string + \li 1.4 + \li \c{"system"} + \li The library to use for C++. The possible values are: "system", "gabi++_static", + "gabi++_shared", "stlport_static", "stlport_shared", "gnustl_static", "gnustl_shared", + "c++_static", "c++_shared". + \row + \li ndkDir + \li path + \li 1.4 + \li undefined + \li The NDK base directory. + \row + \li platform + \li string + \li 1.4 + \li \c{"android-9"} + \li The versioned platform name. + \endtable + + \section1 Relevant File Tags + + \table + \header + \li Tag + \li Since + \li Description + \row + \li \c{"android.nativelibrary"} + \li 1.4.0 + \li This tag is attached to dynamic libraries that will end up in APK packages. + You do not normally need to use the tag explicitly, as it is the default type of the + \l {DynamicLibrary Item}{DynamicLibrary} item for Android targets. + \endtable + +*/ diff --git a/doc/reference/modules/android-sdk-module.qdoc b/doc/reference/modules/android-sdk-module.qdoc new file mode 100644 index 00000000..197a8071 --- /dev/null +++ b/doc/reference/modules/android-sdk-module.qdoc @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page android-sdk-module.html + \ingroup list-of-modules + + \title Module Android.sdk + \since 1.4 + \brief Provides support for building Android packages. + + The \c Android.sdk module contains the properties and rules to create Android application + packages from Java sources, resources and so on. + + Normally, you will not use this module directly, but instead work + with the \l{AndroidApk Item}{AndroidApk item} that \QBS provides. + + \section1 Android.sdk Properties + + These properties are set automatically when creating an Android profile via the + \c setup-android tool. + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li buildToolsVersion + \li string + \li 1.4 + \li undefined + \li The version of the build tools such as aapt and dx. The \c setup-android + tool sets this to the highest version available in the SDK. + \row + \li ndkDir + \li string + \li 1.4 + \li undefined + \li The NDK base directory, if an NDK is present. + \row + \li platform + \li string + \li 1.4 + \li undefined + \li The versioned platform name (e.g. "android-21"). The \c setup-android + tool sets this to the highest version available in the SDK. + \row + \li sdkDir + \li string + \li 1.4 + \li undefined + \li The SDK base directory. + \endtable + + \section1 Relevant File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"android.aidl"} + \li \c{*.aidl} + \li 1.4.0 + \li This tag is used for Android AIDL files. One Java source file will be generated for each + such file. + \row + \li \c{"android.assets"} + \li - + \li 1.4.0 + \li This tag is used for Android assets, which are typically located in an \c{assets/} + subdirectory. Using the \l {AndroidApk Item}{AndroidApk} item takes care of tagging + these files for you. + \row + \li \c{"android.apk"} + \li n/a + \li 1.4.0 + \li This tag is attached to the output artifact of the rule that creates an APK package. + It is the default type of the \l {AndroidApk Item}{AndroidApk} item. + \row + \li \c{"android.manifest"} + \li \c{AndroidManifest.xml} + \li 1.4.0 + \li This tag is used for the Android manifest. There must be one such file for every + Android app. + \row + \li \c{"android.resources"} + \li - + \li 1.4.0 + \li This tag is used for Android resources, which are typically located in a \c{res/} + subdirectory. Using the \l {AndroidApk Item}{AndroidApk} item takes care of tagging + these files for you. + \endtable +*/ diff --git a/doc/reference/modules/archiver-module.qdoc b/doc/reference/modules/archiver-module.qdoc new file mode 100644 index 00000000..13cda125 --- /dev/null +++ b/doc/reference/modules/archiver-module.qdoc @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page archiver-module.html + \ingroup list-of-modules + + \title Module archiver + \since 1.4 + \brief Provides support for building archives. + + The \c archiver module contains the properties and rules for creating (compressed) archives. + The output artifact has the file tag "archiver.archive". The sole input artifact is a text file + containing the list of files to package, with one file path per line. The paths can be + relative, in which case they will be looked for at \c{archiver.workingDirectory}. The file tag + of this input artifact is "archiver.input-list". + + \section1 archiver Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li flags + \li stringList + \li 1.4 + \li empty list + \li Custom options not covered by any of the other properties. + \row + \li archiveBaseName + \li string + \li 1.4 + \li \c{product.targetName} + \li The base name of the archive file (in other words, the file name without + any extensions). + \row + \li compressionLevel + \li string + \li 1.4 + \li \c undefined + \li How much effort to put into the compression of a 7-Zip or zip archive. + Possible values are \c undefined, "0", "1", "2", "3", "4", "5", "6", "7", "8" and "9" + Higher numbers result in a smaller archive, but the compressing process will take more + time. 7-Zip only supports 0 and odd numbers. + A value of \c undefined means to use the default compression level. + \row + \li compressionType + \li string + \li 1.4 + \li \c{"gz"} for tar archives, otherwise \c undefined + \li How to compress a tar or zip archive. + Possible options are "none", "gz", "bz2", "Z", "xz", "deflate", "store". + \c undefined uses the archiver's default compression type. + \row + \li outputDirectory + \li string + \li 1.4 + \li \c{product.destinationDirectory} + \li Where to put the archive file. + \row + \li type + \li string + \li 1.4 + \li \c{undefined} + \li Which kind of archiver to use. + The currently supported values are: "tar", "7zip", "zip". + \row + \li workingDirectory + \li string + \li 1.4 + \li undefined + \li The directory in which to execute \c command. + \row + \li command + \li string + \li 1.4 + \li Depends on \c{type}. + \li The command with which to invoke the archiver. + \endtable +*/ diff --git a/doc/reference/modules/bundle-module.qdoc b/doc/reference/modules/bundle-module.qdoc new file mode 100644 index 00000000..92b51029 --- /dev/null +++ b/doc/reference/modules/bundle-module.qdoc @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jake Petroules. +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page bundle-module.html + \ingroup list-of-modules + + \title Module bundle + \since 1.4 + \brief Provides Core Foundation bundle support. + + The \c bundle module contains properties and rules for building and working with + Core Foundation bundles on Apple platforms (commonly known as CFBundles or simply "bundles"), + directories with a standardized hierarchical structure that hold executable code and resources. + Examples include applications, frameworks, and plugins. + + This module is available on all platforms but is currently only useful on Apple platforms. + + \note Core Foundation bundles are not to be confused with Mach-O loadable modules, which are + also referred to as (loadable) "bundles" in Apple parlance. + In \QBS, Core Foundation bundles are referred to as "bundles", + while Mach-O loadable bundles are referred to as "loadable modules". + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li isBundle + \li \c{bool} + \li 1.4 + \li \c{true} for applications and dynamic libraries on Apple platforms, \c{false} otherwise + \li Whether the product should actually be packaged as a bundle as opposed to a flat file. + This allows a product indirectly dependent on the \c{bundle} module to retain control of + whether it should actually be built as a bundle. + \row + \li isShallow + \li \c{bool} (read only) + \li 1.4 + \li \c{false} on macOS, otherwise \c{true} + \li Whether the bundle directory tree is "shallow", i.e. whether it lacks a Contents + subdirectory. This is the default on all platforms other than macOS. + \row + \li identifierPrefix + \li \c{string} + \li 1.4 + \li \c{org.example} + \li Prefix for the product's bundle identifier. If \c{identifier} is left unset, the bundle + identifier will be a concatenation of this value and the \c{identifier} property, + separated by a '.'. This corresponds to the organization identifier in Xcode. + \row + \li identifier + \li \c{string} + \li 1.4 + \li combination of \c{identifierPrefix} and the product's target name formatted as an + RFC 1034 identifier + \li The bundle's identifier. If left unset, the bundle identifier will be a concatenation + of this value and the \c{identifierPrefix} property, separated by a '.'. + \row + \li extension + \li \c{string} + \li 1.4 + \li \c{"app"} for \c{"APPL"} packages, + \c{"framework"} for \c{"FMWK"} packages, + \c{"bundle"} for \c{"BNDL"} and custom packages + \li The extension of the bundle's wrapper directory (minus the leading '.'). This property + should not normally need to be set unless creating a custom bundle type. + \row + \li packageType + \li \c{string} + \li 1.4 + \li \c{"APPL"} for applications, + \c{"FMWK"} for frameworks, + \c{"BNDL"} for custom bundles + \li The four-letter file type code of the bundle, specified in the bundle's PkgInfo file and + in the bundle's Info.plist as the value for the CFBundlePackageType key. + This property should almost never need to be changed, though specifying an + alternative package type for custom bundles is allowed. + \row + \li generatePackageInfo + \li \c{bool} + \li 1.5 + \li \c{true} for applications, otherwise \c{false} + \li Whether to generate a PkgInfo file for the bundle. + This property should almost never need to be changed, though enabling it when specifying + an alternative package type for custom bundles using \c{packageType} is allowed. + \row + \li signature + \li \c{string} + \li 1.4 + \li \c{"????"} + \li The four-letter signature specific to the bundle, also known as the creator code, + specified in the bundle's PkgInfo file and in the bundle's Info.plist as the value for + the CFBundleSignature key. This property should normally never need to be set. + \row + \li bundleName + \li \c{string} + \li 1.4 + \li combination of the product's \c{targetName} and bundle's \c{extension} + \li The file name of the bundle's wrapper directory. + This property should not normally need to be changed. + \row + \li frameworkVersion + \li \c{string} + \li 1.4 + \li \c{"A"} + \li For framework bundles, the version of the framework. Unused for other package types. + \row + \li publicHeaders + \li \c{pathList} + \li 1.4 + \li \c{undefined} + \li List of public header files to copy to a framework bundle's Headers subdirectory. + \row + \li privateHeaders + \li \c{pathList} + \li 1.4 + \li \c{undefined} + \li List of private header files to copy to a framework bundle's PrivateHeaders subdirectory + \row + \li resources + \li \c{pathList} + \li 1.4 + \li \c{undefined} + \li List of resources to copy to a bundle's Resources subdirectory. Files will automatically + be copied into lproj subdirectories corresponding to the input files' paths. + \row + \li infoPlist + \li \c{object} + \li 1.4 + \li \c{undefined} + \li Dictionary of key-value pairs to add to the bundle's Info.plist. + The contents of this property will be aggregated with the values from any plist files. + If \c{infoPlist} and any plist files contain the same key, the former will take + precedence, but may also be overridden during postprocessing (see \c{processInfoPlist}). + If undefined, will not be taken into account. + \row + \li processInfoPlist + \li \c{bool} + \li 1.4 + \li \c{true} + \li Whether to perform post-processing on the aggregated Info.plist contents. + If this property is \c{true}, various post-processing operations will be applied to the + bundle's property list dictionary after it has been aggregated from the contents of any + plist files on disk, and the \c{infoPlist} property. + First, values from a list of defaults will be added to the dictionary if they were not + already present. Then, values from the AdditionalInfo key of the platform SDK's + Info.plist file will be added to the dictionary if they were not already present, as + well as some other miscellaneous keys, such as BuildMachineOSBuild and UIDeviceFamily + (on iOS). Finally, variable expansions will be performed such that substrings of the + form $(VAR) or ${VAR} will be replaced with their corresponding environment variables. + \row + \li embedInfoPlist + \li \c{bool} + \li 1.4 + \li \c{true} if the product is a command line tool, otherwise \c{false}. + \li Whether to create a __TEXT section in the product's executable containing the processed + Info.plist. Only applies to command line applications. + \row + \li infoPlistFormat + \li \c{string} + \li 1.4 + \li \c{"binary1"} for iOS; + \c{"same-as-input"} or \c{"xml1"} for macOS depending on whether a plist file is used; + \c{undefined} for all other operating systems. + \li The file format to write the product's resulting Info.plist in. + Possible values: \c{"xml1"}, \c{"binary1"}, \c{"json"}, \c{"same-as-input"} + \endtable + + \section1 Path Specific Properties + + All properties in this section are read-only properties of type \c{string} specifying file paths + relative to the directory containing the bundle. + + \table + \header + \li Property + \li Since + \li Description + \row + \li infoPlistPath + \li 1.4 + \li Path that the Info.plist file will be written to. + \row + \li infoStringsPath + \li 1.5 + \li Path that the InfoPlist.strings file will be written to. + \row + \li pkgInfoPath + \li 1.4 + \li Path that the PkgInfo file will be written to. + \row + \li versionPlistPath + \li 1.4 + \li Path that the version.plist file will be written to. + \row + \li executablePath + \li 1.4 + \li Path that the main executable file will be written to. + \row + \li contentsFolderPath + \li 1.4 + \li Path of the bundle's Contents subdirectory. + \row + \li documentationFolderPath + \li 1.5 + \li Path of the directory where documentation will be written. + \row + \li executableFolderPath + \li 1.4 + \li Path of the directory where the main exectuable will be written. + Not to be confused with \c{executablesFolderPath}. + \row + \li executablesFolderPath + \li 1.4 + \li Path of the directory where auxiliary executables will be copied. + Not to be confused with \c{executableFolderPath}. + \row + \li frameworksFolderPath + \li 1.4 + \li Path of the directory where internal frameworks will be copied. + \row + \li javaFolderPath + \li 1.5 + \li Path of the directory where Java content will be written. + \row + \li localizedResourcesFolderPath + \li 1.5 + \li Path of the directory where localized resource files will be copied. + \row + \li pluginsFolderPath + \li 1.4 + \li Path of the directory where plugins will be copied. + \row + \li privateHeadersFolderPath + \li 1.4 + \li Path of the directory where private header files will be copied. + \row + \li publicHeadersFolderPath + \li 1.4 + \li Path of the directory where public headers files will be copied. + \row + \li scriptsFolderPath + \li 1.4 + \li Path of the directory where script files will be copied. + \row + \li sharedFrameworksFolderPath + \li 1.4 + \li Path of the directory where shared frameworks will be copied. + \row + \li sharedSupportFolderPath + \li 1.4 + \li Path of the directory where shared support files will be copied. + \row + \li unlocalizedResourcesFolderPath + \li 1.4 + \li Path of the directory where non-localized resource files will be copied. + This is the same as the base resources path. + \row + \li versionsFolderPath + \li 1.5 + \li Path of the bundle's Versions subdirectory. + This is only relevant for (non-shallow) framework bundles. + \endtable +*/ diff --git a/doc/reference/modules/cpp-module.qdoc b/doc/reference/modules/cpp-module.qdoc new file mode 100644 index 00000000..160ee977 --- /dev/null +++ b/doc/reference/modules/cpp-module.qdoc @@ -0,0 +1,812 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page cpp-module.html + \ingroup list-of-modules + + \title Module cpp + \since 1.0 + \brief Provides C/C++ support. + + The \c cpp module contains the properties and rules for toolchains of the C/C++ family. + On Apple platforms this includes support for Objective-C/C++. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li allowUnresolvedSymbols + \li \c{bool} + \li 1.2 + \li \c{false} + \li Switch this on if you want the linking step to succeed even if the resulting binary + contains unresolved symbols. Normally this makes little sense, but in special cases it + is possible that the respective symbols will be available at load time even if they are + not present during linking. + \row + \li architecture + \li \c{string} + \li 1.0 + \li \c{qbs.architecture} + \li Target architecture. See \c{qbs.architecture}. + \row + \li debugInformation + \li \c{bool} + \li 1.0 + \li \c{qbs.debugInformation} + \li Generate debug information. See \c{qbs.debugInformation}. + \row + \li separateDebugInformation + \li \c{bool} + \li 1.4 + \li \c{false} for gcc/clang, \c{true} for MSVC + \li Whether to store debug information in an external file or bundle instead of within the + binary. + \row + \li defines + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li List of preprocessor macros that gets passed to the compiler. + To set macro values use the following syntax: + \c{cpp.defines: ["USE_COLORS=1", 'COLOR_STR="blanched almond"']} + \row + \li platformDefines + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li List of preprocessor macros that are used for all projects that are built for the + current target platform. User project files usually do not set this property. + \row + \li includePaths + \li \c{pathList} + \li 1.0 + \li \c{undefined} + \li List of include paths. Relative paths are considered to be relative to the .qbs product + file they are used in. + \row + \li systemIncludePaths + \li \c{pathList} + \li 1.0 + \li \c{undefined} + \li List of include paths that are passed as system include paths to the compiler. + For header files in those paths warnings will be ignored. + Relative paths are considered to be relative to the .qbs product file they are used in. + \row + \li systemRunPaths + \li \c{stringList} + \li 1.6 + \li Auto-detected for host builds on Linux via \c ldconfig, \c{["/lib", "/usr/lib"]} + otherwise on Unix, empty on Windows + \li The paths the dynamic linker uses on process start-up to locate dynamic libraries. + \row + \li libraryPaths + \li \c{pathList} + \li 1.0 + \li \c{undefined} + \li List of library search paths. Relative paths are considered to be relative to the .qbs + product file they are used in. + \row + \li dynamicLibraries + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li List of dynamic libraries to be linked. If the library is part of your project, consider + using a Depends item instead. + \row + \li staticLibraries + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li List of static libraries to be linked. If the library is part of your project, consider + using a Depends item instead. + \row + \li prefixHeaders + \li \c{pathList} + \li 1.0.1 + \li \c{undefined} + \li List of files to automatically include at the beginning of each source file in the + product. + \row + \li optimization + \li \c{string} + \li 1.0 + \li \c{qbs.optimization} + \li Optimization level. See \c{qbs.optimization}. + \row + \li treatWarningsAsErrors + \li \c{bool} + \li 1.0 + \li \c{false} + \li Warnings will be handled as errors and cause the build to fail. + \row + \li useCPrecompiledHeader, useCxxPrecompiledHeader, useObjcPrecompiledHeader, + useObjcxxPrecompiledHeader + \li \c bool + \li 1.5 + \li \c false + \li Specifies whether to use a precompiled header for the respective language, if one + is present (see \l{Relevant File Tags}{below} for the associated file tags). + \row + \li warningLevel + \li \c{string} + \li 1.0 + \li \c{"all"} + \li Specifies the warning level for the compiler - \c{"none"} or \c{"all"}. + \row + \li driverFlags + \li \c{stringList} + \li 1.6 + \li undefined + \li Flags that are added to all compilation and linking commands performed by the compiler + driver, independently of the language. + \row + \li commonCompilerFlags + \li \c{stringList} + \li 1.0.1 + \li undefined + \li Flags that are added to all compilation commands independently of the language. + \row + \li compilerVersionMajor + \li \c{int} + \li 1.4 + \li undefined + \li The major version of the compiler. + \row + \li compilerVersionMinor + \li \c{int} + \li 1.4 + \li undefined + \li The minor version of the compiler. + \row + \li compilerVersionPatch + \li \c{int} + \li 1.4 + \li undefined + \li The patch level component of the compiler version. + \row + \li assemblerFlags + \li \c{stringList} + \li 1.5 + \li undefined + \li Additional flags for the assembler. + \row + \li cppFlags + \li \c{stringList} + \li 1.0 + \li undefined + \li Additional flags for the C preprocessor. + \row + \li cFlags + \li \c{stringList} + \li 1.0 + \li undefined + \li Additional flags for the C compiler. + \row + \li cxxFlags + \li \c{stringList} + \li 1.0 + \li undefined + \li Additional flags for the C++ compiler. + \row + \li cLanguageVersion + \li \c{string} + \li 1.4 + \li undefined + \li The version of the C standard with which the code must comply. If this property is set, + corresponding compiler and/or linker flags will be added, depending on the toolchain. + If the value is left undefined, the compiler default will be used. + Possible values include: \c{"c89"}, \c{"c99"}, \c{"c11"} + \row + \li cxxLanguageVersion + \li \c{string} + \li 1.4 + \li undefined + \li The version of the C++ standard with which the code must comply. If this property is + set, corresponding compiler and/or linker flags will be added, depending on the + toolchain. If the value is left undefined, the compiler default will be used. + Possible values include: \c{"c++98"}, \c{"c++11"}, \c{"c++14"} + \row + \li cxxStandardLibrary + \li \c{string} + \li 1.4 + \li undefined + \li The C++ standard library to link to. If this property is set, corresponding compiler + and/or linker flags will be added, assuming the value is valid for the current + toolchain. If the value is left undefined, the compiler default will be used. + Possible values include: \c{"libstdc++"}, \c{"libc++"} + \row + \li objcFlags + \li \c{stringList} + \li 1.0 + \li undefined + \li Additional flags for the Objective-C compiler. + \row + \li objcxxFlags + \li \c{stringList} + \li 1.0 + \li undefined + \li Additional flags for the Objective-C++ compiler. + \row + \li linkerFlags + \li \c{stringList} + \li 1.0 + \li undefined + \li Additional flags for the linker. These flags should \e not be escaped using the -Wl or + -Xlinker syntaxes, as \QBS will do this automatically based on the linker being used. + See cpp.linkerMode for additional information. + \row + \li assemblerName + \li \c{string} + \li 1.5 + \li determined by qbs-setup-toolchains + \li Name of the assembler binary. This is set in the build profile. + \row + \li assemblerPath + \li \c{string} + \li 1.5 + \li determined by qbs-setup-toolchains + \li Full path of the assembler binary. This is set in the build profile. + \row + \li compilerName + \li \c{string} + \li 1.0 + \li determined by qbs-setup-toolchains + \li Name of the main compiler binary. This is set in the build profile. + \row + \li compilerPath + \li \c{string} + \li 1.0 + \li determined by qbs-setup-toolchains + \li Full path of the main compiler binary. This is set in the build profile. + If the toolchain provides different compilers for different languages, then + \c{compilerPathByLanguage} is used. + \row + \li compilerPathByLanguage + \li \c{string} to \c{string} map + \li 1.3 + \li determined by qbs-setup-toolchains + \li Maps file tags to full paths of compiler binaries. This is set in the build profile. + \row + \li compilerWrapper + \li \c{stringList} + \li 1.1 + \li \c{undefined} + \li Wrapper binary and its arguments for wrapping compiler calls. + This is useful for compiler wrappers like ccache and alike. + \row + \li linkerName + \li \c{string} + \li 1.1.1 + \li determined by qbs-setup-toolchains + \li Name of the linker binary. This is set in the build profile. + \row + \li linkerPath + \li \c{string} + \li 1.1.1 + \li determined by qbs-setup-toolchains + \li Full path of the linker binary. This is set in the build profile. + \row + \li linkerWrapper + \li \c{stringList} + \li 1.6.2 + \li \c{undefined} + \li Wrapper binary and its arguments for wrapping linker calls. + This is useful for linker wrappers as needed by Bullseye Coverage, for example. + \row + \li entryPoint + \li \c{string} + \li 1.3 + \li \c{undefined} + \li Name of the entry point of an executable or dynamic library. If this property is + undefined, the toolchain's default is used. + \row + \li runtimeLibrary + \li \c{string} + \li 1.3.3 + \li \c{"dynamic"} for MSVC, \c{undefined} for others + \li Type of the used runtime library. Accepted values are \c{"static"} and \c{"dynamic"}. + If this property is set to \c{undefined}, then the default runtime library of the + toolchain is used. + \note For MSVC the default value is \c{"dynamic"}. + \note At the moment this property is only functional for MSVC. + \row + \li enableExceptions + \li \c{bool} + \li 1.5 + \li \c{true} + \li Whether to enable exceptions in C++ code. + \row + \li exceptionHandlingModel + \li \c{string} + \li 1.5 + \li \c{"default"} + \li The exception handling model to use. For MSVC, this can be \c{"default"}, \c{"seh"} or + \c{"externc"}. For all other compilers, \c{"default"} indicates the default or only + exception handling model. + \row + \li enableRtti + \li \c{bool} + \li 1.5 + \li \c{undefined} + \li Whether to enable runtime type information in C++ code. + \row + \li enableReproducibleBuilds + \li \c{bool} + \li 1.5 + \li \c{false} + \li Try to generate reproducible object files. Some compilers (notably gcc) use random + numbers for generating symbol names that have to be different in every compilation + unit. This is avoided by setting this property to \c{true}. + \endtable + + \section1 Properties Specific to Apple Platforms + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li dsymutilFlags + \li \c{stringList} + \li 1.4.1 + \li undefined + \li Additional flags for the dsymutil tool. + \row + \li dsymutilPath + \li \c{string} + \li 1.4 + \li determined by qbs-setup-toolchains + \li Full path of the dsymutil binary. This is set in the build profile. + \row + \li frameworkPaths + \li \c{pathList} + \li 1.0 + \li \c{undefined} + \li List of framework search paths. Relative paths are considered to be relative to the + .qbs product file they are used in. + \row + \li systemFrameworkPaths + \li \c{pathList} + \li 1.0 + \li \c{undefined} + \li List of framework search paths. Relative paths are considered to be relative to the + .qbs product file they are used in. Header files in frameworks in those paths will not + cause warnings. + \row + \li frameworks + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li List of frameworks to be linked. + If the framework is part of your project, consider using a Depends item instead. + \row + \li weakFrameworks + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li List of frameworks to be weakly linked. + If the framework is part of your project, consider using a Depends item instead. + \row + \li automaticReferenceCounting + \li \c{bool} + \li 1.4 + \li \c{undefined} + \li Whether to enable Automatic Reference Counting (ARC) for Objective-C and Objective-C++ + source code. If \c{undefined}, uses the compiler default (probably \c{false}). + \row + \li requireAppExtensionSafeApi + \li \c{bool} + \li 1.4 + \li \c{undefined} + \li Whether to enforce the use of only app-extension-safe APIs on Apple platforms. This is + necessary for building Application Extensions in OS X Yosemite and iOS 8 and above. If + \c{undefined}, uses the compiler and linker defaults (probably \c{false}). + \row + \li minimumIosVersion + \li \c{string} + \li 1.0 + \li undefined, but may be set by generated profiles + \li A version number in the format [major].[minor] indicating the earliest version of iOS + that the product should run on. Passes -miphoneos-version-min= to the compiler. + If undefined, compiler defaults will be used. + \row + \li minimumOsxVersion + \li \c{string} + \li 1.0.1 + \li undefined, but may be set by generated profiles + \li Deprecated in \QBS 1.5.2. Use \c minimumMacosVersion instead. + \row + \li minimumMacosVersion + \li \c{string} + \li 1.5.2 + \li undefined, but may be set by generated profiles + \li A version number in the format [major].[minor] indicating the earliest version of macOS + that the product should run on. Passes -mmacosx-version-min= to the compiler. + If undefined, compiler defaults will be used. + \row + \li minimumWatchosVersion + \li \c{string} + \li 1.5 + \li undefined, but may be set by generated profiles + \li A version number in the format [major].[minor] indicating the earliest version of + Apple watchOS that the product should run on. + If undefined, compiler defaults will be used. + \row + \li minimumTvosVersion + \li \c{string} + \li 1.5 + \li undefined, but may be set by generated profiles + \li A version number in the format [major].[minor] indicating the earliest version of + Apple tvOS that the product should run on. + If undefined, compiler defaults will be used. + \endtable + + \section1 Properties Specific to Unix Platforms + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li archiverName + \li \c{string} + \li 1.0 + \li \c{"ar"} + \li Name of the archiver binary. This is set in the build profile. + \row + \li archiverPath + \li \c{string} + \li 1.0 + \li determined by qbs-setup-toolchains + \li Full path of the archiver binary. This is set in the build profile. + \row + \li exportedSymbolsCheckMode + \li \c{string} + \li 1.4.1 + \li "ignore-undefined" + \li Controls how \QBS determines whether an updated dynamic library causes relinking of + dependents. The default value is \c "ignore-undefined", which means that undefined + symbols being added or removed do not cause any relinking. If that should happen, for + example because dependent products are linked with an option such as + \c "--no-undefined", then this property can be set to \c "strict". + \row + \li linkerMode + \li \c{string} + \li 1.6 + \li "automatic" + \li Controls whether to automatically use an appropriate compiler frontend in place of the + system linker when linking binaries. The default is \c{"automatic"}, which chooses + either the C++ compiler, C compiler, or system linker specified by the \c{linkerName} + and \c{linkerPath} properties, depending on the type of object files present on the + linker command line. \c{"manual"} allows you to explicitly specify the linker using the + \c{linkerName} and \c{linkerPath} properties. + \row + \li nmName + \li \c{string} + \li 1.2 + \li \c{"nm"} + \li Name of the nm binary. This is set in the build profile. + \row + \li nmPath + \li \c{string} + \li 1.2 + \li determined by qbs-setup-toolchains + \li Full path of the nm binary. This is set in the build profile. + \row + \li objcopyName + \li \c{string} + \li 1.4 + \li \c{"objcopy"} + \li Name of the objcopy binary. This is set in the build profile. + \row + \li objcopyPath + \li \c{string} + \li 1.4 + \li determined by qbs-setup-toolchains + \li Full path of the objcopy binary. This is set in the build profile. + \row + \li stripName + \li \c{string} + \li 1.4 + \li \c{"strip"} + \li Name of the strip binary. This is set in the build profile. + \row + \li stripPath + \li \c{string} + \li 1.4 + \li determined by qbs-setup-toolchains + \li Full path of the strip binary. This is set in the build profile. + \row + \li positionIndependentCode + \li \c{bool} + \li 1.0 + \li \c{undefined} + \li Generate position independent code. + If this property is \c{undefined}, then position independent code is generated for + libraries, but not for applications. + \row + \li rpaths + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li List of rpaths that are passed to the linker. Paths that also appear in + \c systemRunPaths are ignored. + \row + \li sonamePrefix + \li \c{string} + \li 1.5 + \li \c{undefined} + \li If defined, the value of this variable is used as a path to be prepended to + the built shared library's \c SONAME identifier. The \c SONAME + (\c LC_ID_DYLIB on Apple platforms, \c DT_SONAME on other Unix-like platforms) is the + identifier that the dynamic linker will later use to reference the library. + In general, this reference may be a library name or full library path. + + On Apple platforms, the path may contain the following placeholders: + + \list + \li \b @rpath - + Expands to paths defined by LC_RPATH Mach-O commands in + the current process executable or the referring libraries. + \li \b @executable_path - + Expands to the current process executable location. + \li \b @loader_path - + Expands to the referring executable or library location. + \endlist + + In most cases, using \c @rpath is sufficient and recommended. + However, the prefix may be also specified using different placeholders, or + an absolute path. + + For more information, see the + \l{https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/dyld.1.html}{dyld documentation} + on dynamic library install names. + \row + \li soVersion + \li \c{string} + \li 1.7 + \li Major part of \c{product.version} if a version is set, otherwise the empty string + \li The version to be appended to the soname in ELF shared libraries. + \row + \li useRPaths + \li \c{bool} + \li 1.3 + \li \c{true} + \li Set this property to \c{false} to prevent the linker from writing rpaths to the binary. + \row + \li visibility + \li \c{string} + \li 1.0 + \li \c{"default"} + \li Visibility level for exported symbols. + Possible values include: \c{"default"}, \c{"hidden"}, \c{"hiddenInlines"}, + and \c{"minimal"}, which combines \c{"hidden"} and \c{"hiddenInlines"}. + \endtable + + \section1 Properties Specific to Windows + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li generateManifestFile + \li \c{bool} + \li 1.5.0 + \li \c{true} + \li Specifies whether to auto-generate a manifest file and include it in the binary. + Disable this property if you provide your own rc file. + \row + \li windowsApiCharacterSet + \li \c{string} + \li 1.0.1 + \li \c{"unicode"} + \li Specifies the character set used in the Win32 API. "unicode" will define the + preprocessor symbols UNICODE and _UNICODE, "mbcs" will define _MBCS, and + setting the value to undefined will use the default character set. + \row + \li minimumWindowsVersion + \li \c{string} + \li 1.0 + \li undefined, but may be set by generated profiles + \li A version number in the format [major].[minor] indicating the earliest version of + Windows that the product should run on. Defines WINVER, _WIN32_WINNT, and + _WIN32_WINDOWS, and applies a version number to the linker flags /SUBSYSTEM and + /OSVERSION for MSVC or --major-subsystem-version, --minor-subsystem-version, + --major-os-version and --minor-os-version for MinGW. + If undefined, compiler defaults will be used. + \endtable + + \section1 Advanced Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li compilerDefines + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li List of preprocessor macros that are used for all projects that are using the current + toolchain. User project files usually do not set this property. + \row + \li compilerIncludePaths + \li \c{pathList} + \li 1.6 + \li determined automatically + \li List of #include search paths that are used for all projects that are using the current + toolchain. Determined automatically by probing the compiler. + User project files usually do not set this property. + \row + \li compilerFrameworkPaths + \li \c{pathList} + \li 1.6 + \li determined automatically + \li List of framework search paths that are used for all projects that are using the current + toolchain. Determined automatically by probing the compiler. + User project files usually do not set this property. + \row + \li compilerLibraryPaths + \li \c{pathList} + \li 1.6 + \li determined automatically + \li List of library search paths that are used for all projects that are using the current + toolchain. Determined automatically by probing the compiler. + User project files usually do not set this property. + \endtable + + \section1 Relevant File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"application"} + \li n/a + \li 1.0.1 + \li The rule that creates executable files (typically via a linker) attaches this tag + to its output artifact. + \row + \li \c{"asm"} + \li \c{*.s} (for GCC-like toolchains), \c{*.asm} (for MSVC) + \li 1.1.0 + \li Source files with this tag serve as inputs to a rule invoking the toolchain's + assembler. One object file is generated for each such file. + \row + \li \c{"asm_cpp"} + \li \c{*.S}, \c{*.sx} + \li 1.1.0 + \li Like \c{"asm"}, but for source files that need preprocessing. This tag only has an + effect with GCC-like toolchains. + \row + \li \c{"c"} + \li \c{*.c} + \li 1.0.1 + \li Source files with this tag serve as inputs to a rule invoking the toolchain's + C compiler. One object file is generated for each such file. + \row + \li \c{"cpp"} + \li \c{*.C}, \c{*.cpp}, \c{*.cxx}, \c{*.c++}, \c{*.cc} + \li 1.0.1 + \li Source files with this tag serve as inputs to a rule invoking the toolchain's + C++ compiler. One object file is generated for each such file. + \row + \li \\c{"c_pch_src"}, \c{"cpp_pch_src"}, \c{"objc_pch_src"}, \c{"objcpp_pch_src"} + \li - + \li 1.5 + \li Files with this tag will be turned into precompiled headers for C, C++, Objective-C + and Objective-C++, respectively. There can be only one such file per product and + language. + \row + \li \c{"dynamiclibrary"} + \li n/a + \li 1.0.1 + \li The rule that creates dynamic libraries (typically via a linker) attaches this tag + to its output artifact. + \row + \li \c{"hpp"} + \li \c{*.h}, \c{*.H}, \c{*.hpp}, \c{*.hxx}, \c{*.h++} + \li 1.0.1 + \li This tag is used for header files (C, C++, Objective-C and Objective-C++). No rule + in this module generates output artifacts from such files directly, but the compiler + rule will have a dependency on all rules that create such files. + \row + \li \c{"linkerscript"} + \li - + \li 1.5.0 + \li This tag is used for \c ld linker scripts. You can provide such a file if you need + to replace the default linker script. + This file tag only has an effect with GCC-like toolchains. The linker needs to be + \c{ld}-compatible. + \row + \li \c{"obj"} + \li n/a + \li 1.0.1 + \li The rule that creates object files (typically via a compiler) attaches this tag + to its output artifacts. Such files are usually intermediate artifacts of the build + process and rarely need to be referenced in project files. + \row + \li \c{"objc"} + \li \c{*.m} + \li 1.1.0 + \li Source files with this tag serve as inputs to a rule invoking the toolchain's + Objective-C compiler. One object file is generated for each such file. + \row + \li \c{"objcpp"} + \li \c{*.mm} + \li 1.1.0 + \li Source files with this tag serve as inputs to a rule invoking the toolchain's + Objective-C++ compiler. One object file is generated for each such file. + \row + \li \c{"rc"} + \li \c{*.rc} + \li 1.1.0 + \li Files with this tag serve as inputs to the Windows resource compiler. One object file + is generated for each such file. The tag has no effect on target platforms other than + Windows. + \row + \li \c{"staticlibrary"} + \li n/a + \li 1.0.1 + \li The rule that creates static libraries (typically via a linker) attaches this tag + to its output artifact. + \row + \li \c{"versionscript"} + \li - + \li 1.5.0 + \li This tag is used for \c ld linker scripts. You can provide such a file if you need + fine-grained control over the symbols present in a shared library. + This file tag only has an effect with GCC-like toolchains. The linker needs to be + \c{ld}-compatible. + \endtable + +*/ diff --git a/doc/reference/modules/ib-module.qdoc b/doc/reference/modules/ib-module.qdoc new file mode 100644 index 00000000..65614687 --- /dev/null +++ b/doc/reference/modules/ib-module.qdoc @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page ib-module.html + \ingroup list-of-modules + + \title Module ib + \since 1.1 + \brief Provides support for Apple Interface Builder and related tools and file types. + + The \c ib module contains properties and rules for building Interface Builder documents, + storyboards, asset catalogs, and icon sets. + + This module is only available on Apple platforms. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li warnings + \li bool + \li 1.1 + \li \c{true} + \li Whether to print warnings when compiling. Does not apply to icon sets. + \row + \li errors + \li bool + \li 1.1 + \li \c{true} + \li Whether to print warnings when compiling. Does not apply to icon sets. + \row + \li notices + \li bool + \li 1.1 + \li \c{true} + \li Whether to print warnings when compiling. Does not apply to icon sets. + \row + \li flags + \li stringList + \li 1.1 + \li undefined + \li Additional flags to pass to the underlying tool (ibtool, actool, iconutil). + \endtable + + \section1 Properties Specific to NIBs and Storyboards + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li ibtoolName + \li string + \li 1.3 + \li \c{"ibtool"} + \li Name of the ibtool binary used to compile NIBs and storyboards. + This should not normally need to be changed. + \row + \li ibtoolPath + \li string + \li 1.3 + \li \c{ibtoolName} + \li Directory where the ibtool binary is located. + This should not normally need to be changed. + \row + \li flatten + \li bool + \li 1.1 + \li \c{true} + \li Compiles XIBs and storyboards into flattened (non-wrapper) files. + Set to \c{false} to preserve editability of the resulting nib and storyboard + bundles in Interface Builder. This property should not normally need to be changed. + \row + \li module + \li string + \li 1.3 + \li \c{undefined} + \li Sets the name of the module that the nib or storyboard is a part of. + Requires Xcode 6 or newer. + \row + \li autoActivateCustomFonts + \li bool + \li 1.3 + \li \c{true} + \li Instructs the ibtool compiler to add custom fonts to the + application's Info.plist when compiling XIBs and storyboards, + which will cause the fonts to activate upon application launch. + Requires Xcode 6 or newer. + \endtable + + \section1 Properties Specific to Asset Catalogs + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li actoolName + \li string + \li 1.3 + \li \c{"actool"} + \li Name of the actool binary used to compile asset catalogs. + This should not normally need to be changed. + \row + \li actoolPath + \li string + \li 1.3 + \li \c{actoolName} + \li Directory where the actool binary is located. + This should not normally need to be changed. + \row + \li appIconName + \li string + \li 1.3 + \li \c{undefined} + \li Name of the resource in the asset catalog that will be used as the application's icon. + Used to generate the partial Info.plist which will be merged into the resulting app. + If this property is \c{undefined}, no application icon will be specified. + \row + \li launchImageName + \li string + \li 1.3 + \li \c{undefined} + \li Name of the resource in the asset catalog that will be used as the application's launch + image. Used to generate the partial Info.plist which will be merged into the resulting + app. If this property is \c{undefined}, no launch image will be specified. + Only applies to iOS applications. + \row + \li compressPngs + \li bool + \li 1.3 + \li \c{true} + \li Whether to compress PNG image files when building asset catalogs. + \endtable + + \section1 Properties Specific to Icon Sets + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li iconutilName + \li string + \li 1.3 + \li \c{"iconutil"} + \li Name of the iconutil binary used to compile icon sets. + This should not normally need to be changed. + \row + \li iconutilPath + \li string + \li 1.3 + \li \c{iconutilName} + \li Directory where the iconutil binary is located. + This should not normally need to be changed. + \endtable +*/ diff --git a/doc/reference/modules/innosetup-module.qdoc b/doc/reference/modules/innosetup-module.qdoc new file mode 100644 index 00000000..7f86ed07 --- /dev/null +++ b/doc/reference/modules/innosetup-module.qdoc @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page innosetup-module.html + \ingroup list-of-modules + + \title Module innosetup + \since 1.7 + \brief Provides Inno Setup support. + + The \c innosetup module contains properties and rules for building + EXE setup packages with \l{http://www.jrsoftware.org/isinfo.php}{Inno Setup}. + Inno Setup 5 and above are supported. + + \note A typical Inno Setup Script includes an OutputBaseFilename command to set the filename + of the generated installer executable. However, \QBS overrides any OutputBaseFilename commands + found in the script by passing the /F option to the ISCC compiler, and therefore, you must use + the targetName property to set the filename. \QBS also overrides any Output commands by passing + the /O option to the ISCC compiler. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li defines + \li \c{stringList} + \li 1.7 + \li \c{undefined} + \li List of preprocessor macros that get passed to the compiler. + To set macro values use the following syntax: + \c{innosetup.defines: ["USE_COLORS=1", 'COLOR_STR="blanched almond"']} + \row + \li includePaths + \li \c{pathList} + \li 1.7 + \li \c{undefined} + \li List of include paths. Relative paths are considered to be relative to the .qbs product + file they are used in. + \row + \li verboseOutput + \li \c{bool} + \li 1.7 + \li \c{false} + \li Whether to display verbose output from the Inno Setup compiler. + \row + \li compilerFlags + \li \c{stringList} + \li 1.7 + \li \c{undefined} + \li Additional flags for the Inno Setup compiler. + \row + \li version + \li \c{string} + \li 1.7 + \li \c{undefined} + \li The Inno Setup version. + Consists of three numbers separated by dots, for instance "5.5.9". + \row + \li versionMajor + \li \c{int} + \li 1.7 + \li \c{versionParts[0]} + \li The Inno Setup major version. + \row + \li versionMinor + \li \c{int} + \li 1.7 + \li \c{versionParts[1]} + \li The Inno Setup minor version. + \row + \li versionParts + \li \c{list} + \li 1.7 + \li \c{empty} + \li The Inno Setup version as a list. + For instance, Inno Setup version 5.5.9 would correspond to a + value of \c[5, 5, 9]. + \row + \li versionPatch + \li \c{int} + \li 1.7 + \li \c{versionParts[2]} + \li The Inno Setup patch level. + \row + \li toolchainInstallPath + \li \c{path} + \li 1.7 + \li determined automatically + \li Inno Setup installation directory. + Determined by searching the registry for the latest version. + This should not normally need to be changed. + \row + \li compilerName + \li \c{string} + \li 1.7 + \li \c{"ISCC.exe"} + \li Name of the compiler binary. + This should not normally need to be changed. + \row + \li compilerPath + \li \c{string} + \li 1.7 + \li \c{compilerName} + \li Full path of the compiler binary. + This should not normally need to be changed. + \endtable + + \section1 Relevant File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"innosetup.iss"} + \li \c{"*.iss"} + \li 1.7 + \li Source files with this tag identify Inno Setup Script files, which serve as inputs + to a rule invoking the Inno Setup Script Compiler. + \row + \li \c{"innosetup.exe"} + \li n/a + \li 1.7 + \li The rule that creates Inno Setup executable files attaches this tag + (as well as the "application" tag) to its output artifact. + \endtable +*/ diff --git a/doc/reference/modules/java-module.qdoc b/doc/reference/modules/java-module.qdoc new file mode 100644 index 00000000..8a3511ac --- /dev/null +++ b/doc/reference/modules/java-module.qdoc @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page java-module.html + \ingroup list-of-modules + + \title Module java + \since 1.4 + \brief Provides Java support. + + The \c java module contains the properties and rules for building Java projects. + + \section1 Java Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li additionalClassPaths + \li stringList + \li 1.4 + \li undefined + \li Locations beside this product's class output path to consider when compiling. + \row + \li additionalCompilerFlags + \li stringList + \li 1.4 + \li undefined + \li Use this to supply compiler flags not covered by any of the properties in this module. + \row + \li additionalJarFlags + \li stringList + \li 1.4 + \li undefined + \li Use this to supply archiver flags not covered by any of the properties in this module. + \row + \li bootClassPaths + \li stringList + \li 1.4 + \li undefined + \li Use this if you need to specify non-standard bootstrap class files. + \row + \li compilerFilePath + \li string + \li 1.4 + \li \c compilerName, prefixed by \c jdkPath if it is defined + \li The command to invoke when compiling Java sources. + \row + \li compilerName + \li string + \li 1.4 + \li \c{"javac"} + \li The file name of the Java compiler. + \row + \li enableWarnings + \li bool + \li 1.4 + \li \c true + \li Controls whether warnings are emitted when compiling Java sources. + \row + \li interpreterFilePath + \li string + \li 1.4 + \li \c interpreterName, prefixed by \c jdkPath if it is defined + \li The command to invoke when executing Java code. + \row + \li interpreterName + \li string + \li 1.4 + \li \c{"java"} + \li The file name of the Java interpreter. + \row + \li jarFilePath + \li string + \li 1.4 + \li \c jarName, prefixed by \c jdkPath if it is defined + \li The command to run when creating or extracting \c jar files. + \row + \li jarName + \li string + \li 1.4 + \li \c{"jar"} + \li The file name of the \c jar tool. + \row + \li jdkIncludePaths + \li pathList + \li 1.4.1 + \li determined automatically + \li List of include paths for native header files. Applications using JNI to interface + with native code should add these paths to \c{cpp.includePaths}. + \row + \li jdkPath + \li path + \li 1.4 + \li determined automatically + \li The base path of the Java Development Kit (JDK). This is equivalent to the \c JAVA_HOME + environment variable, and by default will be determined automatically from one of the + following: + \list + \li \c JAVA_HOME environment variable (all platforms) + \li Registry (Windows) + \li \c java_home tool (macOS) + \li Known JDK paths (other Unix platforms) + \endlist + \row + \li languageVersion + \li string + \li 1.4 + \li undefined + \li The Java language version to interpret source code as. If undefined, the compiler + will use its default. + \row + \li runtimeVersion + \li string + \li 1.4 + \li undefined + \li The version of the Java runtime to generate compatible bytecode for. If undefined, + the compiler will use its default. + \row + \li manifest + \li object + \li 1.4.2 + \li undefined + \li The properties to add to the manifest file when building a JAR. + The contents of this property will be aggregated with the values from \c{manifestFile}. + If \c{manifest} and \c{manifestFile} contain the same key, the former will take + precedence. If undefined, will not be taken into account. + \row + \li manifestFile + \li path + \li 1.4.2 + \li undefined + \li The manifest file to embed when building a JAR. + The contents of this file will be aggregated with the values in \c{manifest}. + If \c{manifestFile} and \c{manifest} contain the same key, the latter will take + precedence. If undefined, will not be taken into account. + \row + \li manifestClassPath + \li stringList + \li 1.4.2 + \li undefined + \li The entries to add to the manifest's Class-Path when building a JAR. + \row + \li warningsAsErrors + \li bool + \li 1.4 + \li \c false + \li If this property is enabled, the compiler will abort where it would normally emit + a warning. + \endtable + + \section1 Relevant File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"java.class"} + \li - + \li 1.4 + \li This tag is attached to the output artifacts of the rule that runs the + \c javac tool. + \row + \li \c{"java.jar"} + \li - + \li 1.4 + \li This tag is attached to the output artifacts of the rule that runs the + \c jar tool. + \row + \li \c{"java.java"} + \li \c{*.java} + \li 1.4 + \li Source files with this tag serve as inputs to the rule running the \c javac tool. + \endtable + +*/ diff --git a/doc/reference/modules/lexyacc-module.qdoc b/doc/reference/modules/lexyacc-module.qdoc new file mode 100644 index 00000000..42156c86 --- /dev/null +++ b/doc/reference/modules/lexyacc-module.qdoc @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page lex_yacc-module.html + \ingroup list-of-modules + + \title Module lex_yacc + \since 1.6 + \brief Provides support for the \c lex and \c yacc tools. + + The \c lex_yacc module allows you to create scanners and parsers via the POSIX tools \c lex + and \c yacc, respectively. These tools are closely related and share a number of properties, + which is why they are represented by a single module. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li lexBinary + \li \c{string} + \li 1.6 + \li \c{"lex"} + \li The file path of the \c lex tool. + \row + \li lexFlags + \li \c{stringList} + \li 1.6 + \li \c{empty} + \li Additional command-line options for the \c lex tool. + \row + \li outputTag + \li \c{string} + \li 1.6 + \li \c{"c"} + \li The file tag for the generated scanner and parser sources. Use \c{"cpp"} if you want to + use a C++ compiler on them. + \row + \li uniqueSymbolPrefix + \li \c{bool} + \li 1.6 + \li \c{false} + \li If this property is \c true, the normal prefix \c yy used for the generated lexer + and parser functions will be replaced by the base name of the file provided as input + to \c lex and \c yacc, respectively. + Enable this property if you want to use more than one lexer or parser in a single + product. + \note Enabling this property requires associated lexer and scanner source files + to have the same base name. It also assumes a variant of \c lex that supports + the non-POSIX option \c{-P}, such as \c flex. + \row + \li yaccBinary + \li \c{string} + \li 1.6 + \li \c{"yacc"} + \li The file path of the \c yacc tool. + \row + \li yaccFlags + \li \c{stringList} + \li 1.6 + \li \c{empty} + \li Additional command-line options for the \c yacc tool. + \endtable + + \section1 Relevant File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"lex.input"} + \li \c{*.l} + \li 1.6 + \li Source files with this tag serve as inputs to the \c lex tool. + \row + \li \c{"yacc.input"} + \li \c{*.y} + \li 1.6 + \li Source files with this tag serve as inputs to the \c yacc tool. + \endtable + +*/ diff --git a/doc/reference/modules/nodejs-module.qdoc b/doc/reference/modules/nodejs-module.qdoc new file mode 100644 index 00000000..447fb6be --- /dev/null +++ b/doc/reference/modules/nodejs-module.qdoc @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page nodejs-module.html + \ingroup list-of-modules + + \title Module nodejs + \since 1.3 + \brief Provides Node.js support. + + The \c nodejs module contains support for running \l{http://nodejs.org}{Node.js} + applications from \QBS. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li applicationFile + \li path + \li 1.3 + \li \c{undefined} + \li The input JavaScript file whose corresponding output will be executed when running the + Node.js application. If this property is \c{undefined}, the product will not be + runnable. + \endtable +*/ diff --git a/doc/reference/modules/nsis-module.qdoc b/doc/reference/modules/nsis-module.qdoc new file mode 100644 index 00000000..20f1174e --- /dev/null +++ b/doc/reference/modules/nsis-module.qdoc @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page nsis-module.html + \ingroup list-of-modules + + \title Module nsis + \since 1.2 + \brief Provides Nullsoft Scriptable Install System support. + + The \c nsis module contains properties and rules for building EXE installers + for Windows using the Nullsoft Scriptable Install System. + + This module is available on all platforms. + + \note A typical NSIS script includes an OutFile command to set the filename of the generated + installer executable. However, \QBS overrides any OutFile commands found in the script, and + therefore, you must use the targetName property to set the filename. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li defines + \li stringList + \li 1.2 + \li \c{undefined} + \li List of preprocessor macros that get passed to the compiler. + To set macro values, use the following syntax: + \c{cpp.defines: ["USE_COLORS=1", 'COLOR_STR="blanched almond"']} + \row + \li disableConfig + \li bool + \li 1.2 + \li \c{false} + \li Whether to disable inclusion of nsisconf.nsh. + Generally you do not need to set this property. + \row + \li enableQbsDefines + \li bool + \li 1.2 + \li \c{true} + \li Whether to define preprocessor macros corresponding to values from the project and + product objects. When building a 64-bit package, the preprocessor variable \c{Win64} + will also be defined. + \row + \li warningLevel + \li string + \li 1.2 + \li \c{"normal"} + \li Severity of warnings to emit. The higher the level, the more warnings will be shown. + The levels \c{none}, \c{errors}, \c{warnings}, \c{info} and \c{all} correspond to NSIS + verbosity levels 0 through 4, inclusive. \c{normal} corresponds to the default level. + \row + \li compilerFlags + \li stringList + \li 1.2 + \li undefined + \li Additional flags for the NSIS compiler. + \row + \li compressor + \li string + \li 1.2 + \li \c{"default"} + \li Compression algorithm used to compress files and data in the installer. + Setting this property overrides any SetCompressor command in the NSI file being + compiled. Possible values include: \c{"default"}, \c{"zlib"}, \c{"zlib-solid"}, + \c{"bzip2"}, \c{"bzip2-solid"}, \c{"lzma"}, \c{"lzma-solid"} + \row + \li version + \li string + \li 1.2 + \li \c{undefined} + \li The NSIS version. Consists of four numbers separated by dots, for instance "2.46.0.0". + \row + \li versionMajor + \li int + \li 1.2 + \li \c{versionParts[0]} + \li The NSIS major version. + \row + \li versionMinor + \li int + \li 1.2 + \li \c{versionParts[1]} + \li The NSIS minor version. + \row + \li versionParts + \li list + \li 1.2 + \li \c{empty} + \li The NSIS version as a list. For instance, NSIS version 2.46.0.0 would correspond to a + value of \c[2, 46, 0, 0]. + \row + \li versionPatch + \li int + \li 1.2 + \li \c{versionParts[2]} + \li The NSIS patch level. + \row + \li versionBuild + \li int + \li 1.2 + \li \c{versionParts[3]} + \li The fourth NSIS version number component. + \row + \li toolchainInstallPath + \li path + \li 1.2 + \li determined automatically + \li NSIS installation directory. Determined by searching known registry keys and known + installation paths until a match is found. This should not normally need to be changed. + \row + \li compilerName + \li string + \li 1.2 + \li \c{"makensis"} + \li Name of the compiler binary. This should not normally need to be changed. + \row + \li compilerPath + \li string + \li 1.2 + \li \c{compilerName} + \li Directory where the compiler binary is located. This should not normally need to be + changed. + \endtable +*/ diff --git a/doc/reference/modules/qbs-module.qdoc b/doc/reference/modules/qbs-module.qdoc new file mode 100644 index 00000000..afca54da --- /dev/null +++ b/doc/reference/modules/qbs-module.qdoc @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage list-of-modules.html + \page qbs-module.html + \ingroup list-of-modules + + \title Module qbs + \since 1.0 + \brief Comprises general properties. + + The \c qbs module is implicitly loaded in every product. It contains properties of the current + build environment, independent of the used programming languages and toolchains. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li configurationName + \li \c{string} + \li 1.6 + \li \c{"default"} + \li Contains the name of the current build configuration. + \row + \li buildVariant + \li \c{string} + \li 1.0 + \li \c{"debug"} + \li Contains the name of the build variant for the current build. + \row + \li debugInformation + \li \c bool + \li 1.0 + \li \c{true} for debug builds, \c{false} otherwise + \li Specifies whether to generate debug information. + \row + \li enableDebugCode + \li \c bool + \li 1.0 + \li \c{true} for debug builds, \c{false} otherwise + \li Specifies whether to compile debug code in the product. + This is typically enabled for debug builds and disabled for release builds. + \row + \li optimization + \li \c{string} + \li 1.0 + \li \c{"none"} for debug builds, \c{"fast"} for release builds + \li Specifies the general type of optimization that should be performed by all toolchains. + Allowed values: \c{"none"}, \c{"fast"}, \c{"small"} + \row + \li targetOS + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li Specifies the OS you want to build the project for. This is typically set in a profile. + Possible values include one or more of the following: + \c{"aix"}, + \c{"android"}, + \c{"blackberry"}, + \c{"bsd"}, + \c{"bsd4"}, + \c{"bsdi"}, + \c{"cygwin"}, + \c{"darwin"}, + \c{"dgux"}, + \c{"dynix"}, + \c{"freebsd"}, + \c{"hpux"}, + \c{"hurd"}, + \c{"integrity"}, + \c{"ios"}, + \c{"ios-simulator"}, + \c{"irix"}, + \c{"linux"}, + \c{"lynx"}, + \c{"macos"}, + \c{"msdos"}, + \c{"nacl"}, + \c{"netbsd"}, + \c{"openbsd"}, + \c{"os2"}, + \c{"os2emx"}, + \c{"osf"}, + \c{"qnx"}, + \c{"qnx6"}, + \c{"reliant"}, + \c{"sco"}, + \c{"solaris"}, + \c{"symbian"}, + \c{"ultrix"}, + \c{"unix"}, + \c{"unixware"}, + \c{"vxworks"}, + \c{"windows"}, + \c{"windowsce"}, + \c{"windowsphone"}, + \c{"winrt"} + \row + \li architecture + \li \c{string} + \li 1.0 + \li \c{undefined} + \li Specifies the target platform's processor architecture. \c{undefined} indicates that + the target platform is architecture-independent (for example the CLR or JVM). + This is typically set in a profile. + Commonly used values are: \c{"x86"}, \c{"x86_64"} and \c{"arm"}. + \row + \li toolchain + \li \c{stringList} + \li 1.0 + \li \c{undefined} + \li Specifies the attributes of the toolchain that is going to be used for this build. + Typical values include: \c{"gcc"}, \c{"llvm"}, \c{"clang"}, \c{"mingw"}, \c{"msvc"} + \row + \li sysroot + \li \c{string} + \li 1.0 + \li \c{undefined} + \li Specifies the sysroot of the target platform. + This property is typically set in a profile for cross-compiling. + \row + \li pathListSeparator + \li \c{string} + \li 1.0 + \li \c{";"} on Windows, + \c{":"} on Unix + \li Holds the platform-specific separator for path list that is used in environment + variables or other contexts. + \row + \li nullDevice + \li \c{string} + \li 1.4.2 + \li \c{"NUL"} on Windows, + \c{"/dev/null"} on Unix + \li Holds the platform-specific file path corresponding to the null device. + \row + \li shellPath + \li \c{path} + \li 1.5 + \li \c{"%COMSPEC%"} on Windows, + \c{"/bin/sh"} on Unix + \li Holds the platform-specific file path corresponding to the command line interpreter. + On Windows this is the path to \c{cmd.exe}, which is held in the \c{COMSPEC} + environment variable - typically \c{C:/Windows/System32/cmd.exe}, + and on Unix-like platforms this is \c{/bin/sh}. + \endtable + + \section1 Environment Properties + + This section lists constant, read-only properties set by \QBS internally. + These properties should not be overridden. + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li hostOS + \li \c{stringList} (read only) + \li 1.0 + \li \c{undefined} + \li This property is set by \QBS internally and specifies the OS \QBS is running on. + The possible values for this property are the values of \c targetOS, though some may not + be supported. + \row + \li hostOSVersion + \li \c{string} (read only) + \li 1.2 + \li \c{undefined} + \li The host operating system version. Currently only defined for Windows and Apple + platforms. Consists of two or three numbers separated by dots, for instance "10.9" or + "6.3.9600". + \row + \li hostOSBuildVersion + \li \c{string} (read only) + \li 1.2 + \li \c{undefined} + \li The host operating system's build version. Currently only defined for Windows and Apple + platforms. On Windows, this is the 4 or 5 digit Windows build number and is equivalent + to \c versionPatch. On Apple platforms, this is a standard build number in the Apple + versioning scheme, for instance "13C64". + \row + \li hostOSVersionMajor + \li \c{int} (read only) + \li 1.2 + \li \c{hostOSVersionParts[0]} + \li The host operating system major version. + \row + \li hostOSVersionMinor + \li \c{int} (read only) + \li 1.2 + \li \c{hostOSVersionParts[1]} + \li The host operating system minor version. + \row + \li hostOSVersionParts + \li \c{list} (read only) + \li 1.2 + \li \c{empty} + \li The host operating system version as a list. + For instance, Windows 8.1 (version 6.3.9600) would correspond to a value of + \c[6, 3, 9600]. + \row + \li hostOSVersionPatch + \li \c{int} (read only) + \li 1.2 + \li \c{hostOSVersionParts[2]} + \li The host operating system patch level. + \row + \li version + \li \c{string} (read only) + \li 1.4.1 + \li + \li Version number of \QBS as a string, i.e. "1.4.1". + \row + \li versionMajor + \li \c{int} (read only) + \li 1.4.1 + \li + \li Major version number of \QBS. + \row + \li versionMinor + \li \c{int} (read only) + \li 1.4.1 + \li + \li Minor version number of \QBS. + \row + \li versionPatch + \li \c{int} (read only) + \li 1.4.1 + \li + \li Patch version number of \QBS. + \endtable + + \section1 Installation Properties + + This section lists properties specific to the \QBS installation mechanism. + See \l{Installing Files} for more information. + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li install + \li \c{bool} + \li 1.0 + \li \c{false} + \li Specifies whether to install a certain set of files. + This is typically set in a \c{Group} item to mark a number of files as installable. + \note Artifacts for which this property is enabled automatically receive the file tag + "installable". This is useful for writing packaging-related rules. + \row + \li installSourceBase + \li \c{string} + \li 1.4 + \li see below + \li Specifies the base directory of the local files that are going to be installed. The + source base directory is omitted from the target directory path specified in + \c{installDir}. The default value of this property is the directory of the current file + to be installed, relative to the product's source directory. + \row + \li installDir + \li \c{string} + \li 1.0 + \li \c{undefined} + \li Specifies the installation directory for the files of a product or a \c{Group}. The + value of this property is a path that is relative to \c installPrefix. + \row + \li installPrefix + \li \c{string} + \li 1.1 + \li \c{empty} + \li Specifies the global installation prefix. It is implicitly prepended to all values of + \c installDir. The \c installPrefix itself is relative to the \c installRoot in the + context of installation. + \row + \li installRoot + \li \c{string} + \li 1.4 + \li \c{/install-root} + \li Specifies the global installation root. It is implicitly prepended to all values + of \c installPrefix in the context of installation. + \note This property is fundamentally different from \c installDir and \c installPrefix + in that it must not be visible to the code being built. In fact, the install root is + often just a temporary location used to package the binaries, which should therefore not + assume they will be in that location at run-time. For the same reason, this property + must not be set from within project files. + \endtable +*/ + diff --git a/doc/reference/modules/qt-modules.qdoc b/doc/reference/modules/qt-modules.qdoc new file mode 100644 index 00000000..4b375d90 --- /dev/null +++ b/doc/reference/modules/qt-modules.qdoc @@ -0,0 +1,639 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page qt-modules.html + \ingroup list-of-modules + + \title Qt Modules + \brief Provide Qt support. + + The \c{Qt.*} modules contain properties and rules for Qt. + + \section1 Creating Dependencies to Qt Modules + + The Qt modules are special in that they are submodules within the \c Qt "namespace", which + has ramifications on the syntax when expressing dependencies. Assume your product depends + on the \c core and \c network modules. Then you could write: + \code + Depends { name: "Qt.core" } + Depends { name: "Qt.network" } + \endcode + Or, alternatively: + \code + Depends { name: "Qt"; submodules: ["core", "network" } } + \endcode + + \section1 List of Submodules + + \table + \header + \li Submodule Name + \li Qt Module Name + \li Notes + \row + \li axcontainer + \li QAxContainer + \li This module is only available on Windows. + \row + \li axserver + \li QAxServer + \li This module is only available on Windows. + \row + \li concurrent + \li Qt Concurrent + \li + \row + \li core + \li Qt Core + \li All other \c Qt modules have a dependency to this one, so you do not + need to list it in your dependencies if you depend on at least one + other \c Qt module. + + For more information on the properties you can specify, see + \l{core Properties}. + + For more information on the relevant file tags, see + \l {core File Tags}. + \row + \li dbus + \li Qt D-Bus + \li For more information on the properties you can specify, see + \l{dbus Properties}. + + For more information on the relevant file tags, see + \l {dbus File Tags}. + \row + \li declarative + \li Qt Quick 1 + \li Provides the \c{Qt Quick 1} module. For more information, see + \l{declarative Properties}. + \row + \li designer + \li Qt Designer + \li + \row + \li enginio + \li Qt Enginio + \li + \row + \li gui + \li Qt GUI + \li For more information on the properties you can specify, + see \l {gui Properties}. + + For more information on the relevant file tags, see + \l {gui File Tags}. + \row + \li help + \li Qt Help + \li You do not need this module for building \c qdoc documentation, + because that functionality is part of the \c core module. This + module is for using Qt classes such as \c QHelpEngine. + \row + \li multimedia + \li Qt Multimedia + \li + \row + \li multimediawidgets + \li Qt Multimedia Widgets + \li + \row + \li network + \li Qt Network + \li + \row + \li opengl + \li Qt OpenGL + \li + \row + \li phonon + \li Phonon (Qt 4 only) + \li + \row + \li printsupport + \li Qt Print Support + \li + \row + \li quick + \li Qt Quick (2) + \li Provides the \c{Qt Quick} module (Qt Quick 2). For more information, + see \l {quick Properties}. + \row + \li qml + \li Qt QML + \li + \row + \li script + \li Qt Script + \li + \row + \li scxml + \li Qt Scxml + \li For more information on the properties you can specify, see + \l{scxml Properties}. + + For more information on the relevant file tags, see + \l {scxml File Tags}. + \row + \li sql + \li Qt SQL + \li + \row + \li svg + \li Qt SVG + \li + \row + \li testlib + \li Qt Test + \li + \row + \li webkit + \li Qt WebKit + \li + \row + \li webkitwidgets + \li Qt WebKit Widgets + \li + \row + \li widgets + \li Qt Widgets + \li + \row + \li xml + \li Qt XML + \li You do not need this module for the \c QXmlStreamReader and + \c QXmlStreamWriter classes, because those classes are a part of the + \c core module. This module provides the deprecated DOM and SAX + classes. + \row + \li xmlpatterns + \li Qt XML Patterns + \li + \li + \endtable + + \section1 Module Properties + + The following sections describe the properties of Qt modules that can be + interesting to users. + + \section2 core Properties + + Some of the following properties only need to be defined if the respective installation + of Qt was built in some unusual way, for instance by setting non-default \c configure flags. + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li availableBuildVariants + \li \c{stringList} + \li set by \c{qbs-setup-qt} + \li The build variants that this Qt installation offers. + \row + \li binPath + \li \c{path} + \li \c{undefined} + \li The path in which Qt tools such as \c qmake, \c moc and so on are located. + \row + \li config + \li \c{stringList} + \li \c{empty} + \li Corresponds to the default value of qmake's \c CONFIG variable. + \row + \li docPath + \li \c{path} + \li \c{undefined} + \li The path in which the Qt documentation is located. + \row + \li frameworkBuild + \li \c{bool} + \li \c{undefined} + \li Specifies whether Qt was built as a framework. This is only relevant for Darwin systems. + \row + \li incPath + \li \c{path} + \li \c{undefined} + \li The base path of the Qt headers. + \row + \li libInfix + \li \c{string} + \li \c{empty} + \li The library infix can be set at Qt build time to change the name of Qt's libraries. + For instance, if the infix is "Test", then on Unix systems, the \c{Qt Core} library will + be in a file called \c{libQt5CoreTest.so} instead of the default \c{libQt5Core.so}. + \row + \li libPath + \li \c{path} + \li \c{undefined} + \li The path in which the Qt libraries are located. + \row + \li lreleaseMultiplexMode + \li \c{bool} + \li \c{false} + \li If this property is \c true, \c lrelease will merge all ts files into one qm file. + Otherwise, one qm file will be created for every ts file. + \row + \li lreleaseName + \li \c{string} + \li \c{"lrelease"} + \li The base name of the \c lrelease tool. Set this if your system uses a name such as "lrelease-qt4". + \row + \li mkspecPath + \li \c{path} + \li \c{undefined} + \li The path in which the Qt mkspecs are located. + \row + \li mocFlags + \li \c{stringList} + \li empty + \li Additional flags to \c moc. You will rarely need to set this property. + \row + \li mocName + \li \c{string} + \li \c{"moc"} + \li The base name of the \c moc tool. Set this if your system uses a name such as "moc-qt4". + \row + \li namespace + \li \c{string} + \li \c{undefined} + \li The Qt namespace that can be set at build time via the \c configure script. By default, + Qt is not built in a namespace. + \row + \li pluginMetaData + \li \c{stringList} + \li empty + \li Additional plugin meta data. The elements of the list are key-value pairs separated + by the \c = character. A possible use case is to set the plugin URI when building + a static QML plugin: + \code + Qt.core.pluginMetaData: ["uri=thePlugin"] + \endcode + \row + \li pluginPath + \li \c{path} + \li \c{undefined} + \li The path in which the Qt plugins are located. + \row + \li qdocEnvironment + \li \c{stringlist} + \li \c{undefined} + \li The environment for calls to \c qdoc. Typically, you will need to set some variables + here when running \c qdoc to build your project documentation. + \row + \li qdocName + \li \c{string} + \li \c{"qdoc3"} for Qt 4, \c{"qdoc"} otherwise + \li The base name of the \c qdoc tool. + \row + \li qmBaseName + \li \c{string} + \li \c{product.targetName} + \li The base name of the qm file to be built from the ts files in the product. + This property is ignored if \c lreleaseMultiplexMode is \c false. + \row + \li qtBuildVariant + \li \c{string} + \li See below. + \li Specifies the type of Qt libraries to build against: "debug" or "release". + The default value is the build variant of the code linking against Qt. If Qt does not + offer that build variant, the build variant offered by Qt is chosen instead. + \note On some systems, it is not possible to link code built in debug mode against + libraries built in release mode and vice versa. + \row + \li qtConfig + \li \c{stringList} + \li \c{empty} + \li Corresponds to the default value of qmake's \c QT_CONFIG variable. + \row + \li resourceSourceBase + \li \c{path} + \li \c{undefined} + \li For files tagged as \c{qt.core.resource_data}, this property determines which part of + their path will end up in the generated \c qrc file. If this property is set to + \c undefined, only the file name is used. + \row + \li resourcePrefix + \li \c{string} + \li \c{"/"} + \li For files tagged as \c{qt.core.resource_data}, this property determines the prefix + under which they will be available in the generated \c qrc file. + \row + \li resourceFileBaseName + \li \c{string} + \li \c{product.targetName} + \li For files tagged as \c{qt.core.resource_data}, this property determines the base name + of the generated \c qrc file. If this property needs to be changed, it must be set + in the corresponding product rather than in a Group. + \row + \li staticBuild + \li \c{bool} + \li \c{undefined} + \li Specifies whether Qt was built statically. + \row + \li version + \li \c{string} + \li \c{undefined} + \li The Qt version. Consists of three numbers separated by dots, for instance "5.1.1". + \row + \li versionMajor + \li \c{int} + \li \c{versionParts[0]} + \li The Qt major version. + \row + \li versionMinor + \li \c{int} + \li \c{versionParts[1]} + \li The Qt minor version. + \row + \li versionParts + \li \c{list} + \li \c{empty} + \li The Qt version as a list. For instance, Qt version 5.1.1 would correspond to a value of + \c[5, 1, 1]. + \row + \li versionPatch + \li \c{int} + \li \c{versionParts[2]} + \li The Qt patch level. + \endtable + + \section2 dbus Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li xml2cppName + \li \c{string} + \li \c{"qdbusxml2cpp"} + \li The base name of the \c qdbusxml2cpp tool. Set this if your system uses a different name. + \row + \li xml2CppHeaderFlags + \li \c{stringList} + \li empty list + \li Additional flags when running the \c qdbusxml2cpp tool to create header files. + \row + \li xml2CppSourceFlags + \li \c{stringList} + \li empty list + \li Additional flags when running the \c qdbusxml2cpp tool to create source files. + \endtable + + \section2 declarative Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li qmlDebugging + \li \c{bool} + \li \c{false} + \li Specifies whether QML debugging support should be compiled into your binaries. + \row + \li qmlImportsPath + \li \c{string} + \li set by \c{qbs-setup-qt} + \li The absolute path to the directory where Qt's QML imports are installed. + \row + \li qmlPath + \li \c{string} + \li set by \c{qbs-setup-qt} + \li The absolute path to the directory where Qt's QML files are installed. + This property is undefined for Qt4. + \endtable + + \section2 gui Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li uicName + \li \c{string} + \li \c{"uic"} + \li The base name of the \c uic tool. Set this if your system uses a name such as "uic-qt4". + \endtable + + \section2 quick Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li qmlDebugging + \li \c{bool} + \li \c{false} + \li Specifies whether QML debugging support should be compiled into your binaries. + \row + \li qmlImportsPath + \li \c{string} + \li set by \c{qbs-setup-qt} + \li The absolute path to the directory where Qt's QML imports are installed. + \row + \li qmlPath + \li \c{string} + \li set by \c{qbs-setup-qt} + \li The absolute path to the directory where Qt's QML files are installed. + This property is undefined for Qt4. + + \endtable + + \section2 scxml Properties + + \table + \header + \li Property + \li Type + \li Default + \li Description + \row + \li className + \li \c{string} + \li \c undefined + \li The class name of the generated state machine. By default, the compiler will use the + \c name attribute of the input file's \c{} tag. + \row + \li namespace + \li \c{string} + \li \c undefined + \li The C++ namespace in which to put the generated class. By default, the compiler will + place the class in the global namespace. + \row + \li qscxmlcName + \li \c{string} + \li \c{"qscxmlc"} + \li The base name of the Qt SCXML compiler. Set this if your system uses a different name. + \endtable + + \section1 Relevant File Tags + + The following sections describe the file tags that are relevant for the Qt + modules. + + \section2 core File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"qch"} + \li n/a + \li 1.1 + \li This tag is attached to the output artifacts of the rule that runs the + \c qhelpgenerator tool. + \row + \li \c{"qdoc"} + \li \c{*.qdoc} + \li 1.1 + \li Source files with this tag trigger a re-execution of the rule running the \c qdoc + tool when their timestamp changes. + \row + \li \c{"qdocconf"} + \li \c{*.qdocconf} + \li 1.1 + \li Source files with this tag trigger a re-execution of the rule running the \c qdoc + tool when their timestamp changes. + \row + \li \c{"qdocconf-main"} + \li - + \li 1.1 + \li Source files with this tag serve as inputs to the rule running the \c qdoc tool. + \row + \li \c{"qdoc-output"} + \li n/a + \li 1.5 + \li Use this tag to match all \c qdoc outputs, for instance in a \l{Group Item}{Group} + using the \c fileTagsFilter property. + \row + \li \c{"qhp"} + \li \c{*.qhp} + \li 1.1 + \li Files with this tag serve as inputs to the rule running the \c qhelpgenerator tool. + Such files are created by \c qdoc, but can also appear as source files. + \row + \li \c{"qm"} + \li n/a + \li 1.1 + \li This tag is attached to the output artifacts of the rule that runs the \c lrelease tool. + \row + \li \c{"qrc"} + \li \c{*.qrc} + \li 1.0 + \li Files with this tag serve as inputs to the rule running the \c rcc tool. + \row + \li \c{"qt_plugin_metadata"} + \li - + \li 1.0 + \li Source files with this tag trigger a re-execution of the rule running the \c moc + tool when their timestamp changes. + \row + \li \c{"qt.core.resource_data"} + \li - + \li 1.7 + \li Source files with this tag serve as inputs to the rule creating \c qrc files. + \row + \li \c{"ts"} + \li \c{*.ts} + \li 1.0 + \li Files with this tag serve as inputs to the rule running the \c lrelease tool. + \endtable + + \section2 dbus File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"qt.dbus.adaptor"} + \li - + \li 1.5 + \li Source files with this tag serve as inputs to the rule running the \c qdbusxml2cpp tool, + which will create an adaptor class. + \row + \li \c{"qt.dbus.interface"} + \li - + \li 1.5 + \li Source files with this tag serve as inputs to the rule running the \c qdbusxml2cpp tool, + which will create an interface class. + \endtable + + \section2 gui File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"ui"} + \li \c{*.ui} + \li 1.0 + \li Source files with this tag serve as inputs to the rule running the \c uic tool. + \endtable + + \section2 scxml File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"qt.scxml.compilable"} + \li - + \li 1.7 + \li Source files with this tag serve as inputs to the rule running the Qt SCXML compiler, + which will create a C++ class representing a state machine. + \endtable + +*/ diff --git a/doc/reference/modules/typescript-module.qdoc b/doc/reference/modules/typescript-module.qdoc new file mode 100644 index 00000000..edac2519 --- /dev/null +++ b/doc/reference/modules/typescript-module.qdoc @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page typescript-module.html + \ingroup list-of-modules + + \title Module typescript + \since 1.3 + \brief Provides TypeScript support. + + The \c typescript module contains properties and rules for building + \l{http://www.typescriptlang.org}{TypeScript} applications and may be used in combination with + the \l {Module nodejs} {nodejs} module to run TypeScript applications directly from \QBS. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li warningLevel + \li \c{string} + \li 1.3 + \li \c{"normal"} + \li Severity of warnings to emit. The higher the level, the more warnings will be shown. + \c{pedantic} causes the TypeScript to emit warnings on expressions and declarations with + an implied 'any' type. + \row + \li targetVersion + \li \c{string} + \li 1.3 + \li \c{undefined} + \li ECMAScript target version for generated JavaScript code. \c{undefined} uses the + TypeScript + \l{https://github.com/Microsoft/TypeScript/wiki/Compiler-Options}{compiler default}. + \row + \li moduleLoader + \li \c{string} + \li 1.3 + \li \c{undefined} + \li If TypeScript modules are being used, the JavaScript module loading mechanism to use in + the generated JavaScript code. \c{undefined} indicates modules are not being used. + See \l{https://github.com/Microsoft/TypeScript/wiki/Compiler-Options}{Compiler Options} + for a list of possible values. + \row + \li stripComments + \li \c{bool} + \li 1.3 + \li \c{!qbs.debugInformation} + \li Whether to remove comments from the generated JavaScript files. + \row + \li generateDeclarations + \li \c{bool} + \li 1.3 + \li \c{false} + \li Whether to generate corresponding .d.ts files during compilation; these are TypeScript's + equivalent of header files. + \row + \li generateSourceMaps + \li \c{bool} + \li 1.3 + \li \c{qbs.debugInformation} + \li Whether to generate corresponding .map files during compilation. + \row + \li compilerFlags + \li \c{stringList} + \li 1.3 + \li \c{undefined} + \li Additional flags for the TypeScript compiler. + \row + \li singleFile + \li \c{bool} + \li 1.3 + \li \c{false} + \li Whether to compile all TypeScript source files to a single JavaScript output file. The + default is to compile each TypeScript file to a corresponding JavaScript file. This + property is incompatible with \c{moduleLoader}. + \row + \li version + \li \c{string} + \li 1.3 + \li \c{undefined} + \li The TypeScript version. Consists of four numbers separated by dots, for instance + "1.0.0.0". + \row + \li versionMajor + \li \c{int} + \li 1.3 + \li \c{versionParts[0]} + \li The TypeScript major version. + \row + \li versionMinor + \li \c{int} + \li 1.3 + \li \c{versionParts[1]} + \li The TypeScript minor version. + \row + \li versionParts + \li \c{list} + \li 1.3 + \li \c{empty} + \li The TypeScript version as a list. For instance, TypeScript version 1.0 would correspond + to a value of \c[1, 0, 0, 0]. + \row + \li versionPatch + \li \c{int} + \li 1.3 + \li \c{versionParts[2]} + \li The TypeScript patch level. + \row + \li versionBuild + \li \c{int} + \li 1.3 + \li \c{versionParts[3]} + \li The fourth TypeScript version number component. + \row + \li toolchainInstallPath + \li \c{path} + \li 1.3 + \li \c{undefined} + \li TypeScript installation directory. This should not normally need to be changed provided + that \c{tsc} is already available by searching the PATH environment variable. + \row + \li compilerName + \li \c{string} + \li 1.3 + \li \c{"tsc"} + \li Name of the compiler binary. This should not normally need to be changed. + \row + \li compilerPath + \li \c{string} + \li 1.3 + \li \c{compilerName} + \li Directory where the compiler binary is located. + This should not normally need to be changed. + \endtable +*/ diff --git a/doc/reference/modules/wix-module.qdoc b/doc/reference/modules/wix-module.qdoc new file mode 100644 index 00000000..2b01d26e --- /dev/null +++ b/doc/reference/modules/wix-module.qdoc @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \contentspage index.html + \page wix-module.html + \ingroup list-of-modules + + \title Module wix + \since 1.2 + \brief Provides Windows Installer XML Toolset support. + + The \c wix module contains properties and rules for building MSI and + EXE setup packages with the \l{http://wixtoolset.org}{Windows Installer XML Toolset}. + + This module is available on all platforms. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li debugInformation + \li \c{bool} + \li 1.2 + \li \c{qbs.debugInformation} + \li Generate debug information. See \c{qbs.debugInformation}. + \row + \li defines + \li \c{stringList} + \li 1.2 + \li \c{undefined} + \li List of preprocessor macros that get passed to the compiler. + To set macro values use the following syntax: + \c{wix.defines: ["USE_COLORS=1", 'COLOR_STR="blanched almond"']} + \row + \li enableQbsDefines + \li \c{bool} + \li 1.2 + \li \c{true} + \li Whether to define preprocessor macros corresponding to values from the project and + product objects. When building a 64-bit package, the preprocessor variable \c{Win64} + will also be defined. + \row + \li visualStudioCompatibility + \li \c{bool} + \li 1.2 + \li \c{true} + \li Passes most of the same preprocessor macros to the compiler as Visual Studio does. + This allows easier authoring WiX files that are compatible with both \QBS and MSBuild. + \row + \li includePaths + \li \c{pathList} + \li 1.2 + \li \c{undefined} + \li List of include paths. Relative paths are considered to be relative to the .qbs product + file they are used in. + \row + \li treatWarningsAsErrors + \li \c{bool} + \li 1.2 + \li \c{false} + \li Warnings will be handled as errors and cause the build to fail. + \row + \li warningLevel + \li \c{string} + \li 1.2 + \li \c{"normal"} + \li Severity of warnings to emit. The higher the level, the more warnings will be shown. + Possible values include: \c{"none"}, \c{"normal"}, \c{"pedantic"} + \row + \li verboseOutput + \li \c{bool} + \li 1.2 + \li \c{false} + \li Whether to display verbose output from the compiler and linker. + \row + \li compilerFlags + \li \c{stringList} + \li 1.2 + \li \c{undefined} + \li Additional flags for the Candle compiler. + \row + \li linkerFlags + \li \c{stringList} + \li 1.2 + \li \c{undefined} + \li Additional flags for the Light linker. + \row + \li cultures + \li \c{stringList} + \li 1.2 + \li \c{undefined} + \li List of localizations to include in the MSI. + Use \c{undefined} to include all localizations. + \row + \li extensions + \li \c{stringList} + \li 1.2 + \li \c{["WixBalExtension"]} if the product type is an EXE setup application, + otherwise \c{undefined}. + \li List of extension assemblies to link into the output. + Possible values include: + \c{"WixBalExtension"}, + \c{"WixComPlusExtension"}, + \c{"WixDependencyExtension"}, + \c{"WixDifxAppExtension"}, + \c{"WixDirectXExtension"}, + \c{"WixFirewallExtension"}, + \c{"WixGamingExtension"}, + \c{"WixIisExtension"}, + \c{"WixMsmqExtension"}, + \c{"WixNetFxExtension"}, + \c{"WixPSExtension"}, + \c{"WixSqlExtension"}, + \c{"WixTagExtension"}, + \c{"WixUIExtension"}, + \c{"WixUtilExtension"}, + \c{"WixVSExtension"}, + custom assemblies + \row + \li version + \li \c{string} + \li 1.2 + \li \c{undefined} + \li The WiX version. Consists of four numbers separated by dots, for instance "3.7.1224.0". + \row + \li versionMajor + \li \c{int} + \li 1.2 + \li \c{versionParts[0]} + \li The WiX major version. + \row + \li versionMinor + \li \c{int} + \li 1.2 + \li \c{versionParts[1]} + \li The WiX minor version. + \row + \li versionParts + \li \c{list} + \li 1.2 + \li \c{empty} + \li The WiX version as a list. For instance, WiX version 3.7.1224.0 would correspond to a + value of \c[3, 7, 1224, 0]. + \row + \li versionPatch + \li \c{int} + \li 1.2 + \li \c{versionParts[2]} + \li The WiX patch level. + \row + \li versionBuild + \li \c{int} + \li 1.2 + \li \c{versionParts[3]} + \li The fourth WiX version number component. + \row + \li toolchainInstallPath + \li \c{path} + \li 1.2 + \li determined automatically + \li WiX installation directory. Determined by searching the registry for the latest version. + This should not normally need to be changed. + \row + \li toolchainInstallRoot + \li \c{path} + \li 1.2 + \li determined automatically + \li WiX binaries directory. Determined by searching the registry for the latest version. + This should not normally need to be changed. + \row + \li compilerName + \li \c{string} + \li 1.2 + \li \c{"candle.exe"} + \li Name of the compiler binary. + This should not normally need to be changed. + \row + \li compilerPath + \li \c{string} + \li 1.2 + \li \c{compilerName} + \li Directory where the compiler binary is located. + This should not normally need to be changed. + \row + \li linkerName + \li \c{string} + \li 1.2 + \li \c{"light.exe"} + \li Name of the linker binary. + This should not normally need to be changed. + \row + \li linkerPath + \li \c{string} + \li 1.2 + \li \c{linkerName} + \li Directory where the linker binary is located. + This should not normally need to be changed. + \endtable +*/ diff --git a/doc/reference/modules/xcode-module.qdoc b/doc/reference/modules/xcode-module.qdoc new file mode 100644 index 00000000..a945504b --- /dev/null +++ b/doc/reference/modules/xcode-module.qdoc @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage index.html + \page xcode-module.html + \ingroup list-of-modules + + \title Module xcode + \since 1.5 + \brief Provides Xcode support. + + The \c xcode module contains properties and rules for Xcode-based development. + This module provides the foundation for several other modules on Apple platforms, including the + \c cpp and \c ib modules. + + \section1 General Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li developerPath + \li path + \li 1.5 + \li \c{"/Applications/Xcode.app/Contents/Developer"} + \li Developer directory of the Xcode installation. By default this is set to the developer + directory of the Xcode installation at its default location in /Applications. + Corresponds to the \c DEVELOPER_DIR environment variable. + \row + \li sdk + \li string + \li 1.5 + \li determined by \c{qbs.targetOS} + \li Version of the Xcode SDK used to build products. This can be specified as a full + canonical SDK name (i.e. \c{"macosx10.10"}), a platform version number (i.e. + \c{"10.10"}), or a platform identifier (i.e. \c{"macosx"}) in which case the latest SDK + available for that platform will be used. The default is the latest SDK available in the + Xcode installation for the current platform. + \row + \li targetDevices + \li stringList + \li 1.5 + \li determined by \c{qbs.targetOS} + \li List of the Apple devices targeted by this product. For macOS, watchOS, and tvOS, this + should always be "mac", "watch", and "tv", respectively. For iOS, this can be one or + both of "iphone" and "ipad". The default is the list of all device types supported by + the current platform. + \endtable + + \section1 Read-only Properties + + \table + \header + \li Property + \li Type + \li Since + \li Default + \li Description + \row + \li sdkName + \li string + \li 1.5 + \li determined by \c{xcode.sdk} + \li Canonical name of the SDK used to build products. For example, macosx10.9. + \row + \li sdkVersion + \li string + \li 1.5 + \li determined by \c{xcode.sdk} + \li Version number of the SDK used to build products. For example, 10.9. + \row + \li latestSdkName + \li string + \li 1.5 + \li determined by \c{xcode.developerPath} + \li Canonical name of the latest SDK available in the Xcode installation. + For example, macosx10.10. + \row + \li latestSdkVersion + \li string + \li 1.5 + \li determined by \c{xcode.developerPath} + \li Version number of the latest SDK available in the Xcode installation. + For example, 10.10. + \row + \li availableSdkNames + \li stringList + \li 1.5 + \li determined by \c{xcode.developerPath} + \li Canonical names of all SDKs available in the Xcode installation for the current + platform. For example, [macosx10.9, macosx10.10]. + \row + \li availableSdkVersions + \li stringList + \li 1.5 + \li determined by \c{xcode.developerPath} + \li Version numbers of all SDK available in the Xcode installation for the current + platform. For example, [10.9, 10.10]. + \row + \li platformPath + \li path + \li 1.5 + \li determined by \c{xcode.developerPath} + \li Path of the platform directory containing \c{xcode.sdkPath}. + \row + \li sdkPath + \li path + \li 1.5 + \li determined by \c{xcode.developerPath} and \c{xcode.sdk} + \li Path of the SDK used to build products. Equivalent to \c{cpp.sysroot}. + \endtable +*/ diff --git a/doc/reference/reference.qdoc b/doc/reference/reference.qdoc new file mode 100644 index 00000000..d34f39e0 --- /dev/null +++ b/doc/reference/reference.qdoc @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \contentspage index.html + \previouspage shell.html + \page reference.html + + \title Reference + + \list + \li \l{List of All Items} + \list + \li \l{List of Language Items} + \li \l{List of Convenience Items} + \endlist + \li \l{List of Built-in Services} + \li \l{List of Command-line Tools} + \li \l{List of Modules} + \li \l{Command and JavaScriptCommand} + \endlist +*/ + +/*! + \contentspage reference.html + \group list-of-builtin-services + \title List of Built-in Services + \QBS provides the following built-in JavaScript extensions to simplify operations that + are expected to be needed often in project files. + + To gain access to the operations provided by a particular Service - for example, + the File service - use the following statement at the top of your \QBS project file: + \code + import qbs.File + \endcode + + If you instead need to access the service from a JavaScript file, import it using the following + statement at the top of your JavaScript file: + \code + var File = loadExtension("qbs.File"); + \endcode +*/ + +/*! + \contentspage reference.html + \group list-of-modules + \title List of Modules + These are the modules shipped with \QBS. +*/ + +/*! + \contentspage reference.html + \group list-of-items + \title List of All Items + + \QBS provides the following \l{List of Language Items}{built-in} and + \l{List of Convenience Items}{convenience} items to define projects. +*/ + +/*! + \contentspage reference.html + \group list-of-language-items + \title List of Language Items + + \QBS provides the following built-in QML items to define projects. + These are the primitives upon which all other \QBS items are built. +*/ + +/*! + \contentspage reference.html + \group list-of-convenience-items + \title List of Convenience Items + + \QBS provides the following QML items for convenience. +*/ diff --git a/doc/templates/images/arrow.png b/doc/templates/images/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..f2a83a5740683d31d9aab1e7da51c875d8b122fe GIT binary patch literal 1071 zcmaJ=O-K|`93K_LL__OPR_1FADsbmx-Bo9D)pd8x6-SnJE1`?CGtb>YXWlgPbjLl! zni2Ft2a)I$NYJUe1VKfF<|R9H5i-4mbjm|Yhc1G>QFraoHt^=X_vion{l9+iPHf;( zb>)dlilVBcz2P|7o5(s|QBM9-sNoIS>TqNT526vAku69CRg{Dvs>>-Dhq5|({UdCr zsIs$KVh9gO{i1?&Ms|G|M>h$YqS`whQ&vVH21%IGi~#-f?Ry$%YJk4#mpI7`!L-(! zwcucOAfaSO6+xvtJ3zZ55&|7!894fwVT(?H-sKg^*)_8?*mc390eUZ~At?qz$b!Jn za2|zg;X#X!;eDLo6PkdR<2jc1vRtc&=R|+2$njwRqe(PN9TDT>!GV#tCr zMpgo$v3wU}X@rrTMkWXi`WYTbvZ5KTWfvhyV$`s)Y$z}q4$y>y(KJ;Q{5GvyNE&!%{{pIx6 z`LS5GvF2FG*1P$~w}qTjTXXK{#>&)$d9fx{wsmdB`jIZX;YL3_zPV{}UAYnpHPjvX Sb;h{sKH+F&ApD|x_~sv>GF^E9 literal 0 HcmV?d00001 diff --git a/doc/templates/images/arrow_down.png b/doc/templates/images/arrow_down.png new file mode 100644 index 0000000000000000000000000000000000000000..9d01e97f6a959434811ae2bc02107903f47734f8 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^AT}!p6OjDO_R1beNtU=qlmzFem6RtIr7}3C^|*d5Da34?}~mLp#zjc1zY-~03b|Nqa= z&n2Gn^z<}jdfMmdKI;Vst0PiS3E&u=k literal 0 HcmV?d00001 diff --git a/doc/templates/images/bg_l.png b/doc/templates/images/bg_l.png new file mode 100644 index 0000000000000000000000000000000000000000..90b1da10b9b9f85eff333ba71fcc66803ce9dca2 GIT binary patch literal 100 zcmeAS@N?(olHy`uVBq!ia0vp^*MXRkg9%6;s#tRYNNIbzIEHAPPpa?;-52Q%BN iSFd;$Cx~1SXJCk7;`_h6G^horjKR~@&t;ucLK6UW4;V85 literal 0 HcmV?d00001 diff --git a/doc/templates/images/bg_ll_blank.png b/doc/templates/images/bg_ll_blank.png new file mode 100644 index 0000000000000000000000000000000000000000..95a1c45e04b99502491f0775c4a21100cbd1bdb9 GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0vp^H-MO*g9%7-R!-9eQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JiXM5oIEGZ*dNb3Iui1dd)jKGRP4D2}_svI`w3#~?15Hbl zHJujZP2Ic4yoJT=^-F8Ts}_Cl&uCvRy~})H&Vqg|XUhY@M;0yc3GxzQy(6@-al_5; ztM7mRl40<;GVg3x$BO(V?&iLix8=rO|C7qXE%;t9%OUUfwsqO30(3SAe0;PhPb_u0 z#^R`s?gW*j!oc+rEE_k}IJs;P{`m3nF@Loj;e#PZs!if{nH{+Qlf!^vVSV&|a}Du@ QK;JTWy85}Sb4q9e0RC2b_V7j}x7U-g`a@x=q9~7e$1Rf}U^0(*bDdBftQcKie1#hiK*i0000|6H_V+Po~-c6`6awIEGZ*N~-zwFVdQ&MBb@0Mi>Xu>b%7 literal 0 HcmV?d00001 diff --git a/doc/templates/images/blu_dot.png b/doc/templates/images/blu_dot.png new file mode 100644 index 0000000000000000000000000000000000000000..c332148f74538db9b3438dc33655dffbba42d5d0 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^>>$j+1|*LJg}1r8F?f9?xS{U_HdY?u~vegHUplmkGnl-wu1^ zU6_Bqc6`(maPmLTi~|!mH69*lkmY!Aoio+PVe$=zk}IrkHS1V83>frPCBv^OZP5ps O%HZkh=d#Wzp$P!M1~s7o literal 0 HcmV?d00001 diff --git a/doc/templates/images/box_bg.png b/doc/templates/images/box_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..3322f923f81d060c50ca8e7a50182837c16f7bd9 GIT binary patch literal 89 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrB!2~3Gi;~lTl!B*=V+hA})`RSv3=BNX8v_4( nybP0l+XkKL0T77 literal 0 HcmV?d00001 diff --git a/doc/templates/images/breadcrumb.png b/doc/templates/images/breadcrumb.png new file mode 100644 index 0000000000000000000000000000000000000000..0ded5514d236b9ccf458c0cc3f0afe7f89a1f8f1 GIT binary patch literal 134 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1p!2~4zj|%JrQof!pjv*Y^b9)TA4jAw-fByen zBzR9{S2%y9uaBKz>Z-|3%jKd}6h%WtCw4?QtYY|~w5P78Q2tKs?PZhIB-&6?W5Bj%g?5?lYw&AQV1Pp!)7WW^sD6*?`Lp8??HCO9>Lv(+;Gn7-ko{JG;VWlhh-14{f!t=fnMd%x z@*XvFBp6f^CabxUh+Mu54<9c%E6zO_gr3t8R~|XY6`hkj2)0^s{Gv0WqN>hZH4UE_ zwc1xnR=8`s@aTR5MiadbdG^T?Y!!E0dE_9M$ZFeyDEW1~&IbDXVofq`e`6O3`bTdsK6wbpUG~(Pfl(%QIf$Q2(P4HPh1uEBcVw}wbu-z)MZ;1 z{M(^0pUQeZB!M5lqPG7=j_WCp-Cgc94Z5aRv`%K!JjuB4Kqy=kW&gSK{EedLXBxbf zdd1K8I=`j}Gnu1M5+&0oDH?Z|KQBz(Ir29)tbgQrM@KZCK)<|Q`0{;aw(M2+Uw{Ds X`&Pa300000NkvXXu0mjfc&sgx literal 0 HcmV?d00001 diff --git a/doc/templates/images/btn_prev.png b/doc/templates/images/btn_prev.png new file mode 100644 index 0000000000000000000000000000000000000000..10a620c5189b6d861fec6c84275be3d495baf431 GIT binary patch literal 676 zcmV;V0$crwP)vT*T7TY#0DaIE^Ln!c9wyasG%gox0!GxbweVn^6- z=>(k(YRyyYPm%~eNFoW~?8!lRcz*)Yscyc<9Oqwk4|0$z;pqhEanGeZq0`2O55ev0 zIjEZsh#P`vn2Q|bO7)taS43MQLi%)=kP!M$4Z*x+UHMZ>nS4@8yWXmVO&+<-ag} zcae*XW)HxvtJy#tfgI$Lh}tzL$x#!pvlCywS9tj?k)gprlF literal 0 HcmV?d00001 diff --git a/doc/templates/images/bullet_dn.png b/doc/templates/images/bullet_dn.png new file mode 100644 index 0000000000000000000000000000000000000000..f7762472e2379b57e203b684bb20b95af9d97229 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)H!3HEvS)PI@$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1G9iA?ZAr-fh{`~)M&&;tyZ!&8je`5rHhyA<0sxt?UG_XII zzQ#`AyZmexk2x>TexJ^xn9cj|`t5T}+N(Cq`Ek(a)JJ=dIfd{4e*OR3Z<^5tqs@i~ zWYo_opB7j*^W=diO9T$QSSNGt!~6gCZR_{j8~;iF!9I$ Z47Sfb%jCc7{s9`t;OXk;vd$@?2>|gRE`I<3 literal 0 HcmV?d00001 diff --git a/doc/templates/images/bullet_sq.png b/doc/templates/images/bullet_sq.png new file mode 100644 index 0000000000000000000000000000000000000000..a84845e3c7e7a9dde3388ef851bd1bac74a6c40f GIT binary patch literal 74 zcmeAS@N?(olHy`uVBq!ia0vp^Y#_|R1SIp8bg=d#Wzp$P!_Y7p}P literal 0 HcmV?d00001 diff --git a/doc/templates/images/bullet_up.png b/doc/templates/images/bullet_up.png new file mode 100644 index 0000000000000000000000000000000000000000..7de2f06954bb18c44791abe8e7b3e15c8ce666de GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)H!3-psZi$)zDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MaBU>A+F1>PFZ<%`tmE2PdwfG@WZX;SEekzJn`wf6h(S=m_2Bx8 zZL1#L{l7&?=cyX&VtxU;-)By|UHvOoEO&Ye*R?i*@_YRZ%nSjcOoz0~zP17lVDNPH Kb6Mw<&;$TDNm;=F literal 0 HcmV?d00001 diff --git a/doc/templates/images/feedbackground.png b/doc/templates/images/feedbackground.png new file mode 100644 index 0000000000000000000000000000000000000000..3a38d995d7fa681a7bd2bbc4f5130abb35c8b1b4 GIT binary patch literal 263 zcmeAS@N?(olHy`uVBq!ia0vp^DIm7UKT w-N3;iz{sei;K0JtL02||Ak^L`Rmuk#9^Ph>alAI?I?&M!p00i_>zopr0OBh=_W%F@ literal 0 HcmV?d00001 diff --git a/doc/templates/images/header.png b/doc/templates/images/header.png new file mode 100644 index 0000000000000000000000000000000000000000..3c68d9c5258ee351f35ddc3c0d1317acf6eef1fe GIT binary patch literal 3768 zcmV;p4oC5cP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igS# z5H%j*Jn7N^01h}wL_t(|+U=Zqd{ouh$G@4GOfs3QKo+u)ge74K1Q7y65Lv7RMFCq2 z2(&=mTB+E6WV5xrRFU%b;{vUyfVS2~g%^oXKo&!Q6rzBE010GY2qBYwvP?2drhgD_ zW|Em?)&Rcq`|G)9yU%m(?>_gO=bUrh^m@G>0~FL|lbrK07c0)7Xf8vq*AwW~pZMU3 z%!ygb;68CU3wbq{`SRLMG8O4mHQ&TjA|ozv9CKrrbASH_altk`oD^X)-9zk2i1z^E zgC_EL%n~B~VqBf&0synWh@nNN!c*eK{->_pb^5s(IAF$)XM%wgkCJT$)L@bYA z!&Adw=#tl@R`T>$6KT>YIl8daYDIQszt4`0jp)&v%(tb@-h{pO-wDUn`PnWVwTk&8 ze(ic|f8czwr0ty-Hj#YoBjyeLB}?O1-F5vVxhb-- zHJA7zYujB|%HJ2k7)#c3#|sx73fvhAeVg%9}z&SR>ijOHwcbn^YqV|95EBQyAN`AAEpd=j6Y9H!d)Z<;QjA6pw_hr zji@wD?76zbCI!^G7T!C*Ubz1K1Ml(V&}Zl)3qmUPU_{_pHcZ)hSCh;h`|sEnOXNRH&!V{7`vtm8UYenLn{=s(qL;qhV9Ib84s0B6fnyG$2tL3-J- zju$BM39ITB*XNK@w3mbVpLNNrYpLed)FtH9WE!Pcscw=|oJ?xTe%2;#;i1UKjq;y8 z_@l00DQy$^by?)sWpT9d|9I!&WP;?OR@++DT+XYhKc}EB+bCVxT1%!PolHeK`*T0# zjfeh0h zq$^v=syc6^XG(E07b;G(CUFZJ(w?JHT`%O+6&~=Wa1_9X2rNS7#}tb53wib%1^NQ%x`JZDEZ&?-!U?1 zJbh)s9X9$8WoqOTj0u^<0N){qMH2FAF7xi$wG`B4Q`ub3&WoE^o4Ca)`*m$v)}MM- zc-Ye-=dgI>%LL2AxzSj}M;AA9`ubr?l)1ciav6VnWqirc53N4Tgs1Hht@E13~J4VEMz&7@uJTRMUaM@j7s2u{L9SLQ9HMth zak4=0;r;IC50l;}(r*avVkuGnvAp`g-yH6^6wNnS7`K8)2QNe>@gmTxKQG+h5ku9Z z=jphY`0=2h3Hi^D`J<43N%3u`P=@VXGSq2N+)Ck|!SZmP8}piwf4TBIn{0I!S9^oU z_V$Sgpjp!(BzTHtW(#{tyac()ppHFgGP<{y)%8~| za;sM55WP>8bjVK*ea2zB_80?=e$x2ab=sdcJ!-Dmd}V8`QJ$l?)H$p&$RH-x`JVQ| zUas~QkL~Fb5rCTwgmS~pL0!?YZ%e1wT3vfo@Wd`@x4#GgA>ISJOO*u{-N)XS zF1O~gAP?(%pUEw$BrOx;Cl*r89(_T391K~&>+tVi!0GY%~82Pm5RLCTzM?NjuW%S)f(*P3cnNO6-!Zil5kAAWc$9x^-VS?i-Yb+u_<_LmY{sl9f z7S*aMS)HloVaKD^!iaC39B=;(|& z_<8h2(Od@HTG$BliSAP7_Fg&wf6sosO33W0?*%!gPrO6)_VWl37GJ7W74$bT)v^@2 zHtij8Tefn^*>EOT?*$%<0PYK(BqW^cW)J=ZKh-e4HR>l~BG zt+`}zJErWP6yBlJ-fJIq8A0gvdcMl}yKp@va(1u11PAky$ga)+V1Vynqd#%jqjzGs z@pI^D)MU8JJI`QmAsDaj^(JiPk|SY?PtN3Oceq=4EV@mYg(Fr79rDUI^Z8Hd*EDMy zY1TB7R(gn4Df5M|;G(#f@bw5VYRJQ22%TPb3}tIAZ-3kKsdA~Dx_QuP6JbM9yY_@2%-|e(e?h=hSnQ)aRkmskyE! z;LX!3gyr-heMT}lVwO{123?z$>&im5XKdu%vuoSq5fWA=Y%*GvvPbV3QFDc<#I3r| zQe2;lwoOB+GM~+7UN+LnFifyL<8>5GH&AJtIh6kyNmsTyEUwxcT(TEE@yVH7?GASf zkA>gbwCUT}bow_&`!r19vB8UYDdEj7>FbU!HkwM_hNZjccV%s3$Avd7`mK#6yNZkJ zb69tLNsm(u!}@1$r1JX{KQ}ql9vl$IviQ}kOI>X8zLwZus?2BY@kLZLT{o-G0N=rE zocd3KJ8j0b^<+Zly5=Bv_hHq?z+CF4=p}p7`W!u6C!JGkI(&&xK_kk(-FMiCc*ePUOq0 z+bL1z8F}cqzy#*TE@OPybdwFOOxVOb->ngL9>)bG@T9>;w4U(P@E3>=PGoQUj|8h# z3cTFC85J~vIWbGE_Q;WbF>HVI7zgtH#p&xuD6Y?;Nz;Ib*po1yXySt>cG&D+szb||VlxOgC L^>bP0l+XkKZf78& literal 0 HcmV?d00001 diff --git a/doc/templates/images/home.png b/doc/templates/images/home.png new file mode 100644 index 0000000000000000000000000000000000000000..8de6d0c81f64dde8554f376925b458cae8585949 GIT binary patch literal 1076 zcma)5%TLrm9Im`VAVdx(g7GjGPsD9!_5p0ef=gRu6N;PNHLM9Hly=yy&`xa!cY%XM z;o#q3OgxzANn=6`K2Y=|i4qb`ym2tz_2v<$%kJXAHBQpb%&*_~{hqy>p1Rc4ai)Xg zxUSTMl41L);Av@Q?^}0Gx#{(zY6=gWfzMX?2{$WV2zva3tYL9>|vb}FGt8?t%~U) zp~No|7s%O|fPkhOmJ?Vi3{8_#j!$*Rz$qocGY!GAObN*-IwZpgibjSIQqfpMj*LMy zIUvKRswQJKu0o6@8#}bdHIKQ`hTLEnY?pafuxH)G=D0^}P)S;{8pnbf>Q%XB<5vS?*G=VKRejYuC~74e6jjV zSl`_`z4!fPXJM}uJ-w4(EWO(~@nLfVo{T_ve*=KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0000YNklF6XfJT6kT#eIDr5OOJ zcp!EFVgn?p3qX7Th!&ALD}i-8sj0IB(Z{`?Dg_2Lu5yEk7MzJB@10OT{UurM)j z^RP1riEuNBN$`RA8-QvPfe!dUk;PD7?gv^XaQntfuw}Ra$Pua<;vh$WEOkWpJtGO0 zDgdeTw{N@>0QnRd$crg67^~Va>^QTQ;oHwIC~_d5t7?dWEH?yN{s1Pj)(?-9h&{-jzQ@b1kQoRT*lUj);SUfsc|2C6@RXp4ct;{XgM zg1#jLK>9)EfXpM>$3K4jCd%;FpI#De4huDkze87cGRTQ4Graluf@nR&`1k?804H7r zAPXmKs3T|))SL%ISq#ne4?smBE)e6C#jv<^FR&1?#+}t5`X9gw5n@yI1}PZ8 z{sIestOX0mm!LS6RS<&tw1Fxu5NN$z4=MsdaSrnRU!Vh+p$!3Ow%Gu+^a53zK1i(* jtW6(mt&Wim00ImEt2*(XY?G#g00000NkvXXu0mjfG7b=W literal 0 HcmV?d00001 diff --git a/doc/templates/images/ico_note_attention.png b/doc/templates/images/ico_note_attention.png new file mode 100644 index 0000000000000000000000000000000000000000..b85b696b10eb2275f9e52eebc1227890d9016d0d GIT binary patch literal 529 zcmV+s0`C2ZP)F6XfC%v9w5I?NE0Cgx ziAr|w2jcrc%MIveG0?Jv|G$43{C)cj#O#ASpm%~hWge4WEm41KLdxjHCX)5tH&TJ zpHM1iqK0opEhaHYI^{;UBrj9xc`x=>wZHG> zDF$J;i~CafuP_w9UaNe8?|QRmQ-BD=PKSGp;_EcF)Cs=x*6HooBF`lfk>|&D;Y>ZF zscXfwQ?JEN)d~r&*zWN7o1{ad#-<+!Zz;Q&*q&DEteN?2l8f?lhod%84pt$Rzu9+B zIkxFxAg{&};|FYaZhrFlxBACp?~DgbH!l7NxpUpf!|-5aWb^f%dKRJ1iwg|v3@6IX z_z+jQ_{}oi>0vt$1)YnVEamqyTA{+1yYphZiv2t$9tMFOZAmgq5LHo literal 0 HcmV?d00001 diff --git a/doc/templates/images/page.png b/doc/templates/images/page.png new file mode 100644 index 0000000000000000000000000000000000000000..1db151bd31e81a632d27236f7416ff3df5a02a89 GIT binary patch literal 3102 zcmYjTd0bQ15)PtK5iXzw**C55iX|vJgb-OoETwLUSP6)PwGts=Nl1VSHYn1#{44^2 z1hLwp7%fy}31E>&3ngJwo`k5W4{jiAk^mAQyoR179{kHJpJr2^fDy*ogy74M3d6aCgTwYq6m?Ai-7ZIWAMlD zXQRkTf8o&x#I}|I-vcMo2gik96Lz)fBj5cdOpUtx_wH)LOMx%KbU&=DXc*tU9<8=V zWBc=~mmYqU@yQ{(_Zb_opZw;B_Kst|qVMyLhs~_)SW~ScbJ4 z#leREJmMi0WpKCC%7Xtr`OLXCut~F@cw2F-N@<3eNcxQJwG)|GD{w3d>}_lFD-OCJ zhc;WOa40W7{|~Vl$LUM13zROPt4=;}G%5fNP!`UFI^e5(3(nwhubkACZfe>sv0i#x z4jLFpCIq#kOG`^O*j&3Ss2_Z%O>HsK{5C7$+qVOjlsuz6`JZ8<Sml#YqJbT#VEN29{f)J4p^Z zbH>15t1EKh#EzrJx@F*dzz&6y*eXrBGlLBk!vLv5AgUoWEsPwoCZC35=++8ef;`r# z)6ixDLs!fA3g#<&1I_PN`3o)5iylL9(Gi`X{6U-M3mafzdxUcdEm*_GF*j>cE+4Et zs+6-ohj~VewRILo2PW1I)hv zLu>ihN>#Q#c{|%BXU61Cd&m4mVtNsxcj>vx015Ssn4NMHjsFKYLe}3*y^K%b>mJ9zG3tBUQW zdkt(sKY9}BoniQk*Jxlk=VZpWurq9kuYHqXsxSWc!&yro{7>5z#yJ|9UX&ddj?Ok) zuN_&9%#nTkcssf})btK$;hAKp!lwoG`2B-R7N8M0Jg=s12O~ORU(a9u$ z^6&=2RArE#C?#+n{kTCEeGPNUP^7BDH!teM%?f!!W#w2bGsCl2IHyW{XG}~?oO@}R z$@}%pU99Yz8ynHNAU{-)8F(bZ2fR8nuCow!P$ zR?*`oLh8L36~__o)*EI_Znry~42(so&svv`F6O<017Yv7LWXk&;_A-aTcDPmd=P9@ zaFA}Xd?JmavsSIpo|L9jdwZ9cX)x6E*1R+U+)uF~euotDSqX_qJid8YF!2<4SuxP~ zny`+B^j=?FeSN*VMt8}9`Zf4*c{?1F}`n6QK0({}q*-e6?ZC?oFOU56aFC z5Y_3bK#`rin#?y9hYoF9gY(vhbZS5Ok-Dn5I3rCYJtIby0ybyOAMRfbyv{_@dS!mY zaU}21ba3H|HExCc)QL9&=>9@53AHA4No@!#=7V%V_S=rI^^o`K=hvLB0!+ocHOsU} z(jo*Fgk(zgo84<-sTI1pp}`{;oGk(i>|`Cza0$ADlSar_&+|(`d^YY!g0`}36co?P zgwg(uA{`poHGP@Q-$-WbC*4_maCT}bIQZprvI82m;>DJa-e-+}y1HEbEc<+~$BneOd^Fcy8+B?9!YtF@)0Yy;IW8Fi$D>@#**NKRAhDBsA8Z+WQ^MI#SEo z+>A+IzUUoY+q^fiw%TVppx?XS7vT8}wbwT{yL7;$a)0-Wx@THB=v2~`E0*E!$9#gOT$V@aa7|gx-skg>p5TPx`E8ZgBa=?!~bO7jiZz5x5DcqJ9 zKvok8o(W$%-R_EBT(a_RNj_gIaM!#lc@VW$8?=)(MpqZGV$teH; literal 0 HcmV?d00001 diff --git a/doc/templates/images/page_bg.png b/doc/templates/images/page_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..9b3bd999dfb5b4988ff40bbf82de81f762a5d034 GIT binary patch literal 84 zcmeAS@N?(olHy`uVBq!ia0vp^j6j^h!2~4t_wf1vDQQm^#}J9j$v^)8w?C{QXY=Fa iWc7qUUtR_aurYMpF`Xq+5+@5(#^CAd=d#Wzp$Pz46c|4M literal 0 HcmV?d00001 diff --git a/doc/templates/images/qt_icon.png b/doc/templates/images/qt_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fbaee358435735e32e36b0daf0d5232989f34c7a GIT binary patch literal 4775 zcmV;Y5?JktP)tV^ZjAdH_+t^SB96J~%A#olhGocAFnY1NgV5WplA=9*zKho6c(4p;M^GGMOHBQ3d z389@fG1!<8zz^&MY%D(|Vfi7+diaxM$$Hp#ySwM?-Yb1qf=-igG5GuLw|DQ^v*-NI zIlr@j5Q0s;hOS&b)!iXK9J_E@l#JF_go6RrIs9%-q9t4s&&28yp-eKAVIR>2UWpHO z9C_^JXLg?2BR|JufiK=zR+9z$tCe% zc`}^&Jxctj6nEcqLVwSfg}!=gCLAnLasXEnffh_Klv^$Ul+@};{89ew=r5qY0Ifu= z;l^-DA{%YUMCVCc%EoGcHxj>e=#_ha{1*&m0QlP4yfOx(d(tK7za0Ez4M~c~wTcDa z7M>r`en6X(UakDf^6{o*X+<{Hkd4(RgXNk3`x?}wD>r|(t*=cBAnNmxdv48;p1|F6 zrG8@q^4$)KqAf%zS8E*ySu2q@lu^+>fLT7SNi9P?$yq+SBo(a4#2QkeTq0OLH???n zi36`4dyB-?BCe>9Gp;N_nAc0$P*`#~)EkfyJ1DgJ((lc^Y{yA0HE%Y1YkEu{?GE`BMXqIj*mr<2mG)3ap3#UodT3eZ9iGl`sN9jy$ zI(i!^3XG-T^cFZgvXWx;BGHV<*3sUTYV{*ry9t@1UN6_SCD^60&cayd*`c?UAo|Xn zaBU(~sn!tA23~J_n|TZ~mkL@(1GD>}HH^5yNN7MSVrc#X%(5AIB5jgVtCEpMgKOZ- zxCD8s8GoXurJA?TeWU%xdoemFP8jvHOWKlX9gJQow;Z^!VA>%3AY2KAT@NmplbXT15^^Uk z8h%m396`T>Jg0?eJf?SZQmRKt zv_l+`PaLOXJEpzMTd^gQsB{ecQ-gR;GRQJd+FpJ{cPDR#b zEfj?LXk;~Szz}L<7Ve=4(4*w9xDtjzm}Wtp>+O`5;pkZX-kFh?TL*`SC;w{g{hMyN zt7=ZZ80v?{dyn_Naj@(8@oPh5aw^aezzA*Y!i_FM{fb_M`HN!$DTzip3>ZLnF^hvu zj}WFAn!Mf=F?o&s*TI3K=SEjouXy&M$0}3VnSxU?lwDQ*`Id?eyV{>TF|Z5m5P?Nb z$hCQ#8<>zHE~{)Ay>?+t6wm=MOW_(;9mjDMtyLe;;D`1=VmkziUg*#fReyi>wVs|! zDU3>P`Zh3F_1CKACw@eP6xH_`q z*x&Sw90!AvoO59i1;d0kTy9aB(gtp_B6JkvWw85jp=V$$87<#&-;*CkTzlQ#a9?wF zBLO7}Amejk5`RT;yuyF0Pkpd%#G6^>a5EOS+ zasr<4sP{q#Rn8Hr1Obf@ueA=zkMad;n{F-^X#DEvuC}L+4sZFFy^pW{$Bp0KvANhh zZl3!UX5dQ-)P8Y7PJ7#eRa3+TurZ1Asc}k!2e$!-Q7(aDgM1V1MXq}I%C(N3EArDf zZ~GHp?H)M)vm?DP<|l^2DUmT^N_|ZK_&0E&bJL zs`eWNsWv8SJJohfSstV1>q?GBs7Ekt9XK_t+&@~rX7SCwKz6WeJ3f+(l&))x%NhUS zS3kUbf}> z%O`yme4_W)w3%9n6%BFu(9r0>?qhqWjck~+;>}}&Zyp~EOkTgCaosm={^~Rj_UyI> z0JKRhu1bSI#yp$X+5||aRNz)eV*IM;zBD1f-!gxtuf(0h@0vVJ?NBJhE9Qi|2gdfc z|K_vH*G~cIaAv45|3K%#Z$G$cMSXMeRDZnqyYj!o<9);9{jI%w-s}5MlZ;c)=O$AW zHf`XP37TR9SkK@jYJcUtW?zXfj-JHz;!ets4cSup^__FA#e+Y#;3mS;nJ-z!%F-hh z$`iH<%;^*maDp+D`cmdj-l?%du(J||7)wRUD*d9dysk_Z5X~{Kl-3q0RmIExbBBrt zFRgB5)}=n+5?WBI8qhvDMRl8S3CgMglX~rq10z@EkE?51d}kdR>qGr|Yp1$gm;qGCS`W6R z;cwo5Z~45X-tp3WzxJ`*$DjC;9I)$wpG>pn@BH*Tjn%cua6&#`5Un~Dq4-DHrZB0Q zry>93ecg-{f=L%KoEoF@y6=>$x;*3|L8iiAb4lm$&-PcVg&U&BE@Wdo*O+1MhATtz!TVHV*X-|h&a|1eSW1a9{O zu9Lz#W%(32eQZJ-1J#$F>>B}+3Z$ZB$Ls(UE5H=;Lqln9cWqEY1KuZrRL_*s2V; z4+hgFFqifbwz?Kn*8|n7Ug>am9m2R-eNTljnvrbUm>GcZmD^OKFhl4wrL)a>C^OTZ zXKvFCxQ>wzswY!4I zcP(D;(0L}Ig`Q-5lJr!Zpvr8WO=*}E>E05(#({-pbyZ%0MvNLYR4{nA z3wTX7jOBP&p~qL~d~7j6ieZm>3KKI~asTKU+~S!L6Ss?WfZasP0&~~YkfIbsPnrZ$ zea|H+G#KVMz6{bcQmf^DOg52JRGK9mZJGpf$axv%mlOgt8Q~R zjzMZ#HAhyD8a5CL@;gIDE^8>*X%2cI4OEwjO_SZ*ZE zw7NL<7YuGtcMH=3X2OK@=tbN_}3TD1_p08BsfJmI6=fkg z{r>iMrVVJGd)wESKe6RY|9*7ykq0;YC8OpKSl*Qj!^U~qw-l!ZnS{V*`ue7X{QUI3 z=Pq38@m2HZuKWJ^-r&=(^~z!2zw^;A|Iz1*6#T}eu`@V-Rk~Epf8_j$;;B|Pw#Yv& zY;Z8ixiVtzW}e`7>WDGaCLy%A^6t{QSbarQPVuc5zUQmvhV%`8zVh*x|7=_9-|qS5 zx_hSQ64$QmIkUwkt}S(n9ew`rOTI+t6nw_4-t>sMr%=N(MyplR|A485X+p|BpIb2p zf+agoyt;MwfB4SdSWz!)<4RfI7hSKM-ZFNrVEwwLymWcJESTAY&wsb8aM6b>og6vb zy_0f6+cFfaWa+0@we@${#Cgq2&8FMC66NkmI~M>|jV@7;I)`Cae-Q~RZTU<)&Y zZ~+FcJ(!9PO&Z*urYXXu-m|IF`_|;P?;Y5*{h>!L-2cGGH_cAg4^Q;H-1fwo!hQ@^ zy8R<8UtOIER%gnzUiP((?rm%3rh!0dkT4Dh=7d4X64Ei{&uf>a!L&O$OwX#&HDlqD z74xE@0Dtl5z9(PbF+Mt)PiD&FbNp2M)GylperWtWm0q@+Y3XQ5fQ;yplQxUj<^!oYttFu_l@Y?R#BLOCs3r}5O%SH$Ekpp3lKjFDH zjsdK^{L|{u(-+z4o{Q|)z3w3<@E(z~RE0HTlk*7S0*lFwSvp+vnT3BX{a8s1o~F4f z;x$g22*fPEIE9wPX&qveFdx(>y^uer69e`L4IZDwJ+voQiPP6*GDD&VEEXH4Mu=3X zW@+}5*H?ZbSQ4T0Zr(~j2ycu46L~1ufN8Uv`pRX^gki{p+pBdbJOa4vov^rISrbMF z_4K$PPvv8avato}aIQRDn~Bwh0@3Mb3Wb+P0%Bkt6Jmrpg=JsJ>A)Sc4#DkFICI$7 zVQve>wo{1iFU^z1s}zS~nC(qH!aoAZw=M zTF4RstyR<}S0{rNS-H9K`too!`t|SQQe@BOyB?J?JhN|`uza<9N{sf@p*)GuLmlT1 zRwhE3>co;{B$EjK;f~b@@UP8xJt75^b+QLJz%iV6P4dQcutFaWr=R?P z?#&l3mON9JTAeJdkp3(6r~bEu@JA_M@%6s~3;;6JnH|?8YH0uf002ovPDHLkV1i3j BNt^%x literal 0 HcmV?d00001 diff --git a/doc/templates/images/spinner.gif b/doc/templates/images/spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..1ed786f2ece49ec5db07dee13a56ef38025b628c GIT binary patch literal 2037 zcmY*ZcTf|19{+AO8xjIZfItFCFrkL3BodGwflvety+|>T03uy{D35o<4X`9Q3=bSU zojZMs3Qw_)j=f_!)B!!mUKqwc>ezd^4c;H{$Ifr|uTTHRC8&buXgI)uM*u&6{`~Rd zM}L3+_wV1IK7G1x-@ebEKY#i1<^B8j-@bj@wr$&s7cWknII(;8?sMnPJ$(4^-Me>t z_wN1p@#D#pCr3v|A3b`6qUeJM5ANT;KRi7A^5x65YuBDSb?WNXs}mCwVPRo|gM+(v z?RxX(&Gzlv_w3no`}XavTesf4dGq@9>xT~?_VDnybm`KK8#fLfJh*P%x(gRB?AWp6 z>({T(pFdAaOMCY0SzBA%hYueT6BF;;xnpHzb@}q;O`A4(d3im4{J5Z?;ONn#0|NsW zFJ9cgfB);(ugAv5a&vP_OG`&aMz(C(^6J&A@$vBk2M!!Re*DRkCvV@rJ%9duQ&ZFC z&6|%MJC>4?a{Bb?vuDqqIdg`~z*4Ws1>(;I2=4ORL zQCC-|R4OYgD_5;rwQSk44I4IW+_=%t&(GD>Rj=1yxpHM_Xh`ytnG&0k9<5Zz%KT@c z2mnYvQ>hsF`jQ_R5(mKIydH2saldkg!Gzlvi9nFeu<gyfnCs8|SGO1R0M3N~Sp zx@MoynP5Rh(303ldPHFemMT%$+lXy}A1JR?IwL6=E`apW=@Z-0R!QO^@j_&{6#;i|5R1o$ zJ1J~xCDQ$1cs9`DW9Z!)D)^+%&Ug81908UkMXX;jkp>#b+SE}d{`0S>>Ef(`Ns6oa zB~H-LX25y1!BDgW%?nC0$RettYz8zsuz>9Z!g8TH$kU;rwYWrSTkjuYCH9utgFU6b z*wzBKaG6IjEXYSpAo5j4&c(t zwi-c(Q6YX2N-v2q(mgN`>wuCTV&XSRUA4h-f8g+uV2k_=o;2wb`7N)&+7T-C+CT_Bu4KrjWmK>x0AbH2rmR&7+q6fX+R0o&}V!t?TP+g0{x`8PSP{7CvnEPL^Hzx^T2Eus2 zi$U6ZM7}+l%$}afWn#%|+MPTsC236GKi9-ad*Z9;Ymg?jSO$L1s$w4BvDzu_kH9>z zTWC{K?jx4~H3oGGt3}I}LWNw@H)C>9GF4^|x(5n#+Va}kr_cb>{a+K%>B+@JNrC8? zJOUkD3fe*NdA-rJupqo?FfRK}k zOm!l7Dj$dsL|kS`3FL3^@^7axrU9d*@79yf!+bu3kXbziU!v&TO3p#w{y_lYG5ZPzDh;giB_lkGp((nqZ12&%LTrgm4vY= z{ffd0B$DiUKReJ9>?{#_KVrMyhiu_A8fNd!&DWTZ4+9S|1lJHkLv-^}a0IC{WI9N! zQTY-}db!%OH^;l2ZeajnA&Q6Uv=#0KZB9;^^lzMOi^0~bS~m!&K#e6hia79(ItV9M SXP~*+AU!zMlD%~Wg#Hic*k=_0 literal 0 HcmV?d00001 diff --git a/doc/templates/images/sprites-combined.png b/doc/templates/images/sprites-combined.png new file mode 100644 index 0000000000000000000000000000000000000000..3a48b21f6b85270d2282a74173354220e3a09efe GIT binary patch literal 62534 zcmeFZc{H1A*f&bGbV7IS4rbk2I-y0(K_}X+hE`iOhMGl;A;zFu+M24=Jhe5nrWg}R zjMY*irXYx*H3S(bAtk~W-TOW3eD68uUF-e6b=Eq6oUCO%k>q)v`@Zh$zVGYz`(4-b z(#*tA@UYln9v&XSJGXD%=i%X7;Qq-RGfawL?|gO!ty7>Q6dwBj! zyZ(RRrqew4x2DBOV~2Sn`MM9p3OScJhLP!doO#?NeoC z+9&gpZcEqPuLcKrct)l7g@D0jiqlX^FOb4=E^ubJB#A1 zw{}8n;ADaMT0)|R^135Hvk9))tzh0fF=zeu?ZYnml{!Kr4b!PWtc<8T8c1rGTTX1C zlZUl>GJ;brW8FmG$H=kPf4z)#@?0C*8TU*S(8kZ+5T*`X6D8YWB!JCO;Cr^_)XW3O zWL=kp$j0e~E_feOF95sfCoFdLJpnn)DVcj!GnI?)sv$wtLTID$;jbZ9WFbsrUaFoK z9CMc}g(26xCBq}$cRImZ5Ca^oGr^a`hdfQ-7ItAjzpiZFkN z-FQ`E&dxBGn62AFt;EjuYA>gjA)2!y4spkO%U_=;J*EWQ&Dp$Hq4$2h6U%ScL;7Tl@-vrIRrzz&5RBHq`6%P;g_)<&;T7zE5xi zinXLzL(aH zp!0&KtP};s5k28!sh}GVRdo{Xq+J!WTKC70k=`UJ@qP7jHK@_>64GOoMP;i8DxF^m zn?!@xe78Z}xC*{O-|*v`)AnwGJO#m$%*A`-EDfx671GoChWMvf7=aIqz>} zfQ8h%*x^c-bKxT;?K7=0{<(-ep7W_*H$tE2n+DyqKF8c(4Z}e+=!TQYnYmGmIyswm zq!zRPhB4u1*kw?&jz*N)P|uBg>0qayruTm}OY^U4P;y~;6Mi^_ZKH!cJpHC$gpQ*a z(e>A4PK)liNim)Y3BLC#FcXVpqSdk$ZZ_i*k4SzJIe>RKO$Cb;c;a1c2rC-M7;79P zI~I#y@vr~;{4;)4QdVossvF&n)+JctVgbHYzcG4o_RC#GEfC83CM@Z#v(eNM5H(?b zsCZt(O*EPM<#iF8$N)ngd5xu9-O2;2jcGL$rRb_q5NASAyR* zbn~-D^A2Z=6Nm2@Lm;OWXSNowNR9Bc09$h9zBNiymdfiBf64Y7;8mY;{E9SLz^6zC zMqs0~!TPX7L6$}U&a1>X`}rwq1`;BkU>gn2hIYTM)F9- zi^xZfm(=3H8q(jxIw8hkS9NxN&8#*phxg?iG_#79g01P81};xRigUGLUZ)qhbHB

hVL02vSpgeUmSV-ZK97+_l<~QR!_Hh-anQF~BBw(EY8Nini`0;XWoO9w|xE**0qs z`s&H>0{<~{JqgPgca88XoN5+S*?@D`UOUoH>zt^r31ufLtFl6tIofYlLLiOX*n%qF zj<|GVdc}S*ISoM&9bP8E87=*w@uqz3sLMu>U&2RIdql1yTRK zQ^G%9XMM$sIxC*6Q@B2+&un={_WA_J7)>;sRjpD0-|^#kJ)MF_1GDQ!jx5dg?(18MXjEqJF75w-pnM6nd9{ zLW__@ada*{iSsJ-GgS1p7xrgcE4b;ar?tE)wX29BC68$dnCgk3*H?Xw=QKm5)E#Ud zYgfvjedSwoBb!bQ64+pCo$C7 zNGev%(c4~W-`n>icmC{O`}FD3Y$vp14)rFgS8(C^W$PPg$&8A!;?{9cLy2rWP! zs!miLzXMS5UOL6ojM(P^X`Eb@bW_YOE$wNEaLo@7{Pk<$LlQ%k^C3w$OEl^$hmS@#>S9Q9l8yz-oW_+ zF^|A8Se~S@wBZeGFKzyHX=XT6PwHylntL~yOc#S4g$9%_G{3OBUpXr8_}TgF4ce@i z6eUo+<1HCkS%vSSuXo1*j=kr1BAbd0VXd{f9R!>0gu;it=a{HvhtAE&CF&qGyNZhEUHE1;f24!1^^znD^9jW5qbX>+06Uil!@`o-(F)buF? z%on?myQm_yb)@ZKu#>-ej1bR2j6dS5@c;o?5pd$J#5+%Soa z#-e5GC|fxrHCVu$*5O9MmFN6WvT#UkRG71r#fTditl|((9jVwZ+uLHM_F>dc5$C%$ zwtB@*PVBsA2nUN_CR>eN&nm~@MtD0_&%0O$o^e=`bS=P~Nsb!C7_o;u7Hm&!9~)8b ze6C+%d%_jXs3$>0{MEmlxB<nA}9 zs73N2ukHns>a3CgtkG`PWA3*icdZ^ldMlz=(8g5O}Wa5Ov0 zMAIWIU{W-4xrmalj$g7k5p>}Osn8Y`75ckZMQpZ@u7wO%vc`Lw$`%m~KVDUR z+TyULq}BYBjw~QWv4hR?412$E7#WT(%+Y!QmTivsJJ^kv6%GJZ6)XGn%i3Rl#tp(H#!C)R{<#AYM)BHSm}U#OspEY zGM9K(@0@a02n_vhc;iz-*_{NnS|nfHSf^USskJv&1I{C7DU#}kTf)6cl12u{>CT93 zM$B#CcL$u80FB^S)F0L2X-U%o=wkrvO{q&(Lb)cUXWcX*m!4lwl82F&`~q4!C3n62 zq2vyXqE>Dqv?XNt)k=7{F<-NGlG|8--U0>eAs=%MThdt`Dsj_vgjuo-2KC!dyq*y1 zlnN~mu8ewUs7v%A9a}?PYsWS-VmwLdHtm5n3Jro&Ub|}xt^sJRTy0D!Yqy!M%u7}S zq1Fad_(Gms1LNwqi;9YBoN*^LDLMY1i|Joa-;&~D8X#qJz~sdrR?%MUu9NIROP(q{ zu($UzVa~m3{P$B}pe#zVbvRNpL_nv4N zn4lUfbr$0n0sZ;#twq{h9-ftCf;6~)?eGFmWVrMmXW(Nmz&sv!cz?TO2(N?3+3uAVBb5KUI|+QiCwg~IBE)TLM=wL$~R zHy5??^fp)HA(aHh35`w(;lM1MP)oC+fHO2y@M783PNL|H(qdiy8qj3;v!H*^RfnXg zJQ~ZBZH^)x`p}yyl7d?J51=JU_6p2)51#mqvgH1sQC3J>>Lp#jh@f;Y>&q=!5k|kZ zjZ~UtMV5qSKxesz-xm_5K|JKwTf8+;10~hwwS4{uCFN@FyfJzA zP_KkU=iJ6~BT*}nF>6{g0a8x7WMN-VGKx;GxMNH&8NZT44Lt7x`AD|&Di{i{XhUE- zKM7L4UjCqvpJs$BnG5?1Enyv(hl8bbzJ#0L+)x$#whCpkUZQpmQA z8hz_&cQwMzxFH4-u+?^Ij)U?xSs@e5yyla`;5&salsy-TOlla_T@V|(t8tfqAN#}R zvwMpLAM5A;MMi?xfkFwvirR|M^CTz?)X(D#)}6_U>8u7!Sb33jCLl5SWe=~P@k}W8 za)fAkeV*>nw1xLpj1337(W{3S>V(eA5w^9?f53XL0Uy;pe?BaZ{}eEWbbdDC2@_p< z^Pi#7!cu1KtNV6XPayif+j+(!>!JHvAS|1flxMT^dV)P-il@Jvoe4v=#BiZepm;<5 zBG<=d3S76%koA4-Wd@!6Ds0EWisvI)7U7cw-Rg~4umx^J8tKbx5}gg7x5;AYoISFHTrQmmoXXzW=n4p6;ptpQwEBY?p z`fO+yb~3;B`f~j8^0`!LtKsVV6TTJx0gsPy$5kZYPaJ+< zw~MXe#IK)iTalvN&RPGpqR~Uy z|4?`CWm;1PzD3E0{}(~zqScOPg$a~HyDAP;3)u<}X!o)CIF-&kO$ z{Q7)8tVM~T54CcE9RsGr5wxq;!{Fz!^_gU$Pi=E3=1ED1#@EiI$PBvjL&XYP zl=Jz*JMdn=j&h?8jP`eumw>fwiMnmJTK$|w^<2$5bMrKoi*zcNM0 zb6zbUj!w_QQjzrD>Ot%8rUEYs^d|y_gx2{DSYL5zIi{1aP;DLW_=uIlFoiLh4OWi8 zDCeXxx3bhcI0*k%C~YFwrc=ycXFdIoiS^l3xU{MUb}E@BFD9k@ecVp5x5tq%(fAq7 z$q`%{SU$(?$H9f~!Z3C_3G#Gl$A?x?Ow0`TnMu!8s4F%o9BI7@&i692W-y+CVWGMb z`x~1-0xghoBO_9Q!@}e=fR~oFia2WORlO~Juf|7T5M?!_nN~G< z6!DVM$%-bV?VlKI3)c;X9cN#*SVK$}n9TcOP3uklGo-xoZa`};a3>(btqO4whdA&G z(ou>3-iL|EL31|AQ-Oz9eoxI48$Bc zxcg5;FIm(VUeaC}Sl; zOWBo?(K+LrDN54=*v6)$J^pN7KY$%Xeo6jY*d3b?0O3}Y&icL#* zf{59Yj}@$B1KWM|lC>%L!-{>#rs?qj(p@X?-F3q%{?nX+h_Sl3SAl|HmY=7Z)1QC# zM@N2`8(MGi9AB@l$6*w`=TodDg^fSh;vh_QBj{z+S`OS#%wr5LD zX!_%m_zL5Bje!=hFP;f#%s#1Y@@-_fD#lZgL=lk18m2oWo+#N3X4Zxrzgt4ZU((>3 zrC@h;yU<2t!8w*`Si18ffBh$x{nZV}(=T0zLe>7k{l$j)%bV|~I0m#;WpLm#=L%8n z-FKg8Ttviug)Gbmi4=`D9wQM9&Tq zEB4=4?EV|N%YVB0|AtKX|KD_P^#5Bb?0>0$>VM>FnY1aclsU#ldpxbYk^i$N=m*1( zXLHfpbgN6m>LO5Ym!}}&y)btJZ8eOf1b+`#Efg8$Ey&-lDBe@iraxRzEL7llpX59_7^WvqK1AqPRDYXSsec}Fjl>kn;B z1BVzx|6YNHIMejuL$n|-Pm^fBu|_^yfSqp9AtWVZkrVd%L8|>1gcAlTBk z2epBVHaGHisI;?Q;*HV8%gLxOeXAN;;tFFc*HQ|Ro*75Wv_|){#+`hm|Mn5!VxCC& z4L&VnGH=1DeOt*WJDE5&CE>vurC3448zp#H(CIoyJ`7|@5$(~NE1!J$SA5@1SK6aa zZC3jw`8&3^K78^;RJgs)T0Ns&Nq zLv>>wy&n+RwuzoW{#E;>jKdqyil$R4+0%T;G&mS1P@@oRUGI2pN&02M#JXIAI)7XE zM+r6N%?({H5BbLp%&A99JUoYm7Wx#!D+v1&0#5ue)JkS&MkEmWfUf(L$VocD!BCgi zeMeUoS)K`KX_3L)FzGd=8?)O_&7}kxA=YTe=HNY_;W-b#EnmQ=-}lyOlX_9@tpHl< zZ2(7x&v18TE15^~88vI=AyIr z`=Na^u_t+$58O*EJlFk?4XV_5X`x(g)bKlJ7^*Tk;x&-;fRZ#)g>#>$$a3ch5|-uN zzX^1V)SAL+R3B?%im#%$oC%lfuv}fu>cl$&J~kRu?u=g6blm>xDN`W82;;a|a@mkf zGd;kj?UthW{#IVm!B4jYBA*<9vdUdo1V(BdG>?AE6A2?E0ZU$KOPn?td`C*^k(X>W zS9+!}*wph{a(+8QZo}&!tEs17T1b%?*JTq;&7ay#XiPm?z4d{lRr51Qb7nfupMUJR z_Nq($CB31)3I+xr14;?79WI9xn`C;MvGzn! zoM+2OU!J=PT-_HkdQ(SiP(=V5C+CMM)}{swwhsW$x$M_eT2p~jR_J-!E3$yaYQ6zY z*M095CW>zF+Ak!ni05I>%4T5ozThD<66l`|`CoQk5c^7{bGJGhqsJ0~q|f%!hNgeI zjbhhRB2=TAj(KUcK*x!*gP%Ytx5Oz~{ndK5$nq996DcJO=f?sy0?tCsYcy=mPW?Hz z{|H8_S^A)9An+%wEKUu?v7=7KC`s%DplK^ql}SQ>CHv77F*IC=N5JE^AHCxxyN~B3 zzi#R=%gH*Mo5m>^A6E-T;R))V;N;(`=^Zh@^?Ens zE!v#$iK3%o4C7Xkss$r_t>n{+^w1D|CW92JGv)Srn^xGO&Kg=dnZ&^6R%ko*j*qS0 z^wC)e%;!eRe(Vw{d%pEReWgKI|N0w(b}nAcEVfZRp~fX_egQmD#yI#EJ)|)bpas?>MfTM<-4^sckmLF zyw-q21zbd?Z{{_9v8ubKKT$LqdOhM9skroSwsXG5(%rRRPcK+xy!HeaNORe&+Yzzh;TV=8+H8GW==+q+g~1ru=5JTrH-w)e9-LikszzSCfQ!&dX(?_Z$=> zEl9Cb&329d%+N%*1GgsCKs&4B2Eh>H+AxnHD4GJ+dlT&;j=B8IE`576nbOVdAUJKT zf^cstl%C-h{_IUrDxa+B+#b;TQShmz%+XPKXnA4CWAX^Q8s;SoijTMmovypmC>OEc zknh)l6eVG)2-0W!T4pUkbBqSj`wPOjfCv1QG^8zFPL=J zOPZObvsbiXt~nCl0HgNE^`U~`8DG8LgE?GQHr>%Q107wBraFGDXh~}W9y&!191r-) z56R7^*F14Y)6@nQ>(3cO#c!eNZA%)~`$Zzh|CSdUf{eLx!Gx%=EaO*$=BCs&ZbV}r zab$t<9GC0iqNkid>eHbC^Ky2GeHCxtN;8s86Y~@!HV6Bh`y)%eP87edyWguhTXw&? z@$ZvfA?4=yhRLizR99b~onsnnyk75h=8TYfKAF^P#E4*@w4B<8m)_i{@nHmv$9)sj z_SFmffN)PptI#A!&x+PNcC}ib*k14|r{&5atR}s6Ih$8rcoYm1RriBsMp$rIZ=9T) zM{r%*V-U&{g0R*VEZ=w*S(zU5P&^Rg&u&N&WE^^Vy?Zg_vwLNEzAuQNytI*!c8fAs z`GKrAAi^h><{kXA`$5%RMGFc7;f`j)T~~pm;euoG8uE|d*JA^F_KA-(PY>2C-H}n~ z+~a8NlK3axzSRLrH%Fu>?UH5s>4>?38Ec#e?kye24)TZsOXGzG(X3 z?^{pIFw$6aAJ(e(SBz?j&<;1fh(x3dhX{tdII(_yN+y%Vv@6I822);i94!W(9^)<^ zLLUFVySZzktT(+56s$ucEpb%-MK_*ddvJSUOg<=Q^COyAJgx_y<6py!xKKO<3$C%4 zzNgk3a(wG(j8L^gYIxoJlptjsG{w*Un7>UF{64ds!$i1%-1EZ}QphMRgeU&T8enoW zO!bRb!r#a~W^9V{f3* z>_1uV10;`c8A)sE*6*e)FP;_-v_p4K$3O+k0`0Vn>pHY6`&udjU@cQtUJ5P1%z_93 zXvBi5mM|*N?uH-qb;ANoZ^rxYRe#1y{@natR*gFm=Nw_2FVkha@o4l#TEm@3kV#q7Ml88-imkUBt=PWVuPtDW$&%n9Esg)df}(+Jn)u!puz>f(pZ){%Ng>P z-wbtfv)1HxJ)p_qO5pWs@xRHDQ*T@Lf47m;TZ;D)Z&)K-$*nN0G%0!axV;jndCyI= z{-~D*QEr!^_QDGhFLJ>0Gn%w*+ip7grZsqi3`sXe!n=_Cnnx2=T%RWaK90@w#8h3$ zQH;3aIN2oH%RTuDa^@;@Jr4`=RHznrQ4qwA;Knv0oHXT~EKQnKB@9iQZ-K0l0lovs zci0vvneGQ|QZ2L&sW~G~w{lDpAXB_7(K>%cHoLIj@$rO+^|zZ$n5e)^KRZb^(J~?M zouMBU^8i77iBwjTTtg8gy~i#Pb$NW#8VzCeIj<%uz4qQ&G^{3g__IU|;g#koTP(Hu zjMZG_p9>8Uhm?z1jo+pj0``|K(TNr674?DH&#_S|m7=JCo9({9E#75*5t!8cW>qCc zv_ezKKr^5p8D^5R5a>tw!y?TG=$N$$a|On=2~vFd;@zj_eXL5HJMX(nqj^#4vrAJs z-cuA-t{BUNJvBDMh=ici%oiGX&?W}V4R|2yQ?S$`|7&^>wNB7&kel#Z1-d|Iz7nJ` zd?;;m-L!8DdH#3mC9x#upE0&_hd}I~yLtW&{(3<-PkO=c#tf(7oaI%!S$U5y>1DA6 zM9uZCS8`^Ha*Oq#3olm($Jx%@aNWgH>md16Md)2oA#Jz!U)}<}&a$o)#&QOQdvZTT zeNk)cJ*oUwZ8`Bs8u?JBSH_4@BV_c1o-}J+@kWGFYHeA{5#Kg#Qpl1YSs7iy7ot*t zT}#AzA!C%S*JvhGwX0KHr#(eh(t2!lx;xu~)rCwXZHx=pB=KPvFwbiJl7GdzAc?@~OVa)2^YRjQ+2yC(ah3{0=-}?ol-*JG zbuiuss=C^6%wE9fQks0o_mIJ+d%f4)W%?+}R_A+nk`jX^iGB$`_Oh#)#`?n9D5DZp z@sS*Dz`(NS1Z0x^;}6$)n|@L@=s4v=$2}2`*<^LS)C4TD+uXY^dCetfWYJ$_Nl#8) zuX#9Z{`GjK-$3C@W%$aAa?S*K6Z@eh*7d}D{AB2DaGht$1IVMWOsG^{7o=v~8jxWX zSBxKn+#b0$V&b!92fGL(J5;AjTzb~4MB;G=R?JZo{7zodz_VyV2 zOMAmB8KnkPic`}|tK_iaM0$-^$|Xc~6R9HNkQi~ID*7=L>L=bGfZiIVN88OYzYdFS zJjuD-KhH!W?W9jR=5Z|@W+FB#76lwnLaS%L!WuV{l`%;(Sr%A)%ZGgbY-bj$AN251 z?&NMLYMHQ#iKe@2)+A)v(PorMJ4?70d2YW(VShgn37$O;w)<6+UurL`yb(hWZC^~3 zg5B&rP%*U*H=qV?kS4w1B}0S{dYQ8p>|MStv{QQt6`KkpI&lwsJxMshNwk5z-h^$R zo#&*KWZR@RAtAnVTBx9euVr9YVvpB3-f!$@5(^DQE2~?QUN?J~mj<(|xuDkD1{?O) zk!Apz0izA+*9iUdI|W3SmFIp+4#j|t0sR3HuKmmBq~9vG*dc({o2D;pO6h&QfeJ1f zEpuNlsU%j2sO;Msil_guyJT&;A6>u1cqywsYlx_poJ-uG#{_e1KnLa)LY4pn?8~nW zucJcu$!qD`Hb;L6V1?2dN)>G|Tp>|6DSL132&DazV=OxK6VmNcdS^Nz?p^?ve3F(9 zH=l6c8NZK&OID3xgmNDRoKZ8-~NugJMpK z;j=*^uU(WvWww1Q-py=xrFZiGrJcZkPqNJ>TLY}WcxQ_027(IahA2SARiSxm`#pFF zQFG9(c_&fT91r?R5urKl52pV>-_ji(aZg0}w&z+_+6`iRVhCq1q6PzB+4kDVtIn5a zN(z;;@FjZQ*Rg4!^mpNS`qo1IFysm`ya>RsA|a@*#C5}xooW((>US(3f^J)xPtg{> zTTIl=Uk_Uno7$L7N~8A@31=Q5cp3aQbQO+|^5~d;FlhpjJzdgS*gT@)1|99QzL77u z60$v>6$IM}Z?iR(%(QDU4|!_DN=~zEaUGX#?3beq;F9nm-w&IcSI*qY@m=}q5;~gv zGXQ%OAqGK0Ecl4%Hh7>&Sxsai_k~u{Y;!N+jN{UF=9OVLm-M)u3U-TZia}!Xc+~K`Q|qJM+#VmrPcB<_$(6Om*9;?|5$dAn zJyUF8_7^G~_UL*?#I3Nl*-M|L*^C!+&zZh~?Dyb;0Uk(n`N;kVF32@*tt0X(K~tI5 zdYgtK#Sp^sllb{b@wuMB>+O5S$NAxw83Li7zr-+~B+N_Y#^xRC=K$Wjc$T==8*n;i z;7_h2#Rthgr_?`YN#%>_LRty^?$ITreX~nRcxB@`h(;Z*dJVa}u7=GEdD5yzSxy+3 zSjU!8rG=OojVNv;0=_a*jrvqB z6B_qu*w(pCxK=V~8v%M7I~lI{p@o&CnD;u<2U2RHyGvf9_5k`Lb{4(A*mX`=k(iYe ztPPZwk`9*(3r^amEdpFJ5w>i0d2}mz19Ym(SXcp&RBOzdezS~wCArofY$#BaB9lWU zh$mfO6t$`Lk4mp?@mYbdeIP0(4oXieAu<&t;jzKG&5SD|M?dhTpjB^UqF>(PYZ~cL z69BAf)dWFTR5*Io*KGx@w94hEZcH%bd8HzT- z?M0$ER1wpRk)Bw5Pf{Rm_;8mA6K^lbVO+&p9RWRKzR>u?WO`9`U^y;N9#+P3^$@Dsp%Twrw2r4;$;B6r5|@YpV{YAqn=$J9>2*hz-bu;#@H z7^D5Rq|Ijq%F!I%m9cKIaN34u(nf@m8lA5kkE|anm+UV^DR%uX3DfB3!x*=iK3+3`Cp5{r9!TtTA%#o{tn#e7JCsz`e1+)1l$e zFtzCZOU?+il%0h~d&~IqB zsQ&T!1_#>W8%iYCzFWlA-x-yk@Xv$6>})yF(`q03G(=llPV99r68=#YyoEowv=A6GJ~PNy}9rS}rW6dO;3u&|dh!JFGXNCWjxs zookZm!`VC=m7&L9PrtN=s#~Uo{K;~I3(p%DY3`k6zk7Hg_YKA=F{kp3R*-dm;^|ax zePKm^7XuHL%D%NFyuvj+CcV}#%yuo*M1zIV`9mgIgx6(T;|?g0TY|)G;*NNKI3;S? z@M*d9#+|;qP0c#&oif|Y9sc}LYs!j}`~31ztKVAS z0+H8t;z735A5DZ8^lP7`EUoh_K+Jc1hR#PP(iA{RAHnhA;fA=ho|=F^ddVRhD>~8A z{!+hBjodli4Nx>2}P{3Ls` zxXF6eN>oX)inMl=aej5L={!tIB*Rc3Hg5lV4=+(ilyK$BQ6d%T-GeUp{#N9x*LQ^LWt;6fi(`Z$`govWa9DS2!9KG;7U--j+30GNNJANFcr&7xPh1gjXBbZw12 zG>%&>UEh%Xu2&vFncAz=3f?-$+ALz8v%j|^1^3BFR^&fJru>SaC~Nix;*ntMPPdu)Nc`5fS)ZS zo%KYVVT>82m1M_DUv+t%Ih&wx$!YSHdaa_H+uV6kYasB$kzeoMYU4S3!IgK1dv(5b zf3A3X4qvX8AkhOR&FQarGW=w%FMkiFMJ`)1_Xr)LZMA zFZp~RQBtRMqR$q2C2(BUFz2jAZ-ed5`Wg&})yt3v0oSOaWgK$ZQImpHD_MX@9 z8+FygevciC4)uY;SJ;}TG~Rh(Y*f}_SI70Xv%^+QYv;yI?ZmXnlN;pK*=|sY$Is}E zMU{@>3>Lg;L2r({j>EDM(WHbuIg-0BNcBTPR|;NY1!v$_#4w?P{_3rupa;>ngRe`4 zP&7Bc5?J##3x(eWa6P`UxNG(8+_c-#K~PuTMlm#heKvaF#>Ls088DtroQQ}k$KA7@ z8blhM^bGpTrs&twUNFOy+j_^Rzw@{MHUNM6tYUdoV&((+vdYVE#Z4tIpn$cHnzvEX zxQ|=kEd}7zZklsT8VuERdMK#GFo2-Pphykddklg=)elUt?6A%4I#b+tS$W*ROyXVs zE!!G&a8bEOi1{O0M!uApBJlM;#gOc4`og3d!}buZ7qJ(}6@LqRjEH;S=3>kZzIoeC z%H%gK8*J_fSae(Ht{)IpEx3sItV$>U6=5BiUOu5am5EBfs-G(csQW9pwto4@Y?JNU zToYK)A>CN~0TVI9ytmrT zCe0x&A};B15CU^sj!J29iCRWN?m8OaYc5JyYcAlwJoCm)?JnE*-MDG~=@r?zb=fFg zm5h{sYKu9h6y1enF=iDzu>a9S(p`#>jH`hECGJE{5voY{&Idl~)qQW%yiRNBE+i?v z<_g0&tJg4OQ>t%bYwtu;kM%_mdoiAF*qs}u1Xc-Ti81N|ey=l3cBB^p?DQw><$mW- ze|gxYVaWYFtq?Pfe2X-tZmZXhVlzth^PRcpeX>Se>yR)LF{kYGeDLp;l4r|Uzwfj} z@A89tbS%o=3C5oA-{*fiFNLLISiDuxQpKzYgt_}^x8bG;l9HqG?c=qi3+6$C zGC>1z#oL$n68^YrRKx*h#*yc2uhwSFDb0KK2zLhh`N8X0#VLOeCOy>7slH=K zqAl+q6Rkx`2~v`yMjLOh`HQClOUB2OCYZxUE=K{&QyZX&RIas`YwQPJ`Sn@aGgsa7 z{FPT*D`>`9Q7g?XAXoG37r1-GNRdmH1z$)o z?)YJMq{-YwIK|rlw3b2SK%c0g+#LnF0v;CC*nbt$Tq1%}<2rKBQjE1979Y)*Nr{!d zJj^H1m6)b5SO+sIXGb9tR47dT}n4Gb>3{Q#FagP_5*&m`aWIlED_u@T6v zH|DMmobv_jvHM!Zv!h>7CQU2B$F4_XDBRb??veu`ZUt5g|--h91nIece;$q z5D~~DWa^B6Axq|Pxoq)#Ek`qT`)4`oKMhn)|6Xu-I9dgv196$5i-A9?0plk6j6ZWY zP_vEp!@+uvd0q`j(^lpCoK+-NtNfX>HKH3^$OTO zI=Ou9M8Cq-`h@``BkzmFXYKmM#m&uJt!j4f3Y~%8hs_z%X^g*8i6@F*lE3XZy#G_h z>5GDVof*yZLm9t|^*6AQQiLjrv57 z54(bN;ihV+cVB^|*#REK2ua*Z*h|ae;W_Rv$&|MJWh_$Woz0GAo?jfG<^^ZUmc==# znM$A9|1;~=J7vt)l~1BqZgNvcs_MDs)qN5+Pv2eBs^-6}ljgnZ3Ti!G*ol2yS{zzxqEkgFtvzy*E{w3L|GODF{IJH0tYxVY0Z&5NMv-*EXuaUMyJLi% zcSp}lUN+tJu$vDBWnG94guHQt(bK$l-OWL70d+p#uSX2Lzg6Qm_qV&dd-CAmeRnW8 zWogMHQ!Ah}Y+fb*=P#>0uh(A|U}BbHUd^r74+oF}RTa)lKGtd)N9^E0h*0Ww_=$cB zK9sCvd=|q&2T;|&)i@i?eGxHP7@>@hlbpEqhl8V|qhManf{}3Y);;T@+4dVne$)CF zl$6p3Ym_e-r^@m9UVntToa{KSkQxEkS*Aa#>)(!+-fnIA*&&WU$MYaG6pLP5!!j^9 z33aX2}rD3X^y0ezhTFxj2;QZPbjExu1!h&R5i~DA1+BS6cw!k)_qU z#*K=-6LG<>e_DkV)w|-vbxnan3}9b*NsLQ)-tE+GZjJwFnf5u0xP7dqp9z2ANZc5t zIV9(S;Q18ZxuU&Pskx$a$_oXees_mGgXeScP50FVXMMS)|2pWoH0xh3zX$#*-_|f{ zyWER1V%+xt66(KoKt;f4eVVKsk^AiuwcFVn9QP8d5NbUuoj4`~4<%j5&I)s*_>Jf7KglW!yRTji7#&is3%9;QN8htBwW;Myk!jzJW>sfPC03xw{$m9z^QW zQejQa{fieb78VxXoShOl(d0S4JdtB*c}$NM$gg|9!{Txl0>m&v2=|I8p`=jOA$H%ztZs{YC!YHHrhGk(tm zLZq(Cmupkx$p^qDhyL5afsmrx%B+z`Y2RWM0+cc9p6+hm>h%P8S4c3}`1s@;WJ=^UxGpKI<4&@$t@6pX@ht6?}FJ+>c7$w9X2!~fe zCjf)hj*yPJJ|HVcRaNyH(#Z^j4XhX%dLYjil0f^LA19w5c0Wc~T2WlhGqo#4KAXqJ zQqxQb$TZ7LK9_njyV7Vg^8VtAmC9#ybF)7zrH(c(ax-WuwNGg1e{l7cVNrhD+bSpm z!qD9v(n^O&i8RvPNDkd4-6@?CA`(OQ&`5XZ$Ph!<(D{z%fBc^zpdP-B@nS3Q$wLj|cc4$|{IpE<3`y_c^*r~(6^ zzVw@$|fN(5sg_E59Blh)XDrQFn47g|L;}!vPj{;harrZ= zOU$!T=Oseb2X&Vya29-0_P2>xXIgB`E_bt?9(mjcW@q-g%OQ)4*4E<(g7YM24w-o0 z$~an&1NA)CLe@_0!RH!N8Ai*Ej@8yPEX15f0sD(}LvL#Qx6&=mhSRt#bTD85u{+Lx zwM_l-yA90C=`Lyq19k;ZlGc{cksw$lib}~JH^i}CA$f=8lGL*O*sLorUrG{ax0`cY zIGeS$`f)7($zzO_d2#S_c@wV7` zLaBBKmXdKh=9fV#JM|WA7pJXGZ|>$4u{~{%$cPeN zkEKm=oo=NxSp?O&*d|z}K5AUF&aimvE*4drRMNGg5v!>)R@V&JE+x)pGrX~xZx~q~ zdHB3wyrmM&t*NeVE-ZGtU(?+uL+R^MyhG`GHgSBiJ5zd{@-O{F{Z#)`9V7WbCgrl= zWz6iHF9-gZ2rOHY27mCNf7d{k(?j>vZ!F;BC-3s0v}C$mZ+a=TZNW6%ZoE&rhm63R zdBY2}-SUhC;-nm>LFN+iX)+!3a%ye6)z*bGi8=1EG_DnG$&(gUK3?9z1Q`meit>t7 zWdxvyA0e+w+^nBXx*V>#Dzq;1-uc)lfp*Z3gLE-T9DKiF_5p+sat-@h;VH`kxgkv8 z;*nZS*Zi7T>X*YfM?H|A;#+-4o%gKR@~2=uqG#!g5qaalPcertL)%(&sLns;LbviUgUdovzI;?r;~H%lu4lk;pW+Dfjpl%A^29 z10QO)8~D5QH{3jVdrurgLbpxAP9268lc>3Z zT9ZEyA+5L_%ElOJx_q4;TFodZLZr z%UumyJ-8s;f2hSomscE^PuX*vC(YL=?IC#eTmd2^sOs91Q<%wrTM_`w$gz1H7tLvq zUk4k=)a(fmeeC8lgBE}9Kb?Kc&2kNirV22*kb$qySbhd+~B6!F3ZgNI7+b`An~7b{b|x5aX|%v%LTI>awwjR${$^S*KQ@9{8YD1{yk;+#T{oJ zqajaF4?b63DRB^hPh8)s{gB+eWwY|vm#u3kMoC{T|0W)@T&OHY&f>H_iulsfqO}1l zD=W{Mszl2yavZr3FBx6=Jliq~28mgHH$rJyrhH4~p4wd6G&9s|j%ZmFJ{4@*ikeGv z@EI3J8E61DDv~saYSogQ?fxb?Nx4XGP!!xLRGc#OKB(h%w)p9JUo$2;)bQ%h_R-L` z$^EpV4l(b}?crmaCHR2Qk+KzrQF?>-f`W_k0rFqz%rtEfd7mpX9)y@fE9$n5-zkNh znZ!aX-emdD-+k-5+!xa3T*qk-bPOazkoMV{BkO0CR|2_>W;xsZp-IlFJ zDpUBOj#xwKMjJw=EYAL*8Kd52C4#u6qIRXult`sCB5-x1E{ zp*+igHVQYM7#^xwEZx~O6;J7uIo#CDVgOqxBFo;FoVXK6=`(iq0lq-g!%(kGX%=e zA3&0t(FlFY59YUn8{QI8s-p-fkMWv0@yAUL|DCa1G060pP3jYolDtc=DUByKa}{sD z_+6iFQW$dlVH(r>cb7pfIThl797d$gN^#oF$hiBO^uv1qY{-&9pIrhaHol#$MP@YT zti90lh*!IAT|~PM4vwb!`9(jdotSP^P9Fta*sri$r=d6FZu|A30m7N81!u8N)OP(S z{SH!Zm#NKRMY#ztl6CnLvxebrNu z0)Rg(8+sO`6n5rF+Qw%1a7$`7boh~3HWZGV`ABb?XwtluVe@kJ&+f)m#;H-U8VgjsK5v$CXzCf&@LRViun>lQ@(MF+ZC#=mK=pp9P zkG0U`TwL2wmma@Lx~xnA_Vdt6I$VNX#EimAcGDOcdGFn^$y3YKmsK|Z?C{C0_dBJc zR<&Hm_HK;09JSQBOd0P*ceyYKyY8heH>%SoIEj@VFu}cn{i|5}qs_ufJjl$e{(+jh5-#HRjjGe^|-?JYo7Cla&)C&Kcx{Kw3IfL4+ zW%sED%5;7U!hZ$LE{+0nNXAB#!rH0GzCSbn331Wo&dMY?F$x5edn*_Lt(NGQ+=232 z-EO#?LUihI3IlYJm)iy$ZyXywhEiBtk1Cv$@_>RKE}*^D4X&V9WLj3zPKMPRb|oN6 zTkDx~C!57P-^-j8ZkK*#9+qZuriyv>$;BQKov6c57qP^;`^GbJ)MKa&4x*m_Pnrf1 zgqQIq8dMl@S2iyFR$avQmc>iK6a1Bau&`SAE!I-X%AUNKv-65GF5ai0o}!EfZ{v3B zDHMGfM-Gz!TRR(``^D~4#ut&VG80D2I$fJ)rmdz-w&HL0)3{8z8zicSy|RH|AP~s9 z`)f?y`>=1rhJ)QzCbPzy>*$ZI@Z$iP-yF^?Wf9b(i z?sV=ABJIjU?S*QaAi7r7bY=fWG7;z-PrL3Z3*|-}<|<7Efb}HU8reV`&XyX0Y42fn&$e`rVP!hvXV zd;4VRp~DVpALP|P)QIjLNvQxGD7Gv5RA}1a!MyXqer$H*Vx>*~quc9C`JpXBwAX z53n7EW1QI>km$ZjNm<#OIVTQQR=LnKhLUDPIW^y({g7cj)t~$_G3~XNWcGt%QqbVV z*Xc+Y;~VzD3I)5<(O>kc+T40STZ6j3&z6(3Iei$xHs-OfDrl7Z$!&n-T?!;Ab2H0*pz$I7v#@KBr;~geX>6|S@<-Pl5DtJYYS0Shv z6BAY>?a0BE$_yWd-5Yte+ktGCgj{<0#4aNsD=xB-!v5l1PieoKsocTj$78_hSLG8{zsS+_3hE%&1ZmpQ@LLgbTmE$?K1ng{WVufA~czHV0B`B&6w z{3v+?Esh$T($ut{L8-RtB_89k3!Vl8*)zYV(#X~6Dd}gF(c5U;%sF%}L zAGu%?(iuA{#rHh|Uv7jotg`T}xFqR$?G%}mrnEnTj|NJLvuMPw#2#hb4wn~-Ng?%$ zmEt0_m2$8EyHz;YZ3F}Xd3KP>k__Pc_JynW-2^wd5WB$?UZ-D$orDaZ$$OClrp^R{;J z75kwHcz<<#omBL&(5ZT#`<8oqY)18ZTzm)cS8c9R%r}~qHwxcY@x|eh&t;3#OtNN1 z#wj8Ku+Z$Pds;v9)n=*Q?)%o=4M?9OiGhL!ay{yP^CmCq?r}dGC5z1by3fDn{K9K& z1re2U+7Z!v|K97HySQ5O*Ku2_NU9(6*9GBd&r~wrPzMI*C{KFxfFYYsO?sfErTsg| zWp?_?0vL>^Is=PJ%Kr1WWhxXX;yW_t6&!q^L4m)55y`mQ_)OM?O5v?sWk5_NCa$)v$uU z&;&}CKhLnA#BBcUT{8N?s))D4WwyIXPdN7Rwa>0oeLDi9vu2*}f&3w-ebsHrPOH@n z6(JoF21P}jHX(C85!G5fF8P&92T;BklhNXL{8T&~#Oi)F z8k6d|d7{6qVs{mRk7ZOI&|{H~qbIzI`8#hwf#E{R>--a7`iaI_-JZ%xW|g zbL2c zwQ<8bwF|bY=84{XN;ZJt#=NjcqF5%e#|wIVa+k#~)Doc|Z1Zvd?+gF@f7u+9Clhk_ zQuw?xlp8gHTQ*vVaN>+RYC-M$=Rk@GrWyH3LjTLin$nh~Uymlp*?)N(NEQ2W9Q7~l zUIy~JrfuW>-6`UWQ3e0=8(94E3H+bwyFDSt2*kkfSyRR0q){SAoyrAUCN?%HDXHg? zk&$P;YS}se{`T-k5=_1q`XwMxUnD+jFwsfmdU|@g%dhaM_nbtM=H^9>l#KFe0c+i1 zNqM;crp}3>R0FmPg@9p=MGV%r&LEVjbQ(og;lNQ!oJ|;P9elZp3kv=4&#rp>g;!$5 zVH1PP%X*2<(CGZUyeCNv3SFTXq}yhH&vDXDz1`w|^eaz1X!{kmB^d&w^+#!G!9V2d zc?`|t9+r_ zM*L*pKb-#m=okObA^*_yznA^b^Z&E#f9U@|%K~%GkdjanJ_L_q{f9+aisBqhOz)hX zd9taY(X?ck-6mDz5r3Ht^_Lh&_;tZ6E$&mqG^6|%hbvK{zBm87U}$J)Vs0-zF*pzIug6a4wm(ljxgsPaI`y{P$BXuUB4+Q75lk*OzzIOFj08 zUs%`c7$?8yq{eB89ol(sZNRKL$z3pnc8eJ#GIQm}v~Q(7aj6{wo>#kp_~^&h3ks-T z-9lQn{<8S4;iEX8`f{AyU1J)^XAck_EU~REihLg$``%5FfsWa)=Honoo~94%?poVD zQkac90cn}Oj3ftHdJSDHb zg22ws?gt1IG+be-FG1dFpp@zgcY-G*W#jthW@JR>&{ypxr6z?TV#C|F2J}S^{AB-; zjQBjtxT|sxIAFfXmLgQ4iHu+&;?$Vdm`jSvTNfME3mUAqA29H0AQ7iw*v$Nh-r1JC zCPrbF{Rm%xu27_TYTvhG@4c?GoZ9xi0pFsI$q~|>uQNQI{mL>cuc5xqWbcA)zST7; z9^M*AbGEDgYeNv_esIvZL0d(2nMbkgdQ@uu-ZebRrx^Vq&+FjcPb?!7Q%cjRS#W$| zfj}fst^i?0KkE8tyRoj$`K%gu(Wa7D@*mLPGn$T6!5J<=` z30Ed!0bE4v9q@P_*k-X}UA9n3@)iYD?sz7(P6?3O55wux^RK%fqw@~CL9h?#lX zbK2V4I^RmInwv_htN72Y?(t8~a~1$_dmac`<1Aj5cg|}QQqR{@Cd5BBx=&InZwd`> z#Og-#5p{JE5MJ=r7X18%R82$OUP-jGgSblSXJ!J?H_b@Lw?0sZ#$|licXVSIe;EN@ z)g>7&?nQpu;m zjzB{cAZZ&50Z?go+$h-daAHnzL7WQ%8C>K*I0Y6KRJ4y~YNoN08+3IXcG zE#8axm^~l7!IboT{I)$)3c~7Z6tQexhkIb5^{W0R-34$PUq)`1#({aKXFg-=K0iNy zyK8CNRseo}a4=#bD#Rw0>{?Ibzj<^Z0IsrIYj?Qk*or*J3I4oqg?qLrQI~eP)=pJp zn#*NQFgSRFdh#}%E=|GWBp^tiLS>g@0i3k&293JvP9`Yk6IIQj+8(cuq6?%*Iz4<& zj_Q=#hzSAW;yOKGg+bWvsDpA3QcIHtni)>W*>u!$M%?RpseVXp>+W7emGi5TO`O1Q zw7l3)c)kz+7g^5G1UX(_p5z{N_dB;^CM`Tm0#O9XL}Qg4^q~EO9nQ_u{ z=``QgofW&mO&sXiSPq`XYr!(0hGB31aFa}h@ixTi%L$a^A<4w%Q7VqN3^U&Gie>5z zk1(9Ka4%g}mc7$}Zi=R#ky26|&l!z}jc$2y9l17iuiHu-9L6-R|H_Qx8~o2@cwEeG{sviYD)=Cl~2vqS#8niZl;;&BM0mZJJiU5#?;Pw`7ESM z6EwwZp$lR?9pc@RbbK=jXqhXcq5vC}WwQi5shAwOFure%Ko-^1x7D7_LYX*xR7&m2 z`vniWHz*2hF%!=$QLJq$O=h@XnL#bebo>}je=W~R4&EFz_(f%#*7R%rCJY~HiWvD` zIIkw4Ieh{NOM;{LpGoNX3>2ktyOM0RT{94{>jWG4-as!}+R62cORAnmiDt(U!}Pr#l4zwF=l z6F)ex&2CLEt&Ep}nSr6mz^rJ=xNZ^K1*MrN3FlGCMLSsbldCVOj*u`-AjL~AE`W1F zq|wwD0=!Gg7?d{8I4vav_MuF@hWjU zBe6V4Mm%o2Gx=gSMu%a?ppC8HoQYg41!gY`@* zOUbpeAyI9V-toawKKDyvB2LuE%Yz?3eqbdWfb=-Y8&=Ql z^qe*|Oj)+l~N>^O(!6McFd$t5c!MphZVadnvF%1s$+31he~JS ziE3oL?kakrR)64ffZ^jQVdG}~(#b{pD!$bwN852LQIKEn91v@-OuLDUSp(hg@fK20 zG8HyqIvQxWm)qtxpJ8HPSS&q8=ySu&-n>myWji!m-gtkp zROQ^~cE)}XbL)RBQ3cdAbfqUDAx(ej6&$Xqp|N&{K8g#mvi&T1@J;Y83Uv$M@9}87 zp+E^Jzk?AqeYq6PKq(#hVgSCSCBm$MQ+`Q~a88R5^xp*NaPRLTVf$gwOq@UyvD{0B z8iCJK4c&1`hcdY0D(g&If ztVElhR8Uxj~>>|69gZpDRX;-dB=M1 zX9SdtPx>Ca*!WfhxVppgnnzGdY4OR~Vi-5?WJCYsGDtcsOF?lkLgWn8HWQ--<{+nE z-9Q}S@7^5%D~2*Sh@_RP3zQ%a4lP1bh+>{p<9a7gxd@i(MP51V`%wvTF`DY==;&8m z7oH-I@+qbO>}7jAe8TQcw}{J9jq-Q0V>t*&74}8zGOYV)K30Z1pf53>t&dk zimzBgIHHMXr&`|fcZWC=J9BIsaQlq9rZ>jKZR8ovxjqg&yZU)dIeBHe!nTkHa?;() z)9RW^$8#r_dfv6cA&%~OX9n61-Dc1CM1oGQcptsjWd;24{$t)mo%i#?enZx zV-hAAGdPPgxZVkQaV9sYt@nbTbq0iI>6Y_O)GQ~YbRn;>FM3CO7RLRM6FE{GK5J6m z>M!sDKpdFPQyevRt#uZ~(S$5^J)m^5k)$MfX?D6{MZ|4Jg-OQGwEB3}2QWsU2RB}> zdQZ&Hha=F}i6z1?3A+4!aNI4dmov^w6Ebo_zD~bNh#aM~NJ~GXA;N=AcJW}&zjX`` z!?Q+fHsN$KC3xCSAPhZ3N0yRnEf6uwNv!CO`gOjeN#Q%Tly?AH43ltmCF1{#b+NyS z_csh_d_EiolzWZ9rfSP)_$t9PAn|EXorMRh)iEt-QQlCcZq(u zvlz?i<@a9JdaYMW1i3wQZdyk$5EvyRafR^XN z(c7Ar#SZhC8I7$k+zYAwc;a)fOc&0a#Q2NiJzYdsB!L;jr!F9~jw_r;bMjK-&={um zW#KR&mz)P;?x#qtxK<3(<-!IX~!*yV+Jp#Sh=U zTL3Y8WcSIery%$?WxaW?0>6Cr;dmJCvB4-7!ogI~TNJDPIK$x|Ta&H@|KaIcisHWP zh@yI6u@5{6e{&-ya{r3gemR_T2}N}EE>EV*l@Aj6)Er#t{V4v1@=e6}Q}9DtlYA2QSBfKAW7I7+Xnqz?!Q`k8kNrh&4-T|DSt)9k6%&ZIi^kW^{CZ*p?}`@0`11(L}gZ4`t(XK&taF0P;C8MgcBKx|!?>*=yRug7%j?Zj1N zDUp$$rYSI}P{rnU7>(17mUh(;cuezuiX=6yAUK}-l~m(;r^6*c|3Fg;5Sr&brzfZI zPaW#sd$oLia0Kt{z1QM!_WPeY6&# zKB3C%B<@IKh8h35*6>*H<@SYo$Y*e3S}J8di(6~RB;7r3VDv+c8;VFMkfs&kL4V#` zG+*mkbF~c73;O8_cN}d>ngG4QaH|PhaVOtTyAml0fAfIljS;rs!}Lru3{ZE=J7I|P zHy)$dp2w*1=_nCuf6RA}8^_nNAx#iSQMyqs>1_l5(1dlSjKy$)?w3pX7}Mr@+G2h%M>=6-57@oMdxT^+VGhj)<)dz1*p53w(2LUKW%dNZU%0(Y^GjcK|g3|ySz9VN1Mjs9j@1k+rEzxs?^WCZa2#xhN-r8-hM}5pvg#viBer%-BSgRj+`^N z6}mZNmXMI}VXYq?mY=t^#(#{!WsHnSLQJEGs%Vt*Nd>`qy4?vl3|l1 zboxaofn%_LqScDcd3~B+qUf+)dT6Q^bsOe5R7<72T5u?>G$r@m)Mr;NvaXndbie9IrL*{zSY$w*nxr-g!sUUf?m9nMJjaZ7=T0FAX&+ zT2^vGLnrLfpF}ZAF#p){rTX7cIgY>)Yh3KPZ{49ZM3~{*&ipu`y=Gr#Y@@{5c#w0R zcElwQd471=Od<}0{|sQAm~}T1EsK2{_YMg9-h)FFyM9$~w-DUsdm9ybV19p7W0T<5 z6?YES%n@(y#>^BT70p#z&b?bhO|^X(3{yPr7XUftt-A;oz{|_a!&chZ_nOB6@Av7Ji{?tF!am_xJWmCipkE+VKNo+%cYFon3v3~w&t>6VW>Ls*G z&H^DiD54_ox~ndwO2Ad_?hvjw=__1yLOpWg|B04W9S%*wM9eF6%-WuM@R2Z z5jsK;*_{)$pm?~XmL8va9&@O*#Y_Ga&&@bdtk3tO+g><7mkG$r2*+<(mgNzJ5hkYBSh**7b|9IU*6-hoauTH05<7c^=}PfWJWqK1MkOp< zhuc}Uby$TaekpK)gD!ZbtZ{>%yN#FHlE*x=;1;&I_jARj#Jv#k`3jm=Hd!{w$46a8Gj z3jvFf40;w2NUI8GXJ_Yq^wnLVEN+!ir{%WcDnL{J9{+BWW6a2<_k(;kFY3%K!02G{ z98rkzbAB|8a#V&S>9{CL^Y^Yzh2$qwT&L4hhsv$IAHNN_4qub~sKB;5YUR5mX4|gT z307*g28dq~xrpY7cJ<{){bC1g*Y)_DpNmA)I z^0D=CRp@;WE+v!JYmKVb?^{nIBR|IOtPM_-*~q8Q&AGR%K*^k$bA>&*TTnEO1`_E_ zvi@=4gLgdmI#widqi5Qhe)K6t5;;@6{J}h9QdUDCt|_IT-H7HSFkKuTFQrZsR~Skx z-$qJ85=;f46qP?vXHSn$8VM_o9j=f@ zibs!gw*7@Fz))%#|2jwWk3gnAMSvzt`Is{dUEVse?OxY}-1C zUCpkf%Bre3`lH_Y@En-7cKX%jEcr2V*L=-m-Vf^d?DPS%xez2fgIe$Id{9dc>d9{; zu?~UtzO~aK_HNU-N5$8CnQZTWUH8DQd#ZG1UN(Cze=EpoG-J$+-n+W@dpq=A7ji|M z>TW>)P*yXF!+boH%2C`d`Hw)(zic+y4zee6rqC6S%Co5^^1oxM5XP}b93dS6? zh|sXB8N)`Zv!MZU&6stl`1;Nk_2J<%6_M?V6mWh(YZ!{Et$j05;JcZD?@(B1GOTUN zX}`>WqWoK4(H&+wc#cA)*N>l5WQ2;IP@Co%*>c_Z>y8qgvh|i%H1m;5)s|pc)SI{d zs66)e&c^WZg@Nxqh6{hTR)e(w#lwJ-%6uuusyF<|14KsO^9xkfn2z&2Kv(`RP^Y&_Q?RUu$95zOX?fO^I ze;AL8YCKu`z_9&7R&zQYyvnX1%dj0@)G?%@qG=e144NIKUDk6ug~NmCG^5KqK>qi- zdI*yspJ)LwtP7W%G%Gzjc8yk=Q>BKMl@AG)#S<}*K|E8J%*4X}P8LLiEMb5Mlgp=b z6EAoY^FJ=58=G@8hv6^#OpWvp%gX3F3$eFsIPEW2eL7QYNMqBEoaz;EZq81IhXEEda$0C?1z@;z)byUG=kg~G&5MxP98%$_v8?2g4N z!CTQd3nxdXmSy?v5r-;G%VOM-5e2BTsN3S#+TWx-PAw}eUP&!0IEcLV@y#Bn^!d0X zX(Q*b1!-0r=JeZoT6%pG2u41*0RQ4m+er9!6UF6p0GpyLA2rrlS3-%#V?9$MzXq@) zX8N^|P!e}&G#Op~p`{@78wy8H@#k)o9xUXx*TbvCIHz}+h|m!GR)GK`?SB(`4CBYS%9q^ zEt!8kL@LGbJW`C66lgf^nVZtPS{fU-8|Co=!pWLyYz?zCG}X0!lT*Bu7Bw=~k>joe zl_%aGS!{PO=MR`)@+1t7jC>K=-aEy5#~4!Hx2c0M(O1|%Z00%F2EMvEpEH4PvCTKz zpxm78h1s;ZVXXSy=58CE8h-i|*4*qxJxi$PL1EzUT7gwx^DDNi1=!A*4>e_xt{NNd zg@z2Ns5eorGpHFIimi$p8&ki)JVY3f8q{yuFx;1YSx@2UX<53)b)TR2zVGBw8mA4V zfaw6-6HeJ>8Ovf86C7>=7wabNk)%F)*)XG`V>FXDROF8!zxVS%qHqxuGBUq3>xTu; zSnuJ8&*_$*z#VD&^7a6(SG;j=UStWul* z8Uo+Db|A^PN@vZjsq(mHY`j5vR*Nyl z?(W|2ljhzlugsNL8&DGkZCH%(CZT>|&EEPg?QSDHoIqXEe8J}<@`?vIQTQlgrvVSw zL3W~=9cOkE_0a3_#z9iS9R{G1-}jOeuMz(m6KGEm0@cygjZaLZVJXdJYRqey_x%;s zLC%b*Jc0f#BoQa8$>T7n50BWQu63_9g^P1kdn-z*jENG>x>Gr!M=q!pMuai5P}$6Z zjHg(Z^H6WZAURP-etX`1VEFUv=dvMVX@Xg=v(wsRNd#_w%*y!~YUsH2xJJVV3|t=` ze#sR}jfZ4~N$7BiYWvRoDw4yNW!p%llUpkcZ^Gf&k}cT$Smq>_3g2$l5kWHSV5Qk| z>JmjkdODvL;Jug`67EhOR6!88jx(~koR;WE^Ben2^4i|kR#I1&wBBwhba3z^^$DVk z9pj#!=5Ib`9{Ikm!Jsq6PM6c_PV($vi4fmXy$4yHwF|#gr~IX?VU7J;(ffTdv^M_v6~-&2fsK&ucGlZy{?Nn<~pOij$nV z_h|yo3>9rR*7Dhc_=qvAh5u1>WMD)h9s+hJWEVbqHgD&KZLE$n<$C^zcYdGYT6#RY z1cZJE86stV%WyRo2b5`npQRdjEXay=;4<&X&>qS}ekXW^8gS^x#ju~)2=icR>w^W05d}ESL%1Vedf9Ly zXU51iNk|R}l$YBfCWq3du<3qDNl8ia8@afKxGHYt z*CeC7p+3-RHHNrWJ2Iv6Ez?D%^1QJ8^-5&6e@RCxeNlGOPPd&n}O$ zkY!Xy_-E^8m3r5Nl*A8BDzn#G&Z5$*J3H?<@!mJs=Tt6RG*GiL5{7`i3U-v)+~#=m ze^$THmI8CKfKbYKTbO1Ue?h_|p!4Pok9IA5NPZkQ=0CY9ww|x1$Lr2tblXi zr~?T>turJoirBdAiQjPv#N$|BF!suM=4+BhwG+>+n~vz;XgN#mCeBvSB{(a7j6_=S z^!#o0v}9*z+xF<4xD~N85D@q!j|eatCqjl6neS=TN9I!BHLDSXbSqMsMxR#R@taFT zB@y@qV*Nh+KqXCEF!z0RS5MAJ&0M`S;qoZvP1O6pWnB@3F8ZTm#A4#f&I}x0wON;6 zw|Z+mSed!%vp8zzmFwDZl(+;M(myH3XU64-pkt$j^twl5yR3Fa6Yeh7dy~5DFUEcg z3d>fr5}kEba_bQ}fn#?|8v!>dUNKR6d}J3)1ybB^g7lQ7GT$mpnlB7jz$1u*Dnwf& z;xZV^XDfQ=l7j))x($$`qFP1)%x<@@g}Kj&)6;1}M0kWqo1TZ{SGUY<;*_6U1=0HL zi?>tyxieCQVGX);tj0&Ys|hY^b-_DhlXA=@d&N|fS5kn>4h;5x zeaQIx3UGuz&mOv7c8Byd@r+8?+he}AC}ml1?a**}T@@_i?H+Y*gGmuEx7Qmuw$(lS zu$M500~-xS>V(n~`lCH}6epwDDyke*R(d<-vEdeRk@g3FeUzz4xzJQr|k zqXDV@M6mv&vf$G`N{-EsscLTPP0F>9SYI4tbiFo34B)Q@YexBu~ zwzGg~tKSm*kpIuwOrGCoUp@?&IX=0Qk0sz(qS^2|Y7>F-tg=TA@e{hKT*VNsbEk($ zo|x;6PEDyQt9^}9X39$^krb@L1UmMjd&E%kJ(bhskn)uvkBs{Gu&2aJ=%haxgKcfV zbfiWY^~t43JzDN7d*ZG6kDtq7`B0MtjYCA9{I<5%<;?u@tIAk|-2>m?;BC}c^x)`b z>(o@}J0_25nZ!mqZL<~IL?h!QZGSH#S#`=!$6VKbd}S7>-{T2a_sPug&akYGC&0h;syuc~uF~IF6q^4oQ^CKwnVIW4!Z5iCNFWlk zrjf=Z3-S{|p%SY>Ips!Ej@J+VI*n(xW!7m{mp`&2u3M}t&mPCo>{~h#i6?_Lnbx6@ z`G$N*lSAkmj?d4c+s`SakUjV>^G}RQguPJ3m@HQ7Okq3Ge09b-Lr39^(Zcg@l*>_L zgu{dnH`zkK?x?Z*=QbJ@;U634)_;NBS0dX*Zt*f|AJ!9%A&prmaZE5Y2R!eA1{KYY}_{WueHmw_55^)hBvB2x}O_3&)i1uvQ4Pj(}_%|S0< z0ykW0e5Ca>#Vl~f1Yylh9e3w0$2k}0#%1kiePW**P@PC(JF;IXSg<$QSWa85Xy4k} z$gjWn%`9lI0K}JA){6j3$Bz0YRoe|&EW4~@5DB|f#-H&rbR;oE_tnbc=I`0A+yST6 zG>s9xkjKFz9=Ex`Z$Lz! z&z1rpA|Yj=M(e!x*;bhj^0i=6>*H_q7gJ3SfZ-EByPJSCGYNsrQ^k?^@+tF=^j+99 zJrbRjoQ~`_s%m%i2^pY|&B!v%TCYYeo?%UI^z_-Ajmv%s zH-p$KVZqg9Eb9$BunS#+g>C4JwUmYiFGFl@Fh=GqkF3~HZEn120=z(*Po+NwE-elN zZTjBkCYm@lXYEz5s{d-&`zdt9gHYg-lH;J36N0a2ud(|`Uk+`by-?0apnzLb{)!<- zw^YUn*)i}qv^t~Ye9|rbst-i@z~y(mJyzx2=jQV;A;ZMXju>{w-aF*)N#ewbs7dFu zoez+qcob=+;K;eUR^kfq<>O&gdMPM&u91XFo=twWqC_F?m@AsbZ~8s$3ix>XaNEN+ z5T>2gopP|;_|&ZhKj(sA-j;xDzJaiQ)Vkgdz|Hpb!=mziI1Z^Auyy0>=0C|@D_j?$ zBl3}+48myVLc#ci?58rJK@m-|F+cZAM{tLwFiDa1hj6z{XRqH}XEA%+fRw)dY_5*l zcT*qQs1?%veIjuFeaTwiA>^{Oo|;ELW>{$D;N{S_M&I>ZLj1Gn`rXNvsFSdlQX**S zX#+nUYQ|2T(AVAQ8K8*_cB*Qtbaq+JcTVT(6F&>#Rd>yprGYlmgHLY<2wkKiNDIr` zVf4>E&Xx;5^pT14S@r35A?t%HaM;_faD=a~fW3Qp#tKjc23`_zadT^DDx_V^OeD~P2z-Fkz6nrl(_rg*Q zy-@w?51J^)WHv|1p?nqK9g8x(Dl$W#DlCq+Q?ymDljqBiQHI|TjUvm4#@WV$izUar zB8=NdFh$Udv%Xek5H=QZr( z8!DeP=y`WkOB8QlyQO9+*-9y&8(;3_bODuY)ypK&2kE=qYZ%hzF6(>YwBnU+nSK*T3}^f zS$#y3U#iu;uCDXY!D#(~qza?G2I*XVS0cyaV!`z20X^kVM%6>uhY@jA;kAZ?p5?>!@j>}+lX0OE1UpMl( zLfwA~sIzFq#|8)9+xt~8ozBkB)3JczIg?NQpL80P?Y;$lphz&^*x01v;~=ai+tE1W z*F=Qv`I!k#mjG~saG@HZ8ilT*M=u;z3r6{ppLsHA=!5pZC&C^8A)X@Y0)RpJtuG~? z7}vj34hJi_mF!jPSGWUv*Dvonk2&8ZSEBe`EA;OuK;66iejj=ts+p&dN0F%p7Y>Qn z1J|@}nse|w7{QvYqM+{$GFWIKh-w+m$0#L-b9}>?oT%1i;f<}0Y&5Yrk!{X4P3ROP zjv$Evb^@?45%&-g(2MwJXG_2O5##;7#E+7SUl(k;oQ^jxWM$>}LlVZsMds%H{rw%W z<+{q_HG~)3R+J)#wuQyT>je$4$dj$u={MLVk6U#REXb|L0-6i&TceA8Ax@iFd1|P- z0vi#h6`JV18Oe9W%+ST%d&fDunXZt=`&l5(xJHl^x@cf&lhl%pn;43jnfYgJ>`=5r zoXy6+Egz1}$Ra$fxC}MH7xn*-ssC_f^Z(v}@eZnL6-8UCswj%0R;@0zX{lAj-Xr!% zkk+hGsZo2>s@i+TsJ-`yy-AD+LV_Sq`u+Vr@8|g!&VA0g&$(XLxh}6ydMKv#p@t-k zQf-BT{%6obN3V=KKqi6d$CZ9Qc4|M+f+87~vSJvcwm=48N2g(Rs!rxwV8Bvm(O6KQ z6y*8cqk-GLI(Gs5DNa(U!FeNz~(d0Lg#BMB_?Dg~8-Bjd9aBF{R zKPiirD!S3t)n&fRz`*dP)H80AK&0&41-6{ieV$L9ZP3oyBlsLDj3uCmG9>N3OeRr8 z4v&eSUMmX?VS~p9+khxv>7a2rQ(#)XA+9hZMqXXF)&|}WOiv%^$;9)g7z-Z$q;#KBb(p+_HGn-`~eS=~NTUzie&gBey`s4g8vNjH z&tsbmU&ia!6&k?1M9BDc)kM+n76&3Dl{DxURXc9KiFNsM4BA<#nEVCGEhba8FgU2cIT4ozvg{>8}v@MDEu(c#mH2~nWla9Ls~k}et_d#Jk*p|)n$)9iSz&F#Ir%3U{la}UNT~V>B2?}qlh6E2A z1}4Hwz|(Ch+--xTi^Zk=oG4(x@w1I<^Y5?SpKiS&ns0n#%u*x#w%sJ;t!5A0#54JO zvr4|sD1!jh*x}OYe%O++4sIh!P5Ef{L5W)LNj4&E&gi`&W6{}Lj;8ZDhnZRn=>^B@ ziKpcqUw^{96mU5cyImsc!%|eN>9%_w?anowms^ZM!94r5AJfm8CU!fxo%}Bw`Z7Yf zy=cN#?03KPYzNa({%V;B(#U6iFe`Nc@>3d8dkYR@_g59AGQ*TAtPL{K%Lwj-{5M_{ z-1Feh&`6{(6nFaAldgK_8Hix3Eck$)w&lT6jtt`L=~2Q>#nBr+&H-1s2_%= zfZR4!f*=r~ioGYtURV7*0o2D~cbZHyveTCS6ansQNEmYIvI|Z>y1eZss1PD&U0`1! znHTwl*(4pnBVHr%SWKGPE0aCC_?gd*uk|VfW9Ls*RM&VEggyDnyulFRN_Nm3eI zkG&_fr?Thuo$-j`0RKr#Gx$oaZw9~M9J8YQ!~K$Nt$DP_OH1g#BPETjUju_5v?o~Z zOIef105aD08Djakgw~^<$FZF~?fJ!{0yeh?AG@p9@;P{aLyAK^dZ;L;yPm~L3K|)I zRTY0+xUswJU|aaMwdLmFOb4VRTa4Yb?>EgP>iwj-8{FuHVQwD=&A7oBNqJq4Hzj!Y zqpNI6X?`^i8}uVmGct|&Jk2{DzFC96T+g zUE|-=#BcqV4|cR3 z9KU7w26I#M8zOHv(Ct1Dc#`4IMoD728sj0(sxeAWDxO@PoFAU=`2cU*G(bvM6>ql%&vim3fXufD{}w2@}d8@1rYXrI0y5YfYZ=a zeDRskttw=8GM-nzR#;U2Z*ue-dwg*)mbreY2noHHN=47D5&XSmFb9EI@>5m1__=)g ze!=X(iDug*nyp!7J!7a^TrB+L#1=#W76Q#OF?OTaiB#&OLhRlfB1ooht2%Ty^XNvH zTq~2*UV0BRqeChsT&O}RZK$-?fb&0G$un>`x2md&e!kXG@%eoso~8Efc2W15k~*!< z^4VniFK-^idyj&t#3F_1$q_!9{V%erS_+@>p9z&&pL4ax#>*KF@sBG7rbQJZo7)P> z8x6ic+@*Q=Zs#R_;Yp9ye3n9MNIcj|I3sH`KzLTi#q!ths%6<|v}Sih#T?%0cnPj@w6Y_NLA>WQ4Fgxyx%g-DQyIQodoE>6iHaZMp!@$BSqIYPYlvrxous9s|A8&|{H zvYztS7S+q2>hf*ll-v=7icL4A(B%gzjhWp@-@1}D4UB>WwJ6$ohgwwANut-TE@U=p zyl)bJVXS?hBXTxMoEu3*+GVd^+E6nJ8b|B`TdDK_uB5dk z#*xH>pp`8{=QeuCmFTm*0K*19DYwc>7eu;uL_Anj8kZ4G?bl}6WWOx80K%iz5*ht3 zgfq_9*@YwgA=87&=Cb~X`ah6^;d%X9B!vuqaN>{EI8Z{lS533$fa7hs&F*I;)!IvG z|MJ;f@aDmY|ahT5Apan@A z`dCUY-E7`dna4{C31&|Ne=7$1Ml|t$ETm2U#Sp&kG=F>#vOa070)|d0r!PPuPqv?*t?%7d1E%KG`?Jb5B z3Ou=nWq$r~?NKebN3a?b7jT|eDc)(we!_H+^Ygd^ea4&MzB%(430ii45`#%9P#Jqk zat;mId~~5!P7iI$%_W(yb7sJh6%}>u1|Qn2o2rT)ffLu3@cVC{+5`1Tu2zZ-w$%{ zyYekKx!m9RS+(catOt|65^updIAw3DG6}XPsHY0M9U}#CpN-y0JGXvCFM@@x#DHw} zg}9#1&-2U~M^|AC{4SSN64~Y(&o>a8Sp@~OY;~!NRVTiSNMpUb7mr48`Kre{Z`TF`HU>3NcCx&OkgRL3>uz8r)P=!W2L=K z_YA6Ro%pnt4vvwY(;a+NXnY>%kf}N+X``>^BKhp&1BC3#$(kL=LCkpc%;p?nUG>=> zAUDKhtobpK(f|qaFdA2#<58+p-btCvC}uTWtM`D6hJAP%|3XCG>*$H!(U@XAGFJ6q zs?3j+K40@~>T+2A)iKZH&`ABT*?WPGs!U zxoExJ_Y*rgEs#rt-NI?O8psTC3m4=3#{ZbdIyb4)O-Qri_oK(Ww-1Gp2`(OlY#I_e zY+|HTqT6~db~qupIz#TY1o>(-Rutattok_Oo`*C2-%VE z0y9w~vi63be{3stMUKcr8H)6>DzT5;jxbrdGau(#S%`WpoN?{}FZq)t^XE_pQqo;M zo*7G}=)tyW-H+>!2Ayxny-!C+MlL4q7V>pnG%y)mOIG$Lg?*EU9Dn>&TN6I%(?a6} zp!v)vz2fK;T9m^8NTjfVGKY10gkOd-F&3$pWSXwg9zVjr|9qVBEuGHd-G4{y#HP~t z?EHs~l}-DeS*1;MxaZURjLl=7R_ny;iB!>b{Z&rW_{Dc-61Lor{hGWbb*+qXK2Z1r z?RG(iNh(5ZJRV=95gx;m-gLqH8wW%}c@g6! zK)!D-$91RP$EtkO;p{h{+2dN4FImi6BKmwsU4(YJ2;Y00Rb2lLa+CNgjr4yVSr6hA zWyt#Qt~j5Nuje8GaIWeiffmJGRsQ$H5J6(h%~v56pTj zYRO;AJCfJcKLuaRU@VDntAnQe#p)esnku)l+MCHUkq*lRy_2N)(`kEhQtX1W#Q3;q z=S531{vB;w{PDe9t2}lu6I~(W!0ffhln>-&8ehxaA2RVN!<-bszv2APu?)?FU5(@o z$Dz}Eu6j@*#Q1PNJQH%gc~+czBqiLJ;6u+0FK!dcxZwPpYFLiU!6ie%Gr)Kb2_CYn z;3yiwjjZx>3NNoR-;2=xlWz>^?&TK0`TlD+e!O~kt-#XW$0f}*c?_&X3e6rexT=HP z1{OI4TXDs&U<$RL-lZ#7Wzt~w;Shc4`DkA0^4cs-2$Eu>MwcxVWSv7>2HA*wroj+x>kvr}Z>7^GZ$aeK8}w zu7E+HG11U9fRm$drG^RcO7SgcDE0j>=Rss+f4P)*FFL^2iFuyySGgVI{oi; z5h7^97g+if#9ws^SZTZuPHJ+fQW{WVu&7ukk>wbiO&{cDhLrXP{2++^>p-I?C}lX9sEdA)Uw-riGG8*)VZh~Bk!uCg`SbC)WaR-gSlmsH_l#+ zM{bp_9EQ_7pC+1}7#)rWL9%Rv7J&<6bN#=?iL|+jG{t^IE8(j?x_h>6_0?3sBz&@c zdHW4??eX5SUbr1%n67RluJufy$(hE>c|J6PeBBlOmS*)DJzD-~(-Gf{Kj~_g54bI+ zvs2mB{^rHhv^nw{?M#)E^m}UN(8&wGVx#)IF{U3rfq-`Qf7>;ge%X&W6Rm{`12eiw z7AJ}q!Q>@dPf7w|Z9`686!>)e^MU`OS^=9&r1z+K>B~r8aUA9G@%C_A{wKM-$D;gk z>JNGchwp}r#pV^U^SWi6HpsCpK0T-9Mqa9f{fWUS6^kWx`jr)0C;qUD>37y`1=h{a zUKP?%cDgNY%}$o#Xg6dEufm`|*#am*xaLkx-Z^Te_Z(2&EV8v*%ML;mTO&QVR-7!y zx=cii6^6UmsD&j{@RVd;9N10N{(IE9Y%U(IvaM0Z!-q&S<5YZ3t0#d)5Wp;z@USxf zlcEFsrFE1&Oyvbhk6x|X??WF!M^j{5Dxsi z$z&3Yn?)b_3hog1F>OEZu{8zTQrRaP7tM7`?eJOjGarIKj%^9v?%&6m|DE6<5v!YCa8`-NWnf+u6TXg z7^TRg{f5+g>Q#^KC)0&l3k7^75hr{9u7F5cmm&&qS=C=04TPjICEi;n@@(G-$1N}e zTX^?*zQdgf8M1reMDvB-7g51CgjD;;a*A1PXXoE#e2=eMPi zKXpEGzV6G#b=yF|9&s&>JnPk~YNyoa`;L}2R&9%8U;U-MUsohmm_Zo!sEJ;DRa8E+ z*i$qkBlZIhy7=niy1J?ZKZ~PmpCv8s{d-+;8E5(9$Dqn$)Z6wmshl#2|4p3)TEBvO z-p#G`fad~8n)i`^!*>?j^eKed6zb-R72{_to%)Y)@1-OH7|kj>C&no%!P&Ad-@=o0 zd!tyV5{!3UHc2Jg+csp{WnQdMcXW0xI*OUe(|gEzrbhZdDK_2s;abrV=^;SRMD?`F z=@2y|I`m-%mn8p&n(s3M@e;EM21mMPbPEq~HzOK|b0_9%3FIo-?2;D$D9s%)%wUIV zy1(Gva_LmD*G9(nmlfwn!3KE+{+9jdo^II7$(q{EOKbAD?XR1cIc|&luGs}S-~HkF z>3VUe>VRCQN#w`+o&K`h!ilprh_WUv=}d_XvAPj5lc#sR%Zz*uyL^x_ia+eDvifGj z^Gu$-qloj@kfz7Vg$f*)ut-@~CoMO+a^j;I(pgbY(Qv3SkHNwt#v(Bg`Ve?)rlxj2 zuO9r^bt%TVXId<>fayP8I*-X{gZa@8DtXr6$6ymwoXNI?)>pzriP26jw%0q_oMWXfyqy&QbEbo>!xo zhClp=%Zs07-`$pKv>N#)^#CsPTLONh+^Gp`!%#kjc7F851MlU7a-VOe!UYx)dZi}8 z#(0Ug6!QtK15Qa`f1Z-&&=;17N5~knTIKt~KF_WWS6OOcNsK=UQ*k(p>D+;LDi^

b?wQV!!CZ$Z-$11^Or4LrS=3wHUOJLzB|BIzp~3+b4a;K z8ba!`{$1iZF1>DSVJ7(cO|oxjW3O4O`!5{-%q-*4t?aQ=kFj>cX;ROvtiMeRBe=vf z6VrpNR1?kD69m@q=~<6N_NX0a%CEPW-leA1@oq<9a=gm`r$Zey>#Iasyp3jh1R47& zM*!i}!iDU@Wq&>ly44cB)Zle!C|HXg;}JU#qRdHi=(>3_;AD+Uou7RTG5!zoItwf{ z1btJcNo2|#;n~XBo>!YZ>0gNU2)iqqW5_S*#vvx`WxQJ#p#PQT{f8vOK=+Xbx=8Us zZBC?2?6Bo!GnWvjtSr3*;EdHwn?kPzns{|amTU7n!hZv`Jg&xDzr6$Ql=R9d9Ohh= z>1qaUKYGehOXFAEU{F)Bg?)Lkz9{vr+_U3)*K@37@-%dAz8usO(Oz zAU?T1Ql|N$25u50f4}{YaE?Ms<3Q2xM7>W@U6kDLl5{qravztHYK29|lCkS@>-w;? zsg0X|dEWKyGpW-tC_CN2rP|SD*0s4s!C);^{pPe`myerZzYJ->c$D4au0<3s>z9r2 z4YLPqUacq3&|%`(^lSKMRW{^)U$4!{2*w-%+T}34l{0#Bp65;35lx1>f`3WRlWj7) zu<6oWu19MwT$?u!67vaxg>#?lXT8|`j}vDawnV+s7%$uj`$$js2H&EZPx@+f?MeYO z2lsG=>8Y;6zQp6Q`~PivmA@kS_N_Oz5bgGL?b~h~*-%;=LZ6vIq{iDQ2i>57zKG84 zkA0^Vb0F6S%jOB#UW9n)MFullTOpOnqZRM#y!M5#F6yR+6}zUB!z#5u#Y(QWKetMx zR&GuN?cXhJ@Daa*0X=4kpQ_mSP?wmJ@pWLNnGFrOYP{>Az`w&9{--s0tM_i%hhuR| zEiGYrdgb^-+fJi#a8BxvXPrWiw`jeLXI#l<#in7KAu}?G?#=~b>Yi^B=%r>BzT8VC zT8R!=!ag&DOP6n4d^+q$`-xb_6Xe0(H*EBbmk9*-{g-&P>EymAmOSJ?zHq!D<#1$V z5|=RguyE&(&Zt(A6GW$IImlF_e4gK(Q6P4DpAC3QZ|<42~QO@K8JY7RvY4Kw-pxTy4>xaeP1meH==LOIJfgQFh;lnWo}Zz5I_*irz` z!}agL>1Z;fV_ew2HINq+r93ma3?sxtAy+7o7b{`Mxt!tg7Dd0T3fu~w6(tl#Jmgi? z{U*KEJ3cWIt7?Mr>Z61veaNVJ3lBp7VD0Y{Ltg}!})^3 zGf&Th6EiB`d7M<|6a6Ln2gb)|UWoej@J{Sl{eryWE9AF)k_sh6%U8?FGcDmsjO|Nm ze3=N1#2{Uh#9m*l7*MxDu^hK7$!%|=Dt3SDrXZqGx7gzJ7bw7uoyjO_xwc#OuTSL{ zMzPON^R%$;HRsKySmmQ;q07390rkuup7U&ppYnVOmV>%yp4-Jch4uwS$xqZLtOpeM zOXEX$p2ewIcj`{KC$|@Ri?7f9c4KNe7imB)3^b8$2X?z|)$JU3I~kR|mb;DR>hS4X z_D|z%TrOBl)j-X>2b1#LHxJvUSyAfK@J=q)GZSL0(`GYYRZXkVO@j_ECtjT1k;n|< zM01c*%{7f?%2%wMLrLVjsMozQQ1n8}+pBFfLASJZ)uPg3UiWOP^`gq}LOU~F-2txB z5%r#VG~MUTt*~2`E-s@@kQ)D-j3o}R2xoFHBxWt+MsdhLtD+Sy3gpWt^~J?%4{ct| ztfCm2H^s}z28y;_OeGMWUhoFwYg=|6Xh>ids%xNx)Jy`D8R~94zi{h3jVWO_e=xr6 zBGb@nKI6L^rmTfSQ?|9=ayQ8rpw&NaRq1w?chp$5WY{E%Jz(X?qzIC?b2X8$T>*E-$OXi`OwHR{ z#W2#hqn9rnfzigY(C@=1TQp9$@|hOT-M5B*M)a%LIXTtP$R8rtv9tU_jj#Cl3O6!f z``DlpFU*4PQR9H1!0k1VCkEFdR5fIQ0Qh7z<62<*u`Mlf*$U4hlm@V9bxTt9r=BuUFF$vJ_W|3(1QkrH3&qRfKQ3~G zXR`k?jzxJ|uJB7fgBvq)?W(5EQEx8T%XqptyYLO4Lt(D9*7wdh^6;*8uA3lNH7Y&~ zeb%Z>vRY_aRoh(fR!m#khDhmPX3e`L|Gtx0*1LwQ*lttl@hNwuG^MYSTY53gg%$UL z9WRFg2i{y0iE%O4Tf>cX+h4I81tJs)Yz}hghiphT4=0*XJEjYvi_){I+r#&ZYMGWL z;vew2(sfs;&)%tO!A0X1eCuQ`=BM0nBM=VA*_FfIdZY81;*e7h3mLTZD%}rJw0zKx z!$BT7;jsh!n&6NHq9ap(DXVk`a1{9F)Sc+_L_}&gXj+ZA>Xt!8sQ2=tQvy0xn_F7k zQ(M_O7p74y0r?)j4SS9baS2ynT?7NhUS8MI)8?EKs}Xr2vq$RhPI-c}d9s(g@(%tE z=bWf#A6GASvHMSicZKUVGJsT=4O7_T3M;LJ%(D-(xZe?q7QIqbNbOKS^Pe>Ras#mZ zVQr;I(rxkV#mmr-53EgL(SsklhZ9X;+pczK$GC-W7O4;^az(mrKL(kH!>T%eb2wzq z(<(Adn)XuG^#D+uh=Xve3Sj2R@B{Y;k`h`cLbg$i-QGQ=(k9Q6zkPqOCy#;12@q|l zOnMzmFbHdiZ<%`Mn%i{IaI8aV!~3g?YolN87E-DsjLH2#U+4SN1(W?BPn% zTrzuu_4i)jNDrGE!o-S4?ItG@8S=T^!3Rsx)U%bYIh09{(756{!oW=2>u6T;)k`G_ z%7apQEz`?JPdDUfmJ35*8dCWQ+%H-bWJoeloV>iyBjWHc!vGOqoZ$o0I}Pgs`M=%& zqG}*x<%J9?VDcN_>Nl*$R?;ug^JN)KWEiA-wSDR8g$vcJ2k#5@pOD(U8xQu`W7@vH zhB11Fkvm8%9&ybsHVtL&?sFCCoA141quR=AKlkBi{-ki}LxIR7!wgw0ecspDsAGf= zHn%QhgO54}jOmR%d27e6H~(2|AkeXb0ygJM;~^LAh|P-pjPlH)DQADbr=rh>P-J<{ z`Kqyp7vV{l>0y?T9u6>_&DNlS*KtsQwsFv<;qq1OGo$(s!}=9(bj%i$|-A;7N_zrO3!H(;ZFpU%2;rrb@RJ{R+En}#x2_J;^-b&7k9gi`63<; ziv1${iM73d2X2Z(SB(h!64aSm_1Hjri6TE(Ua+tA9;|#j_K@RsfM11BVy!k3QUja- z8=$Xq`~N;%*-1@Ab2_<;(QR z>B(LyqBM4a$LEw3>)s`zUsMCtu9dx~N`jyvAJ}U@etHYF%7{dWzXgAquUIf@fRWp(zgp8x)vH{uh`4Z|76C9U*|h- zTKfR-%k(nOgtXH&>;-xc7`HYXRC1P5a*5Enug5Zrn>8=X^R7fUJNq7!w5`4zgGx-w z5&$8bx1+V+;P|!zNEdgV**;BNN}a7H-^-WGQMC-IF7E(YTY3c5yX^axU-R7viDVK= z;EGJ_H7siyzvWyKNJe9?gCEEwWT(}76c}N$XjFPWWcpi<3@?fRv5pNV(}sD=O+BXd zTMf9z%k=asC)1J`yS~^`QGk&VdT5)7rlymnBymH`!@wdVA)y#T&Sw0rR}Tt z??v2PM#Syr>)sN7f+~@>Jm7d1xMS`ondMW#KfknP{#kUoT-<1kquE|#tb@Bn-tY=H z+g*!F0n6#)yr>Wsq+7y4jnwYL5q_t}1XH+t>v|hI*tmHysh*Sj6E!G(Ch{%lFlDbR zQ=6}ty%Y>?!$9$T4jL_SV9R~qLF)TBL9HW8g|Ts)m`p7rSvZ2*W45{1Hw6h$|CzTz zbp5JjaB4OQt3*3FR6x_KH=Z>|c%MnYG033GEd2#~o<9tPx9n}IyLG&j6-5cPYHev4ILw3iE1A*`9Hx$v~ic7;vQ{w}d%Vud8arBdCk=D*6;o~7P$V4W^S zdvE6@&V;%$erdB5YJT#%zC6XRgn`q-jS?@1X3To6Q#s zrqSjWbB+6x6kCZ<+DTQRssx(pE;EUGA=^7l7qT%Q02fK6hWlG;r%|v<$FTq`JDE8FVQ12a zt%w^S$H|Fw+x#2uHq%YNA-3f!zEhqxSgG2FY`#~+K>{MkOp>SizXcPZ%U3>RhP*V< zvJx+WOMUV}l&4#S^U$y-yPZwLD6bbGsOL;<6wV$VUySdJrdr3S+3F{*x0L>yNxsOh zekq-Tn^Jj{#j@er6L%BT_#XPbr*g%0m_5(;E57CWTD#u%n&1ge|NBDwy95jjC>_E> z8pu_fy$btum%Z$c96;hMFmhRKP--|_kiGY^oQOE8d&t z>z+qcf;lMZ<{M6xU>>9lqsZFKM9-}5leWnq-kI9z4y*>=FN+{w@wleY9WGHNSVOk`H4qtMtSC*3i>@m0w4<`{UqwE*&Pcx z%aFa88z$##@_jS`#}*BQC2($5#hAF0wg7iEdU@F%vd9l`Eo>G_SDTNapG!iwI=AR} zC6x#Ck85xA7cFrV1zi5Pz`|QAMx+VZ6oZ@XE*icw?2CO6le(37t?lQ41HIf5P|FT_ zgN&?Rf#*hNU)j4~KfQ>h*s*lEj|sON&kWm__4bi;h#nbh-e(PS{y#rLK>HUqOltV7 z;Qp}WK(PW{oOYpdn&Hc*WN+sw-d@eTor^TVYuyL3fn4EOE!BF~5lX~8ok5D735#I& zk`%&~cWV{?oTinKUTh5OUIJhSMX`raztguz33V+x`>p7JhK(cD4HO!rc&bW3nY3~k zqfl-{Mu1Y`Gl3F;k|R`Ez+30W?PaUo1c4(IIsi^`9CqSiB>ZDQpnM12R5ssiN}Y)x7JEW|6p=%`WRTb!rWL=`i|n}alZ z>w&i;vO-0=WeE~3XWx%{EuhMLF|kA;A^iBn1ZQ$v)2ZhOWh46Ak_2Xg|2V zna;ex!~K26Om6c2UBF8mld5-zX=yX#d)B#9+-iYOfFo?ZE`&x-Amf`d`#|wO@Ii9x zx-owlC_MPsHie?Yz0JWq=Z5=>`-){jSttv!0Dgu9Xf6!z!{NVMYLf>KcCP6PMzv%) zk41T{i~{V0nfwQCI&IZ9__PkdNaXc9d}ZvMxts8d#Y$eIKl7?a1ZkzAdPHw3U&$s} z@tv0J`wm4*B?1Cou-cm1IebRIxm~h|XUZ||q>nf@@tB+wZO8)~FdGpAcM=(4s}@8G zmTF?3Ch)4D_;ZbloXesZmY7ordxI9ghRIK#`nLRGciQU>?+sC0QHK>E>VB`^6VK!? zO`a!9&o%|zyK;j2@bqp2Y}(!#ZW1zu~aneT?+=Cy~d; zUVWJPj2fZ?xu8QP_|t`)>k{hVU7&pX%iHt3-`Q(C-KX1^-;w}O6v7&* z@!zh}4}vPd7plmdtu)nwGt61$<~ffhR!m|boJTOW^5~Ace>TykQc7!i^y$>pl$M?$ zkNnjtbkB8cY>M~hY}NrL!)J@PAl-MFpX-X+U_Xv7(&o&nn7m6o{7$!w~4 zltwm%4(yt{v*oSGzdqI)DP1p}^A^?$Ok3lcN$d4=z^irBxJi=;9Q1J@LgC`zf*PMaESy8$z=^hmcDcEtP>Sham|EGk} z(XH90GvC(N3vPW;mYXRKr7@aULp&Qzn32TS^nZc%hZb&E4ubI$n)1elMTMIciflhU z9pL>JQtq1rCRY$GT|KV41-D3<)6ud#h|8-b(B{v_q-I7>S~Jd&E`ah{-)E3wg|OMoLd_&K@T@7X<&wnJ;t4TLWdDwV09Ek9GfwXC;ZugM5o$G2!zB zHT+>|j<9T_lBa82z3pwFT)FCg6Pi&ue*3^hF=Or&EHB!4bTND~J`dNqo92}8B8x8r zTQt9AA$E-{xwvur`4md)E9@Q8tDAi7dmKWkV`CPJJtF1b+%75FeBjPI+y>fj52({W zw(LAqcU^U}Z^Moj1{D9n_`N1KwzQSxgr_``^Iq~xmVkVHGT6d495r)Uj9c`C8;ivE z`7B-xIi$=}_o_=QJ^|CP{@BdV;NH%yvjo@x>y9apRYlU{RRvoI4YTDEk9scdPDg9de-^35gJfvy;C+^FVheJq_Q&pk4#(}h z<$a1Ski(D!%Nou;x41m5fIm}-pCUi$N{2++wB9C$g;6}6PiJ$?m0I=hsq)4w2Q8#m7tCai`50p#Y^OpKemES-XfWN&Vc?me^H@h^pJJ) z$Ge=f#g0o=hOy!C@p*LIf4~mez{R==IiQPe@2#Z^Et_acN}7~`4`$qg74#VOb(Ku@ za0PaE%SkQBYe}50DnI2hGrTO$23}I*J(4M**iSF)dzwX-*ZBMM)^I99XUVfaq(S4i z))vnC(q#49<@PGpopTYf_`G_)W)!!-X7YdjkUfIG=JN^yxu!2vE#3zCJN5=wn9)yG z&C)(VG9OHfYBEv>L8*at=)@OhM2z9n_p>*fOV2&RzgNvoleBxq>%xM{FV97nEUq$G zb(`{YHGV>xufoLBnC5|R&X=za)GPCRo$ilDmdrN22az@Ix%a%}|DZ4GZNf+#kSa`P zeAL#sO?Tu@uvQ;QWgo|#XlT?+c~3&sI_bydU)TZ?_@a9hcy1k~!y_eP*BL_(%MVj` z-_@G6d^LL3D3-oLzd*#O_&u*lRl4KF&w4cd-^g7ZLW%0th8xU;3&>_DGeL#uuV~AE zaCL+jpI_vZ7MF7f1ZA(BTs8+`_dfDD*Q#EgMboZkA?c}wm;7$Tf*g0Z1xQmDYzS2 zO`R@}d?&HcQz>R3XptQ!#i3W&CJF-rzQH8e@x9vC)fGkub@xb}dB;`+Y^xcE*@bR5 zDtLOjBSLF-Er&18v>q%XuWU*{2d|R>^2Qnfufod(Vq#DPflW-x29qUBeyan^y=Bj4 zaw?B=xsImmn$p|qMqsNEWivn6bPNPUVy3D*%D)KK)Y=Z)#jJpc zfG%eY*Hk-T>&)5}VB?MWwk%2;`TgPC$~(GZW?wz#UvS_Ls@>^%yau55lLnx1FnG^o zAewdF3Q>!~D(JQB@=0|qKo-HvyH4KpNuGUm1Y8MF+27y4WuUb|=3=zWDy2cw#CyEb z%55I9;K*L2#Dyxq@TFk9l`}ddygM_z2bOL`-J?R+3s$Ink$Ub;^r^Xdc{Ap`iPOM6 z4>Q_9OSbwFc{YmzRTd`J{NKW>_QHXEu_(>us;)$`o0a*!y?s$$O9ddsdgzBil}&%H zvZof{qw~!Vj%kICgTwv_7w6Nj>j$R$<&At{eu&VpjfK3#uvH_mbE-Lj+Qa&jN}mEw zdEXW0Bk1y_BA7sAL*9zmJ~%sMgMZ0jrsdVt{aN~mJu!Gccxb1+-1)7qn)%q@0Fr(! zyVi&M?-E2KqtQZ+`@b{myy1e$;=Yy0;C)TV5fH`cL`Kt}A}_~lLYRZ+y3V~g8*3%N z{QYeVQ!g&_<+!|;UBjX%srtLAb~{{J%obnzVs-@+5E+Z6{Ob<~PbNxzE_XND-2nnN z8LpveZsW?agWfw;%N%h-vxt-9fs*D_%vzMqv09z2wo3fCj^!-w$CmtLFF?D!Cq z2TZ;P;nJJYVUTA6WYX$0C2@_Rr5255)#HHwBQsxzVjBD+ge-j!E4 zb-kvKY>*9MyQOA0$zrazO!8e>;`m&-g&mY~w>;UI!Zw`icQ<)Cs778EW2dJNsywCY z{&_9RVaK3z^KbyT#BmA!*kKy@!!7_wQj!genII9he$hVyJS*6nX;IJc*`U$-_>tou zR5gWL=ESF_a>ey2St;aACc7-n%{>+nDB1E6Ol-*jB)@DJPi1^m@jO|v!`G#I*?d?CKEl0lB$Fh1^t?p@OH!O-z7}l?J zu2)W2v?Gtueos$7HuyT)8BFswq46@=_MBZeJsKD@K29X$ff9kwuGr1%*5_ zt{q&`+Y;&$x1|2klJ$C?Du8&^Z+67mAEh+#^=7Onvo73-MotOV%f7Q6M?ttSm$KUy z@eP0!T?%BL?b&o(N1SJ%ns3(9!&;CvWso42xjH>LTwiN~fx7xpmr%f1OV82fz^3Uy zKo#Uxp0if^GZRpZd=o11%LcSDyioj3tke}e0V*-h#S_Wb#kNHqE`yZ8-VFa^ptT@AOpT+xlcceOZM z({&6j_!eNk31@HcNjg~THrBKNq`7Hg*^Zq}+cGXBN6$vUDk3>y$XSQto%qxa;^+#wWC;r6y0|3bRG{0Y*2e zUTx85-j-hvlkkV%Ixbo|(?}KtW*k$42CD5$+3yec_EQMOM$OhHvxr4(d2J~)R#@8sKign*C0$?gm&{)UEw@j8GJ&v`Uuam z5rPKK`41uDZf4@0|4Ad)?B9ra?Z$abm@ts4I!S)8IlG-?s6gA&$5#KAy6p~Xy8`3x z^r5Vv0-9L(Yo5>V8T(FPd>nJ}&k06f^{IXTH6GG3UOAY{GuyMMqFq zQIrt*?3=t9--QqokU> zEHCT$sn>eW0yFWDdL`W%@VR+V(k!ES1h%m;NqpCi4#HcZYrh|@I4x7rd?N1cf_;JO z@bI+Gad@KM&%XcdEoJ|=w=l{7QtW656%oqSn^Ap~IMnxpT%k?o#4o5s=|favX`xVZ zI!(7G9@FUU=(<@ebnzbGKi7XCTvl~nL5^JHZt_R=<7Va?yM4=}&)?Zu)bIV^pS>O< zk%rfhuL589?+otTZ*jIu5yqVEdGgSWa%m|;UqG1&CtBttb?e?zP-_bwTjva#;VW}Q zP)Em6U+ZPOlc0pDGeYg|KJp}`ro^a0t2AI4zc+dk4G>D(Z8~Z!(6)luIePVDFE2~IO)^IbL0NW zj5v!i$hCPYGMJ@nU6=Jb{Zjf`AC{x(aQAE!E?AD4026-jpDlzcyWhESo@WD^!hT$w z>dDFiZ^jmFE;5+_2}|Qmx>pV-67OX-;qRe$WX@O%(GC2@wo)2U?kWR$7KXI`@yS5z zGnTlkJp9*udboc%oalsAjo7DfVGx4l*PDG!s#s)Z_)46}R9k=BO2Q;?O;5?s!=0w$ zirQfS0l}RUG1(en6HJbZPn~}_XlQ7-*{WBPU?U;J&uML_ zr=!zy=RrdDwmU9d4Pn_T2F^~(u6-nS*GVg!!6#Dqe`(kyJl{LL`D_P9#JYqBU8<*vo zUH-gThf~O;=GxPJh{o;$Z&!!2S3U zzXQ=)-vt5=t~oorGq6=$Z9F4plvwx$iU>#O4%{5Mu$;8nnL`dNODKq}m9zmqT8W#s zqo>&@9niS;Gp15PR$Ao&r8HtT)mzRyF#~KrqF$73LQxH@Xq<2th zA`p;H0)(RU-a-#W5?TllY6{%o%zOXA{czvs{c@5g`>dUF*4k_D)t5B1w$C%;mU_CU z13^;pq>rPcueMCG(`uGecSG9qBLaVtJU6hTF&p}@?l^X))+<@MX{BGPbR!s;{oH#w zP>7Y6@I@7O5Zkx?#yCUlwE7ZOj?w<-JC+xNVQ9xxyU`(75d#E{$S}Sg8U%T!(|L$M?Y2*y^G7o9zd;qLYTep}M>O801MISsLsP=f6+xW}L0@7o2+tv{loa#yUyMxp|}y) z+%%9a8@Tnz0%m%$m}i%a${|Q`bm1xjnD5j$%;#Vfd58F%NAim?&40@K6OVoo(!%`6 z9@3RRESsNX3FcK1lYK%HgLiaKg_xAd6?2355HzX%+i?_VqhMt7V*>#@#Vn3&K2?RD zI>spojBaeKaG4=z=vVMzSoF$g2A*mMYXoxRc_<0p>=qpt=D&E3jq^rzd9F*_v9A<1 zP$TXNQuG6yQ1Tpujg8H2%Xy|KLN6%iXrU!sQFbqt=lIRo_;~u@4tH;~ALAPQ zafr9EHi}~6rdA&Xq)@A@^Bz;h!dr>!?)~^)X|-zn^OLa8r9#5qM5p4kKNFjq4x@n6 z`?`Ej;eD12r~gn$idJ!zda?}SG}`*9ZC>=grr(;$MLV=306u|Bnk@dzS8P!ISUb@{ zb4wOaqA!(kL>4#NG7N7S!{oxEPp^o^#G+ zX2e?3hS?Lano`*F0sBcJQxlli`^E)_T-ATW+=Lssj=fdcYfK2%`BtQlU28FBJ*?hG zFZIeil5U0Zi#|i(fo)XQKR6 z%~Q9h_l^8+v_PRyuhnrB%!cqDSKCb8b2qcLHk65};?C2?Zf>$SQ;}G1e|xd4y!OjSoDHmL89S=e$9Pj1jiBq4IH5JCoJw&e83cGaM41dDP*;byIa zn(Q|o`I|$9uB;`Df;S=#4izYSqY3@axJyMOJYq1oyo0>6ZpWZoe4)-UIVS&^WyxNh zk^@?|gc|+fQL=u%-z&bXX}}#X03&O9=258UB^lHGir(@~SQa6~b-D%Z zSc{4u@nzY8z3KQ|(U|AC0@yN{K>#1h8Ff%yp8QfzDQJU2By)o&>9(V_glVrcqFS+Z ztOnakj#v{O<1Tf|D9ZvrH%hno*V|ngRS~?bix*TFfdC7ZbCmN_O&8vdZE) zR#WZfPSKUmj!XIuv@=LmQomxInhcJ1f;Q7|_KXsi-QieKs|DF!-QqWN64fERJi!_KPf3A|LB{9{HXUGMMJu4C*y) zII3ao#VI3gT>9PLao)~r2e+7RdklUwE;^<(&~l7;@TGP$Gb#2VCC*3^`$Q#97bNe=HWwK#^x+| zZ80XyJFGaYiQ9zb@H|n_(85h{`NLC;_EU-{zPSK9h3!jInwgdE+@RSqvyE`rgs7?E z|7dFf_)#&RXh&WLxOrv>7gvg}t!>2nyFY5RoauZOT12f%J>}vuOjF!H;7$cP(SDQF z|20@900F>IE`!Lc(j?;oiWx z3AsqhM}q<}`QxhG(<})oiKrQ~Fb8jAfx*K_j_#GsPy!}P*{c~%c(JI-+RcSLdR>;q zYSt6+iMxr*d)cYshr2=IkL}w_9euzTZSd7I9NUt^z1xchF4dZ#?QuZ9D*~!!z7)I1 z4u)y=Ia0;<;vdBxH%lpOJoaPG0^Mt59*p)!^+)4bjdmt_FEckvrp2)~M)-2jmEVkv zJ2J6pGL-)g)wGswoOtAIkZQzne^FuMHQ`_&pn(FLakgyND=x`&vXjqZt96*PqWW5m z2S7K7)B?eqfsebLj0*MA1(Hq1M<5j@?5RN>W6=JwMoM8FmvYj^<>gqkf{2U*wNpi~ zX6PvbJw-xNvhmZOSF`1kW<7H=*4^DC!cJEil*N#OB9w^pBg0g2{Q=DCDjd{x95zoC zkzNl5n+ABJy#*%%6a)CsZr;Oh`5wtgsJAyv8Pr(%iOSIs8Hiqd`<>j!P2gO1+5Iu+ z-IpFGY63mIO@kk*pEI!<#Q3{2QM<=s`~AKF2c*3q1_Sb^3Jnyja$Pbo`<2+l$AkOP zaI@SyZ`I?p2kdM`;@?cw3~gj$wnfhj!{K>MtE%_vSQA4*!fuRia({u>tKBNCe5;fz z)*J6mxKIQ+?3dSf7ud*$SDIeWoN4vB1dC9SOxT|{Kb?@Bc+E#c76^3Gt~uO^x8I5m z@{tE8?>PX$p&Kp@ho2doTHEd#DL2gr)S(JmBrdwIj>edl+HEf4*avdMPL8+lw2u{! z=6jDgv9WWIwYYrJS`#?>}EBW2GhU6M*MSh!k z-|o8s_2hMvl9g$X@*0zEU6CeZrPGGNZY%TwwX2dX=)hukpakvQUeCDD!Emx9>eS88 z*UR6V(k)y!`?nVkXXMeg|4S$Fj;+P5=ciL8`Gt*Y0d-snR8{qHhp(A6hi1f_z~^BoOJBv@Sy&Q$Jdoz&Al{k;O&DuDu4f(s&%scb#MQ$ zd#XINW){+0HZnHmiY_-U2rl*Iz7c;}Ck3VJ`739xt(TM)(|NJO(Ya(1s~hISSTXN`yJ?w8^es zxILtw!nyW9x)MW3h{eIm(p`6~m4k{pndXN-Fa5k_Ey`}G%Xs&&tuA#h@3Z{T3t1A; zT4@N8*WO@TeJg!H5GC7ZRqK;Hjtnz5a$e!x%%<)d)299&CGL7dr73Gqi>5!pUdp(R z1qih@G@tfa@TG01C&1=u*S2t}C#|=-ohk!l>N^5$a`eI+4gMw!;hJ|CQc0O=akO*v z)4#mt$;Tt3bGQFs6_Myv>j>Sl;2ncm?puM$adEwsqm3aW^K<;tbN-2(n_Crp7pMw7 zbW?6S1S79^A3rJvEPS7~>^|vP=fJ$X4+ZTrPNKM1*K7GM`Wr5JF)}gEBtC49h2YxtKeLzmnkKD z1H0T9_k|l$~W44^Hhw1 z^nTQ|C(GgwC(L`>Wn z3lNli(1vvlJW$6)J7KrDDxc$R(9fPUE3i7$pYYOyzC?UGDCj?h^0;f z{8Y{DMJE?BiP=zL7?6Cr78fQ|lj!aQp;SNl3~ihl9^!sa&A^%!md|ySIt_bpM{odX zj1jTy_`l@Xxauz&e#A0iJy2fpE(>@7-mS8m72xZmW-bi*gCDJ50~uMF@mVm_1>$C$ z&)LWAUD(^khgC5tC9 zu=t!_sP!`6PK&bSy1PJRp4bl!fdRnY^T@$SxWg2}r<`$>WCiG2_;~59iR8%K=-vHM z4*hLd;uP?DR}WBBeDV7Tz!BwvOUuz~l<57smPNRk<*`sI_E~b$M=m?q>CW>KGF5p> znFxXAL%M&7*~@u1f6wKZD{L-BUI#zEKX^PN27kUMGwkA_p8j6Ll0@>s9NycO|AAO~ zzkZ0#KI&37eQM$z%YoNym0!CDU|fO>qYHac$;qj&`4l_YKYZHw;vt6m^V~Fts`3I; zoWkE6(%uQqy(!aP2URmCy`}@by}e$}iWg^?x#qcNC-7fCY`7QoCB%q?)nzs8J}b_Z zv~zzxuwut?#sON~LlG^1#z8ln4ZoEozsN>=ym}Hmtg}l*+49Dvhd+H{A)@~vGe5v6 z+a}HuyIa^hVk8Yt;8JT084Mt0^nB}ib!*G=cS9 zx`4UP_~fvy!6S8meW#>z3C$P(so9GpWyOXLw9nEjhivs1*N(HFC=}?a%=G zgWLlRebY$iFnj40oH=Tz?cvEhMOL|w@7X1tjAg@!8hY1kpprsXybh@5L?@z@GIld- zG=mCLQ@XJZrtD$>m2&nxbm*9Ycrkk8VY>!HfgToj0nEQw2JIT5}U?%1vZ1%b*PC0 zRMUWk-?ZRLI>@lMuz#>8(NKy1!Snt~ll^y(L^)IL_UIi663{;)>ok-;O}YT5N?Cj6 zg4Ja^c}%%Xn8kAz+BL`jf|8OagG#AT->uBt);hB#IaJ71lmHx+d=CqkN~P z280ps4n^}P7CzFqpgLPC^^A)1jZ@6hbQ>7qmQ=s!^h)EGQnGX1dC4?--C}CE&6I9& zh`DV_EuZFqB9Q7iX*AyoTcx$w&xGdtZK=sRv|gL}HMkQ2@|VYuhv{x(t_3<84V4JL z!7h5uMZbLoAbL;6qM248uR4(PmCjWm1GQzZQB)5@%Aqwl+1hVvCE;DcW-N+XL(b-A z*p-z{iOV)XrHdm(MSmzABYv-bgi`0&37E+aSuWHckpLFzQwYIt?!| z|6y=$jgwUA=IltnU(xzW_GB+IHiz@OI@SvrK`ZOEW|Z!ubo;7c`N}^gyy%tp_alR6 ze>Q-+fV|6+ALM;2Ob)huQu+sd67&bX#9G}Z>Gov@3zxZSIIL&wL) zGLV8!(_?D~57sw0C<7~gq(G2Kf#TxhrzW^lo4bAl#gX{^X})J;jfbv@;-^2_hc{E- zhLUp>p`I$J$8i5Oqp)Md$n^B|X5h#=5^$*GTcN*IzYze2%(17}?XipmwJAW6a_x~5 zviti1JWiDO%W%rG`$hLY>E0OHw+Lu%K5gNy$^{J^*#Z^qVRdOVuuR9yrdXZ1U&Sua zR2}tpA!wNTvXD+1KCtZ)aHSJ{oFQb<_;^6^(AuKH{v~CA!HTIzfjKxg5u4%B*BHaRSGR0|+mKJthVys(eK{--; z6ZCiwMxuia+UvP9)!Wq%G*m<~m8JIYEApo1G#ZOQaScW(J-MVu#`=;Rq9@OAz%wqP z^N*wVU{sqn?zKWee~=aL341Z%oWpuUr>fU1m?x!ss)_so7s-9;51fff5ODt#>(t>S zwbVJI#HASpk*#QR(Ol1}#TkwO*mxJf>Ub;37_K+1SMg0;6`Loz>xF6I&5tn|;Ytbp zEOa~eUu#b=bI^mynl7)aczkM8KXmi^aN{5%M**C{^%Su2DM-ebsM8#4mg{sa!~7!W?3 z@CM5eTd=Q`&4m z&hfQSma?uu&+Z4BZ1ZPdSp3h)QK_J!qP!d4?12*DFp|jkxL}m|({10^)zw@M4*wN? zNuNPKZN3JI@SJbd0o2rW%{Bm?hZ}zySy(t=*K4b5wV#_6HLYghYY(KDn@jqaO{6po za}<8km$7|#&<`3Bxm2hs{WaVJyLDMP(!<%fWD!#KlH!VuNWHuq4V`z8uHYGq6U#Te zS4#EQ*H1Utxa})|($`LN1TlKU};ee9b>_ObFqpa22@ro^zam^_GH)}Xt;)J>v z=7lzd1aUSbc*}dvfq)Js4WC1!yw(do3*1o3-j4puT4F?rZQbeeTi@_jxNmz zP<17jb;lp`TGu+5SjXb*>q(J0u4+{XAl87x+>n6c;fXVCXa|$xA41`x)n8z#i+vSk zh2=JN4-?j2yiNDDFoo&w^qrJtJ019C>t!owvKOLsr#G3(f<3llGee3Bak`rYvAv%Kl zqy)#XNhBaDk59Kc-C>Al-k_ulb2(+ZiV@T2#~Ey<$V(zV^z6zV9=a)LplsnHrJow~ zxAQCT>$&R%KSo%X=d$Visb~ui5B){kR8a7f9%28sa>4Ta#{rg!Z}2EZ9~k7}v^*le zsbrKMG$YU#DN`2AM`=K_r&T|I53uBPUENzCB;hbzY>+HFY663+l36f6O9mf%2OqJ~ z=wyqnECT6CZNqg{~lA3BKe5#lphIR4W$O00I(CRc;t6j+^_GS#JPo@ zV)s>sz$>>8e*BmP1F0&-L)qi}*WzB&P9(Vh-yYGwiaI!&VWT2T%(jO);b#T`@>}_D z-xt{DU-_~&xwc$VqngobYbWRrI-=IZ+pS-@;}+=%`I0ghbj>xDMK7XIn&-`0iaK34H-R5>~vzvt^^NLWyNgo11cRfs+KpEwS zp6kB8uSMS<^K@A%3zq%Jsa$S?HYHGQw)t3+m=t>|Gs^{O_ z1OSPO8uldd*Ib}hMm!utvm4LL%Y*nO6kjIYN6X%#wnR>~sB)QhB;+Sv@A;xh=nE{p zBHh%iFOYmd;<$tBemSI#oU*rw*C;65W)=MORP`+J>f$O@Ag@ugRZxJ}kAilV*~h4s zeqO6uPkxI<=}f{<;3oNfzW{B04WrF5N#173;%KtCOtWa>S(ha1+|hw@%R zOLhHTleIC4JX^$j=Lml?9I+nFGXqwMJT^-D*e?u(-J121{yvGDS}08>OfG9QVQJ&j zHCs+#JWTh5Fc)^J-78U2STv>bx@ z9x_ar&kV`C_7|ak3p;(MBT4x(}%4zqxzey7VGiP%_b zfi0SyFVA^1ykMty7&pL=YFHt1i~9d+i>D);_MalVl(o*Lrlq_&4;i`$lbQU$G7#~& zsQujxHQHz9moR%<+#RyM#1X{@usWQ0k9`7k1Rb(CeLeK3AvXLXyaGAIe3yBOAxv#C z>-W~OuzDV{2P*JpMHe~fMyq(Vm)}m;Rf@-WN0+3XGF=H4mHCwVW$rMdVB2`d_h|KG zBqqv_>w0Ph91$AF(ibO{bSio! zwDZoVI8?k6(Y%5@H!J~$-g?4TCn3VVBgoP*qZ z&mwy-0;^nhxc%?lK~z;m6kzK3djIC?g+m6yKpbfM_FnQlFS7-zDd+=OP4)~mjeX~d zi*1nNn*sF;hiV*%KgqG)${+R>{vCZ!9ToV#Y%;-TB&nRCqICq_X(Pro`AjOG0sFo` z2a)jQ^6uI*XE9eh?v*Y@-k8Y@Ig98%b6<Vh{KVf zD6LccWy~he*-t^~7xvypg4o|t++P3oe7E=kR<*9JUJMpR(u7LznVj83BYHDN-)Uav z_jj4vXP##{<7bQJ_ec0|>QD)NW?iJAx%xYjlji29-%mpGTUI-hnST$esQiAA|A72E z!@nT^nc-iMe`oj?OHOYwgk^xwNd{f}+`tJqfRpSSt{ z;Lr>|&85hlWhBtt_~oM*J?H!" + propertyTags[i].getElementsByTagName('pageTitle')[0].firstChild.nodeValue + linkEnd; + $('#ul001').append(full_li_element); + $('#ul001 .defaultLink').css('display','none'); + + } + } + + if(propertyTags[i].getElementsByTagName('pageType')[0].firstChild.nodeValue == 'Article'){ + articleCount++; + + for (var j=0; j< propertyTags[i].getElementsByTagName('pageWords').length; j++){ + full_li_element = linkStart + propertyTags[i].getElementsByTagName('pageUrl')[j].firstChild.nodeValue; + full_li_element =full_li_element + "'>" + propertyTags[i].getElementsByTagName('pageTitle')[0].firstChild.nodeValue + linkEnd ; + + $('#ul002').append(full_li_element); + $('#ul002 .defaultLink').css('display','none'); + + } + } + if(propertyTags[i].getElementsByTagName('pageType')[0].firstChild.nodeValue == 'Example'){ + exampleCount++; + + + for (var j=0; j< propertyTags[i].getElementsByTagName('pageWords').length; j++){ + full_li_element = linkStart + propertyTags[i].getElementsByTagName('pageUrl')[j].firstChild.nodeValue; + full_li_element =full_li_element + "'>" + propertyTags[i].getElementsByTagName('pageTitle')[0].firstChild.nodeValue + linkEnd ; + + $('#ul003').append(full_li_element); + $('#ul003 .defaultLink').css('display','none'); + + } + } + if(i==propertyTags.length){$('#pageType').removeClass('loading');} + + } + if(lookupCount > 0){$('#ul001 .menuAlert').remove();$('#ul001').prepend('

  • Found ' + lookupCount + ' hits
  • ');$('#ul001 li').css('display','block');$('.sidebar .search form input').removeClass('loading');} + if(articleCount > 0){$('#ul002 .menuAlert').remove();$('#ul002').prepend('
  • Found ' + articleCount + ' hits
  • ');$('#ul002 li').css('display','block');} + if(exampleCount > 0){$('#ul003 .menuAlert').remove();$('#ul003').prepend('
  • Found ' + articleCount + ' hits
  • ');$('#ul003 li').css('display','block');} + + if(lookupCount == 0){$('#ul001 .menuAlert').remove();$('#ul001').prepend('
  • Found no result
  • ');$('#ul001 li').css('display','block');$('.sidebar .search form input').removeClass('loading');} + if(articleCount == 0){$('#ul002 .menuAlert').remove();$('#ul002').prepend('
  • Found no result
  • ');$('#ul002 li').css('display','block');} + if(exampleCount == 0){$('#ul003 .menuAlert').remove();$('#ul003').prepend('
  • Found no result
  • ');$('#ul003 li').css('display','block');} + // reset count variables; + lookupCount=0; + articleCount = 0; + exampleCount = 0; + +} +//build regular expression object to find empty string or any number of blank +var blankRE=/^\s*$/; +function CheckEmptyAndLoadList() +{ + var pageUrl = window.location.href; + var pageVal = $('title').html(); + $('#feedUrl').remove(); + $('#pageVal').remove(); + $('.menuAlert').remove(); + $('#feedform').append(''); + $('#feedform').append(''); + $('.liveResult').remove(); + $('.defaultLink').css('display','block'); + var value = document.getElementById('pageType').value; + if((blankRE.test(value)) || (value.length < 3)) + { + //empty inputbox + // load default li elements into the ul if empty + // loadAllList(); // replaced + $('.defaultLink').css('display','block'); + // $('.liveResult').css('display','none'); + }else{ + $('.defaultLink').css('display','none'); + } +} +/* +$(window).resize(function(){ +if($(window).width()<400) + $('body').addClass('offline'); +else + $('body').removeClass('offline'); + }); + */ +// Loads on doc ready + $(document).ready(function () { + //alert(pageUrl); + //$('#pageUrl').attr('foo',pageUrl); + var pageTitle = $('title').html(); + var currentString = $('#pageType').val() ; + if(currentString.length < 1){ + $('.defaultLink').css('display','block'); + CheckEmptyAndLoadList(); + } + + $('#pageType').keyup(function () { + var searchString = $('#pageType').val() ; + if ((searchString == null) || (searchString.length < 3)) { + $('#pageType').removeClass('loading'); + $('.liveResult').remove(); + $('.searching').remove(); + CheckEmptyAndLoadList(); + $('.report').remove(); + // debug$('.content').prepend('
  • too short or blank
  • '); // debug + return; + } + if (this.timer) clearTimeout(this.timer); + this.timer = setTimeout(function () { + $('#pageType').addClass('loading'); + $('.searching').remove(); + $('.list ul').prepend(''); + $.ajax({ + contentType: "application/x-www-form-urlencoded", + url: 'http://' + location.host + '/nokiasearch/GetDataServlet', + data: 'searchString='+searchString, + dataType:'xml', + type: 'post', + success: function (response, textStatus) { + + $('.liveResult').remove(); + $('.searching').remove(); + $('#pageType').removeClass('loading'); + $('.list ul').prepend(''); + processDigiaData(response); + + } + }); + }, 500); + }); + }); diff --git a/doc/templates/scripts/narrow.js b/doc/templates/scripts/narrow.js new file mode 100644 index 00000000..35c81bf4 --- /dev/null +++ b/doc/templates/scripts/narrow.js @@ -0,0 +1,89 @@ +var narrowInit = function() { + /* TODO: + Could probably be more efficient, not hardcoding each element to be created + */ + // 1: Create search form + var narrowSearch = $('
    '); + var searchform = $("#qtdocsearch"); + narrowSearch.append(searchform); + $("#qtdocheader .content .qtref").after(narrowSearch); + + // 2: Create dropdowns + var narrowmenu = $('
      '); + + // Lookup + var lookuptext = $("#lookup h2").attr("title"); + $("#lookup ul").removeAttr("id"); + $("#lookup ul li").removeAttr("class"); + $("#lookup ul li").removeAttr("style"); + var lookupul = $("#lookup ul"); + var lookuplist = $('
    • '); + var lookuplink = $('
      '); + lookuplink.append(lookuptext); + lookuplist.append(lookuplink); + lookuplist.append(lookupul); + narrowmenu.append(lookuplist); + + // Topics + var topicstext = $("#topics h2").attr("title"); + $("#topics ul").removeAttr("id"); + $("#topics ul li").removeAttr("class"); + $("#topics ul li").removeAttr("style"); + var topicsul = $("#topics ul"); + var topicslist = $('
    • '); + var topicslink = $(''); + topicslink.append(topicstext); + topicslist.append(topicslink); + topicslist.append(topicsul); + narrowmenu.append(topicslist); + + // Examples + var examplestext = $("#examples h2").attr("title"); + $("#examples ul").removeAttr("id"); + $("#examples ul li").removeAttr("class"); + $("#examples ul li").removeAttr("style"); + var examplesul = $("#examples ul"); + var exampleslist = $('
    • '); + var exampleslink = $(''); + exampleslink.append(examplestext); + exampleslist.append(exampleslink); + exampleslist.append(examplesul); + narrowmenu.append(exampleslist); + + $("#shortCut").after(narrowmenu); + $('ul#narrowmenu').superfish({ + delay: 100, + autoArrows: false, + disableHI: true + }); +} + +$(document).ready(function(){ +/* if ($('body').hasClass('narrow')) { + narrowInit(); + } + */ + if($(window).width()<600) { + $('body').addClass('narrow'); + + if ($("#narrowsearch").length == 0) { + narrowInit(); + } + } + else { + $('body').removeClass('narrow'); + } +}); + +$(window).bind('resize', function () { + if($(window).width()<600) { + $('body').addClass('narrow'); + + if ($("#narrowsearch").length == 0) { + narrowInit(); + } + } + else { + $('body').removeClass('narrow'); + } +}); \ No newline at end of file diff --git a/doc/templates/scripts/superfish.js b/doc/templates/scripts/superfish.js new file mode 100644 index 00000000..c6a9c7de --- /dev/null +++ b/doc/templates/scripts/superfish.js @@ -0,0 +1,121 @@ + +/* + * Superfish v1.4.8 - jQuery menu widget + * Copyright (c) 2008 Joel Birch + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * CHANGELOG: http://users.tpg.com.au/j_birch/plugins/superfish/changelog.txt + */ + +;(function($){ + $.fn.superfish = function(op){ + + var sf = $.fn.superfish, + c = sf.c, + $arrow = $([' »'].join('')), + over = function(){ + var $$ = $(this), menu = getMenu($$); + clearTimeout(menu.sfTimer); + $$.showSuperfishUl().siblings().hideSuperfishUl(); + }, + out = function(){ + var $$ = $(this), menu = getMenu($$), o = sf.op; + clearTimeout(menu.sfTimer); + menu.sfTimer=setTimeout(function(){ + o.retainPath=($.inArray($$[0],o.$path)>-1); + $$.hideSuperfishUl(); + if (o.$path.length && $$.parents(['li.',o.hoverClass].join('')).length<1){over.call(o.$path);} + },o.delay); + }, + getMenu = function($menu){ + var menu = $menu.parents(['ul.',c.menuClass,':first'].join(''))[0]; + sf.op = sf.o[menu.serial]; + return menu; + }, + addArrow = function($a){ $a.addClass(c.anchorClass).append($arrow.clone()); }; + + return this.each(function() { + var s = this.serial = sf.o.length; + var o = $.extend({},sf.defaults,op); + o.$path = $('li.'+o.pathClass,this).slice(0,o.pathLevels).each(function(){ + $(this).addClass([o.hoverClass,c.bcClass].join(' ')) + .filter('li:has(ul)').removeClass(o.pathClass); + }); + sf.o[s] = sf.op = o; + + $('li:has(ul)',this)[($.fn.hoverIntent && !o.disableHI) ? 'hoverIntent' : 'hover'](over,out).each(function() { + if (o.autoArrows) addArrow( $('>a:first-child',this) ); + }) + .not('.'+c.bcClass) + .hideSuperfishUl(); + + var $a = $('a',this); + $a.each(function(i){ + var $li = $a.eq(i).parents('li'); + $a.eq(i).focus(function(){over.call($li);}).blur(function(){out.call($li);}); + }); + o.onInit.call(this); + + }).each(function() { + var menuClasses = [c.menuClass]; + if (sf.op.dropShadows && !($.browser.msie && $.browser.version < 7)) menuClasses.push(c.shadowClass); + $(this).addClass(menuClasses.join(' ')); + }); + }; + + var sf = $.fn.superfish; + sf.o = []; + sf.op = {}; + sf.IE7fix = function(){ + var o = sf.op; + if ($.browser.msie && $.browser.version > 6 && o.dropShadows && o.animation.opacity!=undefined) + this.toggleClass(sf.c.shadowClass+'-off'); + }; + sf.c = { + bcClass : 'sf-breadcrumb', + menuClass : 'sf-js-enabled', + anchorClass : 'sf-with-ul', + arrowClass : 'sf-sub-indicator', + shadowClass : 'sf-shadow' + }; + sf.defaults = { + hoverClass : 'sfHover', + pathClass : 'overideThisToUse', + pathLevels : 1, + delay : 800, + animation : {opacity:'show'}, + speed : 'normal', + autoArrows : true, + dropShadows : true, + disableHI : false, // true disables hoverIntent detection + onInit : function(){}, // callback functions + onBeforeShow: function(){}, + onShow : function(){}, + onHide : function(){} + }; + $.fn.extend({ + hideSuperfishUl : function(){ + var o = sf.op, + not = (o.retainPath===true) ? o.$path : ''; + o.retainPath = false; + var $ul = $(['li.',o.hoverClass].join(''),this).add(this).not(not).removeClass(o.hoverClass) + .find('>ul').hide().css('visibility','hidden'); + o.onHide.call($ul); + return this; + }, + showSuperfishUl : function(){ + var o = sf.op, + sh = sf.c.shadowClass+'-off', + $ul = this.addClass(o.hoverClass) + .find('>ul:hidden').css('visibility','visible'); + sf.IE7fix.call($ul); + o.onBeforeShow.call($ul); + $ul.animate(o.animation,o.speed,function(){ sf.IE7fix.call($ul); o.onShow.call($ul); }); + return this; + } + }); + +})(jQuery); diff --git a/doc/templates/style/narrow.css b/doc/templates/style/narrow.css new file mode 100644 index 00000000..de5b0a09 --- /dev/null +++ b/doc/templates/style/narrow.css @@ -0,0 +1,270 @@ + /* start narrow mode */ + + body.narrow + { + background-image: none; + } + + .narrow a { + color: #44a51c; + } + + .narrow .header, .narrow .header .content, .narrow .footer, .narrow .wrapper { + margin: 0 7px; + min-width: 300px; + } + + .narrow .footer { + margin: 0px; + } + + .creator .header, .creator .header .content, .creator .footer, .creator .wrapper { + margin: 0px; + min-width: 300px; + } + .narrow .header + { + width: 100%; + margin: 0; + height: auto; + background: #fff url(../images/header_bg.png) repeat-x 0 100%; + padding: 10px 0 5px 0; + overflow: visible; + } + + .narrow .header .content + { + } + + .narrow .header #nav-logo + { + display: none; + } + + .narrow .header .qtref + { + width: auto; + height: auto; + color: #363534; + position: static; + float: left; + margin-left: 25px; + font: bold 18px/1 Arial; + } + + .narrow .header .qtref a + { + color: #00732F; + } + + .narrow .header .qtref span + { + background-image: none; + text-indent: 0; + } + + .narrow .header #nav-topright + { + display: none; + } + + .narrow .header #shortCut + { + clear: both; + font-weight: normal; + position: static; + float: left; + margin: 15px 0 0 25px; + overflow: hidden; + padding: 0; + height: auto; + } + + .narrow .header #shortCut ul + { + float: none; + margin: 0; + width: auto; + font-size: 11px; + } + + .narrow .header #shortCut ul li + { + background-image: none; + } + + .narrow .header #shortCut ul .shortCut-topleft-active, + .narrow .header #shortCut ul .shortCut-topleft-inactive + { + background-image: none; + height: auto; + padding: 0; + width: auto; + } + .narrow .header #shortCut ul li a + { + color: #00732F; + } + + .narrow .wrapper .hd + { + background: url(../images/bg_ul_blank.png) no-repeat 0 0; + } + + .narrow .wrapper .bd + { + background: url(../images/bg_l_blank.png) repeat-y 0 0; + } + + .narrow .wrapper .ft + { + background: url(../images/bg_ll_blank.png) no-repeat 0 0; + } + + .narrow .sidebar + { + display: none; + } + + .narrow .wrap + { + margin: 0 5px 0 5px; + } + + .creator .wrap + { + margin: 0px; + background:#FFFFFF; + } + .narrow .wrap .toolbar + { + border-bottom: none; + } + + .narrow .wrap .content + { + padding-top: 15px; + } + .creator .wrap .content + { + padding-top: 10px; + } + .creator .wrap .content .guide + { + padding-top: 15px; + } + .narrow .wrap .feedback + { + display: none; + } + + .narrow .wrap .breadcrumb ul li { + font-weight: normal; + } + + .narrow .wrap .breadcrumb ul li a { + color: #44a51c; + } + + .narrow .wrap .breadcrumb ul li.last a { + color: #363534; + } + + #narrowsearch { + display: none; + } + + .narrow #narrowsearch { + display: block; + float: right; + margin-right: 25px; + _position: relative; + } + + .narrow #narrowsearch fieldset { + _position: absolute; + _margin-top: -1px; + } + + .narrow #narrowsearch { + background: url("http://doc.qt.nokia.com/prototype/html/images/sprites-combined.png") no-repeat scroll -6px -348px transparent; + height: 21px; + padding: 2px 0 0 5px; + width: 167px; + } + + .narrow #narrowsearch input { + border: none; + font: 13px/1.2 Verdana; + height: 19px; + outline: none; + padding: 0; + width: 158px; + *border: 1px solid #fff; + *height: 17px; + _height: 18px; + /* to be fixed */ + display: none; + /* to be fixed */ + } + + .narrow .indexbox .indexIcon { + display: none; + } + + .narrow .indexboxcont .section { + width: 64%; + padding-left: 0; + } + + .narrow .indexboxcont .sectionlist { + width: 32.5%; + } + + #narrowmenu { + display: none; + float: right; + margin: 15px 40px 0 0; + font-size: 11px; + } + + .narrow #narrowmenu { + display: block; + } + + #narrowmenu a { + line-height: 1.1; + background: url(../images/arrow_down.png) no-repeat 100% 50%; + white-space: nowrap; + padding: 0 16px 0 5px; + } + + #narrowmenu li { + margin-left: 20px; + } + + #narrowmenu li li { + margin: 0 0 5px 0; + } + + #narrowmenu li li a { + padding: 0; + background-image: none; + } + + #narrowmenu li, + #narrowmenu li ul { + background-color: #fff; + margin-top:-1px; + } + + #narrowmenu li ul { + width: auto; + padding: 5px; + } + + .sf-menu li:hover ul, .sf-menu li.sfHover ul { + top: 1.2em; + } + + /* end narrow mode */ diff --git a/doc/templates/style/offline.css b/doc/templates/style/offline.css new file mode 100644 index 00000000..731f9a07 --- /dev/null +++ b/doc/templates/style/offline.css @@ -0,0 +1,632 @@ +body{ +font: normal 400 14px/1.2 Arial; +margin-top:85px; +font-family: Arial, Helvetica; +color:#313131; +text-align:justify; +margin-left:5px; +margin-right:5px; +} + +img{ +-moz-box-shadow: 3px 3px 3px #ccc; +-webkit-box-shadow: 3px 3px 3px #ccc; +box-shadow: 3px 3px 3px #ccc; +border:#8E8D8D 2px solid; +margin-left:0px; +max-width: 800px; +height: auto +} + +b{ +font-weight:600; +} + +.content{} + +.descr{ +margin-top:35px; +/*max-width: 75%;*/ +margin-left:5px; +text-align:justify; +min-height:700px; +vertical-align:top; +} + +.name{ +max-width: 75%; +font-weight:100; +} + +tt{ +text-align:left;} + +/* +----------- +links +----------- +*/ + +a:link{ +color: #2C418D; +text-decoration: none; +text-align:left; +} + +a:hover{ +color: #869CD1; +text-decoration:underline; +text-align:left; +} + +a:visited{ +color: #869CD1; +text-decoration: none; +text-align:left; +} + +a:visited:hover{ +text-decoration:underline; +text-align:left; +} + +a[href*="http://"], a[href*="ftp://"],a[href*="https://"] +{ +text-decoration: none; +background-image:url(../images/ico_out.png); +background-repeat:no-repeat; +background-position:left; +padding-left:20px; +text-align:left; +} + +/*a[href*="http://qt.nokia.com/doc/"], a[href*="http://doc.qt.nokia.com/"] +{ +background: none; +padding-left: 0px; +text-align:left; +}*/ + +.flags{ +text-decoration:none; +text-height:24px; +} + +/* +------------------------------- +NOTE styles +------------------------------- +*/ +.notetitle, .tiptitle, .fastpathtitle{ +font-weight:bold; +} + +.attentiontitle,.cautiontitle,.dangertitle,.importanttitle,.remembertitle,.restrictiontitle{ +font-weight:bold; +} + +.note,.tip,.fastpath{ +background: #F2F2F2 url(../images/ico_note.png); +background-repeat: no-repeat; +background-position: top left; +padding:5px; +padding-left:40px; +padding-bottom:10px; +border:#999 1px dotted; +color:#666666; +margin:5px; +} + +.attention,.caution,.danger,.important,.remember,.restriction{ +background: #F2F2F2 url(../images/ico_note_attention.png); +background-repeat:no-repeat; +background-position:top left; +padding:5px; +padding-left:40px; +padding-bottom:10px; +border:#999 1px dotted; +color:#666666; +margin:5px; +} + +/* +------------------------------- +Top navigation +------------------------------- +*/ + +.header{ + +height:1px; +padding:0px; +margin:0px; +} + +.qtref{ +display: block; +position: relative; +top: -76px; +height:15px; +z-index: 1; +font-size:11px; +padding-right:10px; +float:right; +} + +.naviNextPrevious{ +display: block; +position: relative; +text-align: right; +top: -53px; +float:right; +height:20px; +z-index:1; +padding-right:10px; +padding-top:2px; +vertical-align:top; +margin:0px; +} + + +.naviNextPrevious > a:first-child{ +background-image:url(../images/btn_prev.png); +background-repeat:no-repeat; +background-position:left; +padding-left:20px; +height:20px; +padding-left:20px; +} + +.naviNextPrevious > a:last-child{ +background-image:url(../images/btn_next.png); +background-repeat:no-repeat; +background-position:right; +padding-right:20px; +height:20px; +margin-left:30px; +} + +.breadcrumb{ +display: block; +position: relative; +top:-20px; +/*border-top:2px solid #ffffff;*/ +border-bottom: 1px solid #cecece; +background-color:#F2F2F2; +z-index:1; +height:20px; +padding:0px; +margin:0px; +padding-left:10px; +padding-top:2px; +margin-left:-5px; +margin-right:-5px; +} + +.breadcrumb ul{ + margin:0px; + padding:0px; +} + +.breadcrumb ul li{ +background-color:#F2F2F2; +list-style-type:none; +padding:0; +margin:0; +height:20px; +} + +.breadcrumb li{ +float:left; +} + +.breadcrumb .first { +background:url(../images/home.png); +background-position:left; +background-repeat:no-repeat; +padding-left:20px; +} + + +.breadcrumb li a{ +color:#2C418D; +display:block; +text-decoration:none; +background:url(../images/arrow.png); +background-repeat:no-repeat; +background-position:right; +padding-right:25px; +padding-left:10px; +} + +.breadcrumb li a:hover{ +color:#909090; +display:block; +text-decoration:none; +background:url(../images/arrow.png); +background-repeat:no-repeat; +background-position:right; +padding-right:20px; +padding-left:10px; +} + + +/* table of content +no display +*/ + +/* +----------- +headers +----------- +*/ + + +@media screen{ +.title{ +color:#313131; +font-size: 18px; +font-weight: normal; +left: 0; +padding-bottom: 20px; +padding-left: 10px; +padding-top: 20px; +position: absolute; +right: 0; +top: 0; +background-color:#E6E6E6; +border-bottom: 1px #CCC solid; +border-top: 2px #CCC solid; +font-weight:bold; +margin-left:0px; +margin-right:0px; +} +} + +h1 { +margin: 0; +} + +h2, p.h2 { +font: 500 16px/1.2 Arial; +font-weight:100; +background-color:#F2F3F4; +padding:4px; +margin-bottom:30px; +margin-top:30px; +border-top:#E0E0DE 1px solid; +border-bottom: #E0E0DE 1px solid; +max-width: 99%; +} + +h3{ + +font: 500 14px/1.2 Arial; +font-weight:100; +text-decoration:underline; +margin-bottom:30px; +margin-top:30px; +} + +h3.fn,span.fn{ +border-width: 1px; +border-style: solid; +border-color: #E6E6E6; +-moz-border-radius: 7px 7px 7px 7px; +-webkit-border-radius: 7px 7px 7px 7px; +border-radius: 7px 7px 7px 7px; +background-color: #F6F6F6; +word-spacing: 3px; +padding: 5px 5px; +text-decoration:none; +font-weight:bold; +max-width:75%; +font-size:14px; +margin:0px; +margin-top:30px; + +} + +.name{ +color:#1A1A1A; +} +.type{ +color:#808080; +} + + + +@media print { +.title { +color:#0066CB; +font-family:Arial, Helvetica; +font-size: 32px; +font-weight: normal; +left: 0; +position: absolute; +right: 0; +top: 0; +} +} + + +/* +----------------- +table styles +----------------- +*/ +.table img { +border:none; +margin-left:0px; +-moz-box-shadow:0px 0px 0px #fff; +-webkit-box-shadow: 0px 0px 0px #fff; +box-shadow: 0px 0px 0px #fff; +} + +/* table with border alternative colours*/ + + table,pre{ +-moz-border-radius: 7px 7px 7px 7px; +-webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; +background-color: #F6F6F6; +border: 1px solid #E6E6E6; +border-collapse: separate; +font-size: 12px; +line-height: 1.2; +margin-bottom: 25px; +margin-left: 15px; +font-size: 12px; +line-height: 1.2; +margin-bottom: 25px; +margin-left: 15px; +} + + +table th{ +text-align:left; +padding-left:20px; +} + +table td { +padding: 3px 15px 3px 20px; +border-bottom:#CCC dotted 1px; +} +table p { margin:0px;} + +table tr.even { +background-color: white; +color: #66666E; +} + +table tr.odd { +background-color: #F6F6F6; +color: #66666E; +} + + +table thead { +text-align:left; +padding-left:20px; +background-color:#e1e0e0; +border-left:none; +border-right:none; +} + +table thead th { +padding-top:5px; +padding-left:10px; +padding-bottom:5px; +border-bottom: 2px solid #D1D1D1; +padding-right:10px; +} + + +/* table bodless & white*/ + +.borderless { +border-radius: 0px 0px 0px 0px; +background-color: #fff; +border: 1px solid #fff; +} + +.borderless tr { +background-color: #FFF; +color: #66666E; +} + +.borderless td { +border:none; +border-bottom:#fff dotted 1px; +} + +/* +----------- +List +----------- +*/ + +ul{ +padding-bottom:2px; +} + +li ul { +padding-top: 10px; +} +li { +margin-bottom: 10px; +padding-left: 8px; +list-style:outside; +list-style-type:square; +text-align:left; +} + + +ol{ +margin:10px; +padding:0; +} + +ol > li{ +margin-left: 30px; +padding-left:8px; +list-style:decimal; +} + +.centerAlign{ +text-align: left; +} + +.cpp{ +display: block; +margin: 10; +overflow: hidden; +overflow-x: hidden; +overflow-y: hidden; +padding: 20px 0 20px 0; +} + +.footer{ +margin-top: 50px; +padding-left:5px; +margin-bottom: 10px; +font-size:10px; +border-top: 1px solid #999; +padding-top:11px; +} + +.footerNavi{ +width:auto; +text-align:right; +margin-top:50px; +z-index:1; +} + +.memItemLeft{ +padding-right: 3px; +} + +.memItemRight{ +padding: 3px 15px 3px 0; +} + +.qml{ +display: block; +margin: 10; +overflow: hidden; +overflow-x: hidden; +overflow-y: hidden; +padding: 20px 0 20px 0; +} + +.qmldefault{ +padding-left: 5px; +float: right; +color: red; +} + +.qmlreadonly{ +padding-left: 5px; +float: right; +color: #254117; +} + +.rightAlign{ +padding: 3px 5px 3px 10px; +text-align: right; +} + +/* +----------- +Content table +----------- +*/ + +@media print{ +.toc { +float: right; +padding-bottom: 10px; +padding-top: 50px; +width: 100%; +background-image:url(../images/bgrContent.png); +background-position:top; +background-repeat:no-repeat; +} +} + +@media screen{ +.toc{ +clear:both; +float:right; +vertical-align:top; +-moz-border-radius: 7px 7px 7px 7px; +-webkit-border-radius: 7px 7px 7px 7px; +border-radius: 7px 7px 7px 7px; + background:#FFF url(../images/bgrContent.png); +background-position:top; +background-repeat:repeat-x; +border: 1px solid #E6E6E6; +padding-left:5px; +padding-bottom:10px; +height: auto; +width: 200px; +text-align:left; +z-index:2; +margin-left:20px; +margin-right:20px; +margin-top:0px; +padding-top:0px; +} +} + +.toc h3{ +text-decoration:none; +} + +.toc h3{font: 500 14px/1.2 Arial; +font-weight:100; +padding:0px; +margin:0px; +padding-top:5px; +padding-left:5px; +} + + +.toc ul{ +width:160px; +padding-left:10px; +padding-right:5px; +padding-bottom:10px; +padding-top:10px; +} + +.toc ul li{ +margin-left:20px; +list-style-image:url(../images/blu_dot.png); +list-style:outside; + +} + + +.toc ul li a:link{ +color: #2C418D; +text-decoration: none; +} + +.toc ul li a:hover{ +color: #869CD1; +text-decoration:underline; + +} + +.toc ul li a:visited{ +color: #869CD1; +font-weight: bold; +} + +.level1{ +border:none;} + +.clearfix{ +clear:both;} + diff --git a/doc/templates/style/style.css b/doc/templates/style/style.css new file mode 100644 index 00000000..af16d41e --- /dev/null +++ b/doc/templates/style/style.css @@ -0,0 +1,1592 @@ +@media screen +{ + +/* basic elements */ + html + { + color: #000000; + background: #FFFFFF; + } + body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, button, textarea, p, blockquote, th, td + { + margin: 0; + padding: 0; + } + table + { + border-collapse: collapse; + border-spacing: 0; + } + fieldset, img + { + border: 0; + max-width:100%; + } + address, caption, cite, code, dfn, em, strong, th, var, optgroup + { + font-style: inherit; + font-weight: inherit; + } + del, ins + { + text-decoration: none; + } + li + { + list-style: none; + } + ol li + { + list-style: decimal; + } + caption, th + { + text-align: left; + } + h1, h2, h3, h4, h5, h6 + { + font-size: 100%; + } + q:before, q:after + { + content: ''; + } + abbr, acronym + { + border: 0; + font-variant: normal; + } + sup, sub + { + vertical-align: baseline; + } + tt, .qmlreadonly span, .qmldefault span + { + word-spacing:5px; + } + legend + { + color: #000000; + } + input, button, textarea, select, optgroup, option + { + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; + } + input, button, textarea, select + { + font-size: 100%; + } + strong + { + font-weight: bold; + } + em + { + font-style: italic; + } + + /* adding Qt theme */ + html + { + /* background-color: #e5e5e5;*/ + } + body + { + background: #e6e7e8 url(../images/page_bg.png) repeat-x 0 0; + font: normal 13px/1.2 Verdana; + color: #363534; + } + a + { + color: #00732f; + text-decoration: none; + } + hr + { + background-color: #E6E6E6; + border: 1px solid #E6E6E6; + height: 1px; + width: 100%; + text-align: left; + margin: 15px 0px 15px 0px; + } + + pre + { + border: 1px solid #DDDDDD; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + margin: 0 20px 10px 10px; + padding: 20px 15px 20px 20px; + overflow-x: auto; + } + table, pre + { + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + background-color: #F6F6F6; + border: 1px solid #E6E6E6; + border-collapse: separate; + font-size: 11px; + margin-bottom: 25px; + } + pre.highlightedCode { + display: block; + overflow:hidden; + } + thead + { + margin-top: 5px; + font:600 12px/1.2 Arial; + } + th + { + padding: 5px 15px 5px 15px; + background-color: #E1E1E1; + border-left: 1px solid #E6E6E6; + } + td + { + padding: 3px 15px 3px 15px; + } + tr.odd td:hover, tr.even td:hover {} + + td.rightAlign + { + padding: 3px 5px 3px 10px; + } + table tr.odd + { + border-left: 1px solid #E6E6E6; + background-color: #F6F6F6; + color: #66666E; + } + table tr.even + { + border-left: 1px solid #E6E6E6; + background-color: #ffffff; + color: #66666E; + } + table tr.odd td:hover, table tr.even td:hover + { + /* background-color: #E6E6E6;*/ /* disabled until further notice */ + } + + span.comment + { + color: #8B0000; + font-style: italic; + } + span.string, span.char + { + color: #254117; + } + + +/* end basic elements */ + +/* font style elements */ + .heading + { + font: normal bold 16px/1.2 Arial; + padding-bottom: 15px; + } + .subtitle + { + font-size: 13px; + } + .small-subtitle + { + font-size: 13px; + } +/* end font style elements */ + +/* global settings*/ + .header, .footer, .wrapper + { + min-width: 600px; + max-width: 1500px; + margin: 0 30px; + } + .header, .footer + { + display: block; + clear: both; + overflow: hidden; + } + .header:after, .footer:after, .breadcrumb:after, .wrap .content:after, .group:after + { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; + } + +/* end global settings*/ +/* header elements */ + .header + { + height: 115px; + position: relative; + } + .header .icon + { + position: absolute; + top: 13px; + left: 0; + } + .header .qtref + { + position: absolute; + top: 28px; + left: 88px; + width: 302px; + height: 22px; + } + .header .qtref span + { + display: block; + width: 302px; + height: 22px; + text-indent: -999em; + background: url(../images/sprites-combined.png) no-repeat -78px -235px; + } + .content a:visited + { + color: #4c0033; + text-decoration: none; + } + .content a:visited:hover + { + color: #4c0033; + text-decoration: underline; + } + + #nav-topright + { + height: 70px; + } + + #nav-topright ul + { + list-style-type: none; + float: right; + width: 370px; + margin-top: 11px; + } + + #nav-topright li + { + display: inline-block; + margin-right: 20px; + float: left; + } + + #nav-topright li.nav-topright-last + { + margin-right: 0; + } + + #nav-topright li a + { + background: transparent url(../images/sprites-combined.png) no-repeat; + height: 18px; + display: block; + overflow: hidden; + text-indent: -9999px; + } + + #nav-topright li.nav-topright-home a + { + width: 65px; + background-position: -2px -91px; + } + + #nav-topright li.nav-topright-home a:hover + { + background-position: -2px -117px; + } + + + #nav-topright li.nav-topright-dev a + { + width: 30px; + background-position: -76px -91px; + } + + #nav-topright li.nav-topright-dev a:hover + { + background-position: -76px -117px; + } + + + #nav-topright li.nav-topright-labs a + { + width: 40px; + background-position: -114px -91px; + } + + #nav-topright li.nav-topright-labs a:hover + { + background-position: -114px -117px; + } + + #nav-topright li.nav-topright-doc a + { + width: 32px; + background-position: -162px -91px; + } + + #nav-topright li.nav-topright-doc a:hover, #nav-topright li.nav-topright-doc-active a + { + background-position: -162px -117px; + } + + #nav-topright li.nav-topright-blog a + { + width: 40px; + background-position: -203px -91px; + } + + #nav-topright li.nav-topright-blog a:hover, #nav-topright li.nav-topright-blog-active a + { + background-position: -203px -117px; + } + + #nav-topright li.nav-topright-shop a + { + width: 40px; + background-position: -252px -91px; + } + + #nav-topright li.nav-topright-shop a:hover, #nav-topright li.nav-topright-shop-active a + { + background-position: -252px -117px; + } + + #nav-logo + { + background: transparent url(../images/sprites-combined.png ) no-repeat 0 -225px; + left: -3px; + position: absolute; + width: 75px; + height: 75px; + top: 13px; + } + #nav-logo a + { + width: 75px; + height: 75px; + display: block; + text-indent: -9999px; + overflow: hidden; + } + + + .shortCut-topleft-inactive + { + padding-left: 3px; + padding-right: 3px; + background: transparent url( ../images/sprites-combined.png) no-repeat 0px -58px; + height: 20px; + } + .shortCut-topleft-inactive span + { + font-variant: normal; + } + .shortCut-topleft-inactive span a:hover, .shortCut-topleft-active a:hover + { + text-decoration:none; + } + #shortCut + { + padding-top: 10px; + font-weight: bolder; + color: #b0adab; + } + #shortCut ul + { + list-style-type: none; + float: left; + width: 347px; + margin-left: 100px; + } + #shortCut li + { + display: inline-block; + margin-right: 25px; + float: left; + white-space: nowrap; + } + #shortCut li a + { + color: #b0adab; + } + #shortCut li a:hover + { + color: #44a51c; + } + + + +/* end header elements */ +/* content and sidebar elements */ + .wrapper + { + background: url(../images/bg_r.png) repeat-y 100% 0; + } + .wrapper .hd + { + padding-left: 216px; + height: 15px; + background: url(../images/page.png) no-repeat 0 0; + overflow: hidden; + } + + + + + .wrapper .hd span + { + height: 15px; + display: block; + overflow: hidden; + background: url(../images/page.png) no-repeat 100% -30px; + } + .wrapper .bd + { + background: url(../images/bg_l.png) repeat-y 0 0; + position: relative; + } + + + + + .wrapper .ft + { + padding-left: 216px; + height: 15px; + background: url(../images/page.png) no-repeat 0 -75px; + overflow: hidden; + } + + + + + .wrapper .ft span + { + height: 15px; + display: block; + background: url(../images/page.png) no-repeat 100% -60px; + overflow: hidden; + } + .navTop{ + float:right; + display:block; + padding-right:15px; + + + } + + + +/* end content and sidebar elements */ +/* sidebar elements */ + .sidebar + { + float: left; + margin-left: 5px; + width: 200px; + font-size: 11px; + } + + + + + + + .sidebar .searchlabel + { + padding: 0 0 2px 17px; + font: normal bold 11px/1.2 Verdana; + } + + .sidebar .search + { + padding: 0 15px 0 16px; + } + + .sidebar .search form + { + background: url(../images/sprites-combined.png) no-repeat -6px -348px; + height:21px; + padding:2px 0 0 5px; + width:167px; + } + + .sidebar .search form input#pageType + { + width: 158px; + height: 19px; + padding: 0; + border: 0px; + outline: none; + font: 13px/1.2 Verdana; + } + + .sidebar .box + { + padding: 17px 15px 5px 16px; + } + + .sidebar .box .first + { + background-image: none; + } + + .sidebar .box h2 + { + font: bold 16px/1.2 Arial; + padding: 0; + } + .sidebar .box h2 span + { + overflow: hidden; + display: inline-block; + } + .sidebar .box#lookup h2 + { + background-image: none; + } + .sidebar #lookup.box h2 span + { + } + .sidebar .box#topics h2 + { + background-image: none; + } + .sidebar #topics.box h2 span + { + } + .sidebar .box#examples h2 + { + background-image: none; + } + .sidebar #examples.box h2 span + { + } + + .sidebar .box .list + { + display: block; + max-height:200px; + min-height:120px; + overflow-y:auto; + overflow-x:none; + } + .list li a:hover + { + text-decoration: underline; + } + .sidebar .box ul + { + padding-bottom:5px; + padding-left:10px; + padding-top:5px; + } + .sidebar .box ul li + { + padding-left: 12px; + background: url(../images/bullet_gt.png) no-repeat 0 5px; + margin-bottom: 5px; + } + .sidebar .bottombar + { + background: url(../images/box_bg.png) repeat-x 0 bottom; + } + .sidebar .box ul li.noMatch + { + background: none; + color:#FF2A00; + font-style:italic; + } + .sidebar .box ul li.hit + { + background: none; + color:#AAD2F0; + font-style:italic; + } + .sidebar .search form input.loading + { + background:url("../images/spinner.gif") no-repeat scroll right center transparent; + } + +.floatingResult{ + z-index:1; + position:relative; + padding-top:0px; + background-color:white; + border:solid 1px black; + height:250px; + width:600px; + overflow-x:hidden; + overflow-y:auto; +} + + .floatingResult:hover{ + display:block; + } + .floatingResult:hover{ + } + +/* end sidebar elements */ +/* content elements */ + .wrap + { + margin: 0 5px 0 208px; + overflow: visible; + } + + + + + .wrap .toolbar + { + background-color: #fafafa; + border-bottom: 1px solid #d1d1d1; + height: 20px; + position: relative; + } + .wrap .toolbar .toolblock + { + position: absolute; + } + .wrap .toolbar .breadcrumb + { + font-size: 11px; + line-height: 1.2; + padding: 0 0 10px 21px; + height: 10px; + } + .wrap .toolbar .toolbuttons + { + padding: 0 0 10px 21px; + right: 5px; + vertical-align: middle; + overflow: hidden; + } + .wrap .toolbar .toolbuttons .active + { + color: #00732F; + } + .wrap .toolbar .toolbuttons ul + { + float: right; + } + .wrap .toolbar .toolbuttons li + { + float: left; + text-indent: -10px; + margin-top: -5px; + margin-right: 15px; + font-weight: bold; + color: #B0ADAB; + } + + .toolbuttons #print + { + border-left: 1px solid #c5c4c4; + margin-top: 0; + padding-left: 7px; + text-indent: 0; + } + .toolbuttons #print a + { + width: 16px; + height: 16px; + } + + .toolbuttons #print a span + { + width: 16px; + height: 16px; + text-indent: -999em; + display: block; + overflow: hidden; + background: url(../images/sprites-combined.png) no-repeat -137px -311px; + } + + .toolbuttons #smallA + { + font-size: 10pt; + } + .toolbuttons #medA + { + font-size: 12pt; + } + .toolbuttons #bigA + { + font-size: 14pt; + margin-right: 7px; + } + + #smallA:hover, #medA:hover, #bigA:hover + { + color: #00732F; + } + + + .wrap .content + { + padding: 30px; + word-wrap:break-word; + } + + .wrap .breadcrumb ul + { + } + .wrap .breadcrumb ul li + { + float: left; + background: url(../images/breadcrumb.png) no-repeat 0 3px; + padding-left: 15px; + margin-left: 15px; + font-weight: bold; + } + .wrap .breadcrumb ul li.last + { + font-weight: normal; + } + .wrap .breadcrumb ul li a + { + color: #363534; + } + .wrap .breadcrumb ul li.first + { + background-image: none; + padding-left: 0; + margin-left: 0; + } + + + + + .wrap .content ol li { + background:none; + font:normal 10pt/1.2 Verdana; + + margin-bottom:10px; + margin-left:12px; + /*list-style-type:disc;*/ + } + + .wrap .content ol li + { + background:none; + margin-bottom: 10px; + padding-left:0px; + margin-left:52px; + } + + .wrap .content li + { + background: url(../images/bullet_sq.png) no-repeat 0 5px; + font: normal 400 10pt/1.2 Verdana; + margin-bottom: 10px; + padding-left:12px; + } + + .content li:hover {} + + .wrap .content h1 + { + font: bold 18px/1.2 Arial; + } + .wrap .content h2 + { + border-bottom:1px solid #DDDDDD; + font:600 16px/1.2 Arial; + margin-top:15px; + width:100%; + } + .wrap .content h3 + { + font: bold 14px/1.2 Arial; + font:600 16px/1.2 Arial; + margin-top:15px; + width:100%; + } + .wrap .content p + { + line-height: 20px; + padding: 5px; + } + .wrap .content table p + { + line-height: 20px; + /* padding: 0px;*/ + } + .wrap .content ul + { + padding-left: 25px; + padding-top: 10px; + } + .wrap .content ul img { + vertical-align:middle; + } + a:hover + { + color: #4c0033; + text-decoration: underline; + } + .feedback + { + float: none; + position: absolute; + right: 15px; + bottom: 10px; + font: normal 8px/1 Verdana; + color: #B0ADAB; + } + .feedback:hover + { + float: right; + font: normal 8px/1 Verdana; + color: #00732F; + text-decoration: underline; + } + .alphaChar{ + width:95%; + background-color:#F6F6F6; + border:1px solid #E6E6E6; + -moz-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + font-size:12pt; + padding-left:10px; + margin-top:10px; + margin-bottom:10px; + } + .flowList{ + /*vertical-align:top;*/ + /*margin:20px auto;*/ + + column-count:3; + -webkit-column-count:3; + -moz-column-count:3; +/* + column-width:100%; + -webkit-column-width:200px; + -col-column-width:200px; +*/ + column-gap:41px; + -webkit-column-gap:41px; + -moz-column-gap:41px; + + column-rule: 1px dashed #ccc; + -webkit-column-rule: 1px dashed #ccc; + -moz-column-rule: 1px dashed #ccc; + } + + .flowList dl{ + } + .flowList dd{ + /*display:inline-block;*/ + margin-left:10px; + min-width:250px; + line-height: 1.2; + min-width:100%; + + } + + .flowList dd a{ + } + + .wrap .content .flowList p{ + padding:0px; + } + + .content .alignedsummary + { + margin: 15px; + } + + + .qmltype + { + text-align: center; + font-size: 160%; + } + .qmlreadonly + { + padding-left: 5px; + float: right; + color: #254117; + } + + .qmldefault + { + padding-left: 5px; + float: right; + color: red; + } + + .qmldoc + { + } + + *.qmlitem p + { + } + #feedbackBox + { + display: none; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + border: 1px solid #DDDDDD; + position: fixed; + top: 100px; + left: 33%; + height: 230px; + width: 400px; + padding: 5px; + background-color: #e6e7e8; + z-index: 4; + } + #feedcloseX + { + display: inline; + padding: 5px 5px 0 0; + margin-bottom: 3px; + color: #363534; + font-weight: bold; + float: right; + text-decoration: none; + } + + #feedbox + { + display: inline; + width: 370px; + height: 120px; + margin: 0px 25px 10px 15px; + } + #noteHead + { + font-weight:bold; + padding:10px 10px 10px 20px; + } + #feedsubmit + { + display: inline; + float: right; + margin: 4px 32px 0 0; + } + + .note + { + font-size:7pt; + padding-bottom:3px; + padding-left:20px; + } + + #blurpage + { + display: none; + position: fixed; + float: none; + top: 0px; + left: 0px; + right: 0px; + bottom: 0px; + background: transparent url(../images/feedbackground.png) 0 0; + z-index: 3; + } + .toc + { + float: right; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + background-color: #F6F6F6; + border: 1px solid #DDDDDD; + margin: 0 20px 10px 10px; + padding: 20px 15px 20px 20px; + height: auto; + width: 200px; + } + + .toc h3, .generic a + { + font: bold 12px/1.2 Arial; + } + + .generic{ + } + .generic td{ + /* padding:5px;*/ + } + .generic .alphaChar{ + margin-top:5px; + } + + .generic .odd .alphaChar{ + background-color: #F6F6F6; + } + + .generic .even .alphaChar{ + background-color: #FFFFFF; + } + + .alignedsummary{} + .propsummary{} + .memItemLeft{} + .memItemRight{ + padding:3px 15px 3px 0; + } + .bottomAlign{} + .highlightedCode + { + margin:10px; + } + .LegaleseLeft{} + .valuelist{} + .annotated td{ + padding: 3px 5px 3px 5px; + } + .obsolete{} + .compat{} + .flags{} + .qmlsummary{} + .qmlitem{} + .qmlproto{} + .qmlname{} + .qmlreadonly{} + .qmldefault{} + .qmldoc{} + .qt-style{} + .redFont{} + code{} + + .wrap .content .toc ul + { + padding-left: 0px; + } + + .wrap .content .toc h3{ + border-bottom:0px; + margin-top:0px; + } + + .wrap .content .toc h3 a:hover{ + color:#00732F; + text-decoration:none; + } + + + .wrap .content .toc .level2 + { + margin-left: 15px; + } + + .wrap .content .toc .level3 + { + margin-left: 30px; + } + + .content .toc li + { + font: normal 10px/1.2 Verdana; + background: url(../images/bullet_dn.png) no-repeat 0 5px; + } + .relpage + { + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + border: 1px solid #DDDDDD; + padding: 25px 25px; + clear: both; + } + .relpage ul + { + float: none; + padding: 15px; + } + .content .relpage li + { + font: normal 11px/1.2 Verdana; + } + h3.fn, span.fn + { + -moz-border-radius:7px 7px 7px 7px; + -webkit-border-radius:7px 7px 7px 7px; + border-radius:7px 7px 7px 7px; + background-color: #F6F6F6; + border-width: 1px; + border-style: solid; + border-color: #E6E6E6; + font-weight: bold; + word-spacing:3px; + padding:3px 5px; + } + + .functionIndex { + font-size:12pt; + word-spacing:10px; + margin-bottom:10px; + background-color: #F6F6F6; + border-width: 1px; + border-style: solid; + border-color: #E6E6E6; + -moz-border-radius: 7px 7px 7px 7px; + -webkit-border-radius: 7px 7px 7px 7px; + border-radius: 7px 7px 7px 7px; + width:100%; + } + + .centerAlign + { + text-align:center; + } + + .rightAlign + { + text-align:right; + } + + + .leftAlign + { + text-align:left; + } + + .topAlign{ + vertical-align:top + } + + .functionIndex a{ + display:inline-block; + } + +/* end content elements */ +/* footer elements */ + + .footer + { + min-height: 100px; + color: #797775; + font: normal 9px/1 Verdana; + text-align: center; + padding-top: 40px; + background-color: #E6E7E8; + margin: 0; + } +/* end footer elements */ + + + + + /* start index box */ + .indexbox + { + width: 100%; + display:inline-block; + } + + .indexboxcont + { + display: block; + + } + + .indexboxbar + { + background: transparent url(../images/horBar.png ) repeat-x left bottom; + margin-bottom: 25px; + + + } + + .indexboxcont .section + { + display: inline-block; + width: 49%; + *width:42%; + _width:42%; + padding:0 2% 0 1%; + vertical-align:top; + +} + + .indexboxcont .indexIcon + { + width: 11%; + *width:18%; + _width:18%; + overflow:hidden; + +} + +.indexboxcont .section { + float: left; +} + + .indexboxcont .section p + { + padding-top: 20px; + padding-bottom: 20px; + } + .indexboxcont .sectionlist + { + display: inline-block; + vertical-align:top; + width: 32.5%; + padding: 0; + } + .indexboxcont .sectionlist ul + { + margin-bottom: 20px; + } + + .indexboxcont .sectionlist ul li + { + line-height: 12px; + } + + .content .indexboxcont li + { + font: normal bold 13px/1 Verdana; + } + + .indexbox a:hover, .indexbox a:visited:hover + { + color: #4c0033; + text-decoration: underline; + } + + .indexbox a:visited + { + color: #00732f; + text-decoration: none; + } + + .indexbox .indexIcon { + width: 11%; + } + + + .indexbox .indexIcon span + { + display: block; + } + + .indexbox.guide .indexIcon span + { + width: 96px; + height: 137px; + background: url(../images/sprites-combined.png) no-repeat -5px -376px; + padding: 0; + } + + .indexbox.tools .indexIcon span + { + width: 115px; + height: 137px; + background: url(../images/sprites-combined.png) no-repeat -111px -376px; + padding: 0; + } + .indexboxcont:after + { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; + } + + + +/* start of creator spec*/ + .creator + { + margin-left:0px; + margin-right:0px; + padding-left:0px; + padding-right:0px; + } + .creator .wrap .content ol li { + list-style-type:decimal; + + } + .creator .header .icon, + .creator .feedback, + .creator .t_button, + .creator .feedback, + .creator #feedbackBox, + .creator #feedback, + .creator #blurpage, + /*.creator .indexbox .indexIcon span,*/ + .creator .wrapper .hd, +/* .creator .indexbox .indexIcon,*/ + .creator .header #nav-logo, + .creator #offlinemenu, + .creator #offlinesearch, + .creator .header #nav-topright, + .creator .header #shortCut , + .creator .wrapper .hd, + .creator .wrapper .ft, + .creator .sidebar, + .creator .wrap .feedback + { + display:none; + } + + body.creator + { + background: none; + + font: normal 13px/1.2 Verdana; + color: #363534; + background-color: #FAFAFA; + } + + .wrap .content ol li { + + } + + + .creator .header, .footer, .wrapper + { + max-width: 1500px; + margin: 0px; + } + + .creator .wrapper + { + position:relative; + top:5px; + } + .creator .wrapper .bd + { + + background:#FFFFFF; + } + + + .creator .header, .footer + { + display: block; + clear: both; + overflow: hidden; + } + .creator .wrap .content p + + { + line-height: 20px; + padding: 5px; + } + + .creator .header .qtref span + { + background:none; + } + + + + .creator .footer + { + border-top:1px solid #E5E5E5; + height: 50px; + margin:0px; + padding:10px; + } + + .creator .footer p + { + text-align:justify; + max-width:900px; + } + + .creator .wrap + { + + padding:0 5px 0 5px; + margin: 0px; + } + .creator .wrap .toolbar + { + + + border-bottom:1px solid #E5E5E5; + /*width:100%;*/ + margin-left:-5px; + margin-right:-5px; + } + .creator .wrap .breadcrumb ul li a + { + /* color: #363534;*/ + color: #00732F; + } + + .creator .wrap .content + { + padding: 0px; + word-wrap:break-word; + } + + .creator .wrap .content ol li { + background:none; + font: inherit; + padding-left: 0px; + } + + .creator .wrap .content .descr ol li { + margin-left: 45px; + + } + .creator .content .alignedsummary + { + margin: 5px; + width:100%; + } + .creator .generic{ + max-width:75%; + } + .creator .generic td{ + /* padding:0;*/ + } + .creator .indexboxbar + { + border-bottom:1px solid #E5E5E5; + margin-bottom: 25px; + background: none; + } + + + + .creator .header + { + width: 100%; + margin: 0; + height: auto; + background-color: #ffffff; + padding: 10px 0 5px 0; + overflow: visible; + border-bottom: solid #E5E5E5 1px; + z-index:1; + + + + + + + + + /* position:fixed;*/ + } + + + .creator .header .content + { + } + .creator .header .qtref + { + color: #00732F; + position: static; + float: left; + margin-left: 5px; + font: bold 18px/1 Arial; + } + + .creator .header .qtref:visited + { + color: #00732F; + } + .creator .header .qtref:hover + { + color: #00732F; + text-decoration:none; + } + .creator .header .qtref span + { + background-image: none; + text-indent: 0; + text-decoration:none; + } + + + + + + + .creator .wrap .toolbar + { + display:block; + padding-top:0px; + } + + + + .creator .wrap .breadcrumb ul li { + font-weight: normal; + } + + .creator .wrap .breadcrumb ul li a { + /*color: #44a51c;*/ + } + + .creator .wrap .breadcrumb ul li.last a { + /*color: #363534;*/ + } + + .creator #narrowmenu ul + { + border-bottom:solid 1px #E5E5E5; + border-left:solid 1px #E5E5E5; + border-right:solid 1px #E5E5E5; + } + + .creator #narrowmenu li ul { + margin-top:-15px; + } + + + .creator .toc { + margin:10px 20px 10px 10px; + } + + .creator #narrowsearch, .creator #narrowmenu{ + display:none; + } +/* end of creator spec*/ + +} + +/* end of screen media */ + +/* start of print media */ + +@media print +{ + input, textarea, .header, .footer, .toolbar, .feedback, .wrapper .hd, .wrapper .bd .sidebar, .wrapper .ft, #feedbackBox, #blurpage, .toc, .breadcrumb, .toolbar, .floatingResult + { + display: none; + background: none; + } + .content + { + background: none; + display: block; + width: 100%; margin: 0; float: none; + + } +} +/* end of print media */ diff --git a/doc/templates/style/style_ie6.css b/doc/templates/style/style_ie6.css new file mode 100644 index 00000000..16fb8505 --- /dev/null +++ b/doc/templates/style/style_ie6.css @@ -0,0 +1,54 @@ +.indexbox, .indexboxcont, .group { + zoom: 1; + height: 1%; +} + +.sidebar { + margin-left: 3px; + width: 199px; + overflow: hidden; +} + +.sidebar .search form { + position: relative; +} + +.sidebar .search form fieldset { + position: absolute; + margin-top: -1px; +} + +.sidebar .search form input#searchstring { + border: 1px solid #fff; + height: 18px; +} + +.wrap { + zoom: 1; +} + +.content, +.toolbar { + zoom: 1; + margin-left: -3px; + position: relative; +} + +.indexbox { + clear: both; +} + +.indexboxcont .section { + zoom: 1; + float: left; +} + +.indexboxcont .sectionlist { + zoom: 1; + float: left; +} + +.wrap .toolbar .toolbuttons li { + text-indent: 0; + margin-right: 8px; +} \ No newline at end of file diff --git a/doc/templates/style/style_ie7.css b/doc/templates/style/style_ie7.css new file mode 100644 index 00000000..afbff5f8 --- /dev/null +++ b/doc/templates/style/style_ie7.css @@ -0,0 +1,19 @@ +.indexbox, .indexboxcont, .group { + min-height: 1px; +} + +.sidebar .search form input#searchstring { + border: 1px solid #fff; + height: 17px; +} + + +.indexboxcont .section { + zoom: 1; + float: left; +} + +.indexboxcont .sectionlist { + zoom: 1; + float: left; +} diff --git a/doc/templates/style/style_ie8.css b/doc/templates/style/style_ie8.css new file mode 100644 index 00000000..e69de29b diff --git a/doc/templates/style/superfish.css b/doc/templates/style/superfish.css new file mode 100644 index 00000000..2bdaef4d --- /dev/null +++ b/doc/templates/style/superfish.css @@ -0,0 +1,51 @@ +.sf-menu, .sf-menu * { + margin: 0; + padding: 0; + list-style: none; +} +.sf-menu { + line-height: 1.0; +} +.sf-menu ul { + position: absolute; + top: -999em; + width: 10em; /* left offset of submenus need to match (see below) */ +} +.sf-menu ul li { + width: 100%; +} +.sf-menu li:hover { + visibility: inherit; /* fixes IE7 'sticky bug' */ +} +.sf-menu li { + float: left; + position: relative; +} +.sf-menu a { + display: block; + position: relative; +} +.sf-menu li:hover ul, +.sf-menu li.sfHover ul { + left: 0; + top: 2.5em; /* match top ul list item height */ + z-index: 99; +} +ul.sf-menu li:hover li ul, +ul.sf-menu li.sfHover li ul { + top: -999em; +} +ul.sf-menu li li:hover ul, +ul.sf-menu li li.sfHover ul { + left: 10em; /* match ul width */ + top: 0; +} +ul.sf-menu li li:hover li ul, +ul.sf-menu li li.sfHover li ul { + top: -999em; +} +ul.sf-menu li li li:hover ul, +ul.sf-menu li li li.sfHover ul { + left: 10em; /* match ul width */ + top: 0; +} diff --git a/doc/templates/style/superfish_skin.css b/doc/templates/style/superfish_skin.css new file mode 100644 index 00000000..8d84827c --- /dev/null +++ b/doc/templates/style/superfish_skin.css @@ -0,0 +1,83 @@ + +/*** DEMO SKIN ***/ +.sf-menu { + float: left; + margin-bottom: 1em; +} +.sf-menu a { + border-left: 1px solid #fff; + border-top: 1px solid #CFDEFF; + padding: .75em 1em; + text-decoration:none; +} +.sf-menu a, .sf-menu a:visited { /* visited pseudo selector so IE6 applies text colour*/ + color: #13a; +} +.sf-menu li { + background: #BDD2FF; +} +.sf-menu li li { + background: #AABDE6; +} +.sf-menu li li li { + background: #9AAEDB; +} +.sf-menu li:hover, .sf-menu li.sfHover, +.sf-menu a:focus, .sf-menu a:hover, .sf-menu a:active { + background: #CFDEFF; + outline: 0; +} + +/*** arrows **/ +.sf-menu a.sf-with-ul { + padding-right: 2.25em; + min-width: 1px; /* trigger IE7 hasLayout so spans position accurately */ +} +.sf-sub-indicator { + position: absolute; + display: block; + right: .75em; + top: 1.05em; /* IE6 only */ + width: 10px; + height: 10px; + text-indent: -999em; + overflow: hidden; + background: url('../images/arrows-ffffff.png') no-repeat -10px -100px; /* 8-bit indexed alpha png. IE6 gets solid image only */ +} +a > .sf-sub-indicator { /* give all except IE6 the correct values */ + top: .8em; + background-position: 0 -100px; /* use translucent arrow for modern browsers*/ +} +/* apply hovers to modern browsers */ +a:focus > .sf-sub-indicator, +a:hover > .sf-sub-indicator, +a:active > .sf-sub-indicator, +li:hover > a > .sf-sub-indicator, +li.sfHover > a > .sf-sub-indicator { + background-position: -10px -100px; /* arrow hovers for modern browsers*/ +} + +/* point right for anchors in subs */ +.sf-menu ul .sf-sub-indicator { background-position: -10px 0; } +.sf-menu ul a > .sf-sub-indicator { background-position: 0 0; } +/* apply hovers to modern browsers */ +.sf-menu ul a:focus > .sf-sub-indicator, +.sf-menu ul a:hover > .sf-sub-indicator, +.sf-menu ul a:active > .sf-sub-indicator, +.sf-menu ul li:hover > a > .sf-sub-indicator, +.sf-menu ul li.sfHover > a > .sf-sub-indicator { + background-position: -10px 0; /* arrow hovers for modern browsers*/ +} + +/*** shadows for all but IE6 ***/ +.sf-shadow ul { + background: url('../images/shadow.png') no-repeat bottom right; + padding: 0 8px 9px 0; + -moz-border-radius-bottomleft: 17px; + -moz-border-radius-topright: 17px; + -webkit-border-top-right-radius: 17px; + -webkit-border-bottom-left-radius: 17px; +} +.sf-shadow ul.sf-shadow-off { + background: transparent; +} diff --git a/examples/app-and-lib/app/app.qbs b/examples/app-and-lib/app/app.qbs new file mode 100644 index 00000000..ebc16975 --- /dev/null +++ b/examples/app-and-lib/app/app.qbs @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs 1.0 + +Product { + type: "application" + consoleApplication: true + name : "app-and-lib-app" + files : [ "main.cpp" ] + Depends { name: "cpp" } + Depends { name: "app-and-lib-lib" } +} + diff --git a/examples/app-and-lib/app/main.cpp b/examples/app-and-lib/app/main.cpp new file mode 100644 index 00000000..37c7ab03 --- /dev/null +++ b/examples/app-and-lib/app/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + return bla(); +} diff --git a/examples/app-and-lib/app_and_lib.qbs b/examples/app-and-lib/app_and_lib.qbs new file mode 100644 index 00000000..e214055b --- /dev/null +++ b/examples/app-and-lib/app_and_lib.qbs @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs 1.0 + +Project { + references: [ + "app/app.qbs", + "lib/lib.qbs" + ] +} + diff --git a/examples/app-and-lib/lib/lib.cpp b/examples/app-and-lib/lib/lib.cpp new file mode 100644 index 00000000..eacbeaec --- /dev/null +++ b/examples/app-and-lib/lib/lib.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifndef CRUCIAL_DEFINE +# error CRUCIAL_DEFINE not defined +#endif + +int bla() +{ + puts("Hello World!"); + return 2; +} diff --git a/examples/app-and-lib/lib/lib.h b/examples/app-and-lib/lib/lib.h new file mode 100644 index 00000000..1117ef1e --- /dev/null +++ b/examples/app-and-lib/lib/lib.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LIB_H +#define LIB_H + +int bla(); + +#endif // LIB_H diff --git a/examples/app-and-lib/lib/lib.qbs b/examples/app-and-lib/lib/lib.qbs new file mode 100644 index 00000000..0e6679e7 --- /dev/null +++ b/examples/app-and-lib/lib/lib.qbs @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs 1.0 + +Product { + type: "staticlibrary" + name: "app-and-lib-lib" + files: [ + "lib.cpp", + "lib.h", + ] + cpp.defines: ['CRUCIAL_DEFINE'] + Depends { name: 'cpp' } + + Export { + Depends { name: "cpp" } + cpp.includePaths: [product.sourceDirectory] + } +} + diff --git a/examples/cocoa-application/CocoaApplication.qbs b/examples/cocoa-application/CocoaApplication.qbs new file mode 100644 index 00000000..28d00b38 --- /dev/null +++ b/examples/cocoa-application/CocoaApplication.qbs @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs 1.0 + +CppApplication { + Depends { condition: product.condition; name: "ib" } + condition: qbs.targetOS.contains("macos") + name: "Cocoa Application" + + cpp.precompiledHeader: "CocoaApplication/CocoaApplication-Prefix.pch" + + cpp.frameworks: ["Cocoa"] + + Group { + prefix: "CocoaApplication/" + files: [ + "AppDelegate.h", + "AppDelegate.m", + "CocoaApplication-Info.plist", + "CocoaApplication-Prefix.pch", + "main.m" + ] + } + + Group { + name: "Supporting Files" + prefix: "CocoaApplication/en.lproj/" + files: [ + "Credits.rtf", + "InfoPlist.strings", + "MainMenu.xib" + ] + } +} diff --git a/examples/cocoa-application/CocoaApplication.xcodeproj/project.pbxproj b/examples/cocoa-application/CocoaApplication.xcodeproj/project.pbxproj new file mode 100644 index 00000000..a284a776 --- /dev/null +++ b/examples/cocoa-application/CocoaApplication.xcodeproj/project.pbxproj @@ -0,0 +1,309 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 14ABF7A71717761200140DA2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14ABF7A61717761200140DA2 /* Cocoa.framework */; }; + 14ABF7B11717761200140DA2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 14ABF7AF1717761200140DA2 /* InfoPlist.strings */; }; + 14ABF7B31717761200140DA2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 14ABF7B21717761200140DA2 /* main.m */; }; + 14ABF7B71717761200140DA2 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 14ABF7B51717761200140DA2 /* Credits.rtf */; }; + 14ABF7BA1717761200140DA2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 14ABF7B91717761200140DA2 /* AppDelegate.m */; }; + 14ABF7BD1717761300140DA2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 14ABF7BB1717761300140DA2 /* MainMenu.xib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 14ABF7A31717761200140DA2 /* Cocoa Application.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Cocoa Application.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 14ABF7A61717761200140DA2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 14ABF7A91717761200140DA2 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 14ABF7AA1717761200140DA2 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 14ABF7AB1717761200140DA2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 14ABF7AE1717761200140DA2 /* CocoaApplication-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CocoaApplication-Info.plist"; sourceTree = ""; }; + 14ABF7B01717761200140DA2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 14ABF7B21717761200140DA2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 14ABF7B41717761200140DA2 /* CocoaApplication-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CocoaApplication-Prefix.pch"; sourceTree = ""; }; + 14ABF7B61717761200140DA2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; + 14ABF7B81717761200140DA2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 14ABF7B91717761200140DA2 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 14ABF7BC1717761300140DA2 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 14ABF7A01717761200140DA2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 14ABF7A71717761200140DA2 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 14ABF79A1717761200140DA2 = { + isa = PBXGroup; + children = ( + 14ABF7AC1717761200140DA2 /* CocoaApplication */, + 14ABF7A51717761200140DA2 /* Frameworks */, + 14ABF7A41717761200140DA2 /* Products */, + ); + sourceTree = ""; + }; + 14ABF7A41717761200140DA2 /* Products */ = { + isa = PBXGroup; + children = ( + 14ABF7A31717761200140DA2 /* Cocoa Application.app */, + ); + name = Products; + sourceTree = ""; + }; + 14ABF7A51717761200140DA2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 14ABF7A61717761200140DA2 /* Cocoa.framework */, + 14ABF7A81717761200140DA2 /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + 14ABF7A81717761200140DA2 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 14ABF7A91717761200140DA2 /* AppKit.framework */, + 14ABF7AA1717761200140DA2 /* CoreData.framework */, + 14ABF7AB1717761200140DA2 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 14ABF7AC1717761200140DA2 /* CocoaApplication */ = { + isa = PBXGroup; + children = ( + 14ABF7B81717761200140DA2 /* AppDelegate.h */, + 14ABF7B91717761200140DA2 /* AppDelegate.m */, + 14ABF7BB1717761300140DA2 /* MainMenu.xib */, + 14ABF7AD1717761200140DA2 /* Supporting Files */, + ); + path = CocoaApplication; + sourceTree = ""; + }; + 14ABF7AD1717761200140DA2 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 14ABF7AE1717761200140DA2 /* CocoaApplication-Info.plist */, + 14ABF7AF1717761200140DA2 /* InfoPlist.strings */, + 14ABF7B21717761200140DA2 /* main.m */, + 14ABF7B41717761200140DA2 /* CocoaApplication-Prefix.pch */, + 14ABF7B51717761200140DA2 /* Credits.rtf */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 14ABF7A21717761200140DA2 /* Cocoa Application */ = { + isa = PBXNativeTarget; + buildConfigurationList = 14ABF7C01717761300140DA2 /* Build configuration list for PBXNativeTarget "Cocoa Application" */; + buildPhases = ( + 14ABF79F1717761200140DA2 /* Sources */, + 14ABF7A01717761200140DA2 /* Frameworks */, + 14ABF7A11717761200140DA2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Cocoa Application"; + productName = CocoaApplication; + productReference = 14ABF7A31717761200140DA2 /* Cocoa Application.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 14ABF79B1717761200140DA2 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0460; + ORGANIZATIONNAME = "Petroules Corporation"; + }; + buildConfigurationList = 14ABF79E1717761200140DA2 /* Build configuration list for PBXProject "CocoaApplication" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 14ABF79A1717761200140DA2; + productRefGroup = 14ABF7A41717761200140DA2 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 14ABF7A21717761200140DA2 /* Cocoa Application */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 14ABF7A11717761200140DA2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 14ABF7B11717761200140DA2 /* InfoPlist.strings in Resources */, + 14ABF7B71717761200140DA2 /* Credits.rtf in Resources */, + 14ABF7BD1717761300140DA2 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 14ABF79F1717761200140DA2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 14ABF7B31717761200140DA2 /* main.m in Sources */, + 14ABF7BA1717761200140DA2 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 14ABF7AF1717761200140DA2 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 14ABF7B01717761200140DA2 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 14ABF7B51717761200140DA2 /* Credits.rtf */ = { + isa = PBXVariantGroup; + children = ( + 14ABF7B61717761200140DA2 /* en */, + ); + name = Credits.rtf; + sourceTree = ""; + }; + 14ABF7BB1717761300140DA2 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 14ABF7BC1717761300140DA2 /* en */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 14ABF7BE1717761300140DA2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 14ABF7BF1717761300140DA2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + SDKROOT = macosx; + }; + name = Release; + }; + 14ABF7C11717761300140DA2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "CocoaApplication/CocoaApplication-Prefix.pch"; + INFOPLIST_FILE = "CocoaApplication/CocoaApplication-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 14ABF7C21717761300140DA2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "CocoaApplication/CocoaApplication-Prefix.pch"; + INFOPLIST_FILE = "CocoaApplication/CocoaApplication-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 14ABF79E1717761200140DA2 /* Build configuration list for PBXProject "CocoaApplication" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 14ABF7BE1717761300140DA2 /* Debug */, + 14ABF7BF1717761300140DA2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 14ABF7C01717761300140DA2 /* Build configuration list for PBXNativeTarget "Cocoa Application" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 14ABF7C11717761300140DA2 /* Debug */, + 14ABF7C21717761300140DA2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 14ABF79B1717761200140DA2 /* Project object */; +} diff --git a/examples/cocoa-application/CocoaApplication/AppDelegate.h b/examples/cocoa-application/CocoaApplication/AppDelegate.h new file mode 100644 index 00000000..664a0bae --- /dev/null +++ b/examples/cocoa-application/CocoaApplication/AppDelegate.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +@interface AppDelegate : NSObject + +@property (nonatomic, assign) IBOutlet NSWindow *window; + +@end diff --git a/examples/cocoa-application/CocoaApplication/AppDelegate.m b/examples/cocoa-application/CocoaApplication/AppDelegate.m new file mode 100644 index 00000000..8fd5c49c --- /dev/null +++ b/examples/cocoa-application/CocoaApplication/AppDelegate.m @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#import "AppDelegate.h" + +@implementation AppDelegate + +@synthesize window = _window; + +- (void)dealloc +{ + [super dealloc]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *) __unused aNotification +{ + // Insert code here to initialize your application +} + +@end diff --git a/examples/cocoa-application/CocoaApplication/CocoaApplication-Info.plist b/examples/cocoa-application/CocoaApplication/CocoaApplication-Info.plist new file mode 100644 index 00000000..136a7b5e --- /dev/null +++ b/examples/cocoa-application/CocoaApplication/CocoaApplication-Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + org.example.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright © 2014 Petroules Corporation. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/examples/cocoa-application/CocoaApplication/CocoaApplication-Prefix.pch b/examples/cocoa-application/CocoaApplication/CocoaApplication-Prefix.pch new file mode 100644 index 00000000..d7206045 --- /dev/null +++ b/examples/cocoa-application/CocoaApplication/CocoaApplication-Prefix.pch @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifdef __OBJC__ + #import +#endif diff --git a/examples/cocoa-application/CocoaApplication/en.lproj/Credits.rtf b/examples/cocoa-application/CocoaApplication/en.lproj/Credits.rtf new file mode 100644 index 00000000..9caf316e --- /dev/null +++ b/examples/cocoa-application/CocoaApplication/en.lproj/Credits.rtf @@ -0,0 +1,29 @@ +{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} +{\colortbl;\red255\green255\blue255;} +\paperw9840\paperh8400 +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\b\fs24 \cf0 Engineering: +\b0 \ + Some people\ +\ + +\b Human Interface Design: +\b0 \ + Some other people\ +\ + +\b Testing: +\b0 \ + Hopefully not nobody\ +\ + +\b Documentation: +\b0 \ + Whoever\ +\ + +\b With special thanks to: +\b0 \ + Mom\ +} diff --git a/examples/cocoa-application/CocoaApplication/en.lproj/InfoPlist.strings b/examples/cocoa-application/CocoaApplication/en.lproj/InfoPlist.strings new file mode 100644 index 00000000..b92732c7 --- /dev/null +++ b/examples/cocoa-application/CocoaApplication/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ diff --git a/examples/cocoa-application/CocoaApplication/en.lproj/MainMenu.xib b/examples/cocoa-application/CocoaApplication/en.lproj/MainMenu.xib new file mode 100644 index 00000000..faedd463 --- /dev/null +++ b/examples/cocoa-application/CocoaApplication/en.lproj/MainMenu.xib @@ -0,0 +1,4666 @@ + + + + 1080 + 11D50 + 2457 + 1138.32 + 568.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2457 + + + NSWindowTemplate + NSView + NSMenu + NSMenuItem + NSCustomObject + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + NSApplication + + + FirstResponder + + + NSApplication + + + AMainMenu + + + + CocoaApplication + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + CocoaApplication + + + + About CocoaApplication + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Preferences… + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + Services + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide CocoaApplication + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit CocoaApplication + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + submenuAction: + + File + + + + New + n + 1048576 + 2147483647 + + + + + + Open… + o + 1048576 + 2147483647 + + + + + + Open Recent + + 1048576 + 2147483647 + + + submenuAction: + + Open Recent + + + + Clear Menu + + 1048576 + 2147483647 + + + + + _NSRecentDocumentsMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Close + w + 1048576 + 2147483647 + + + + + + Save… + s + 1048576 + 2147483647 + + + + + + Revert to Saved + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Page Setup... + P + 1179648 + 2147483647 + + + + + + + Print… + p + 1048576 + 2147483647 + + + + + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + Edit + + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1179648 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Paste and Match Style + V + 1572864 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Find + + 1048576 + 2147483647 + + + submenuAction: + + Find + + + + Find… + f + 1048576 + 2147483647 + + + 1 + + + + Find and Replace… + f + 1572864 + 2147483647 + + + 12 + + + + Find Next + g + 1048576 + 2147483647 + + + 2 + + + + Find Previous + G + 1179648 + 2147483647 + + + 3 + + + + Use Selection for Find + e + 1048576 + 2147483647 + + + 7 + + + + Jump to Selection + j + 1048576 + 2147483647 + + + + + + + + + Spelling and Grammar + + 1048576 + 2147483647 + + + submenuAction: + + Spelling and Grammar + + + + Show Spelling and Grammar + : + 1048576 + 2147483647 + + + + + + Check Document Now + ; + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Check Spelling While Typing + + 1048576 + 2147483647 + + + + + + Check Grammar With Spelling + + 1048576 + 2147483647 + + + + + + Correct Spelling Automatically + + 2147483647 + + + + + + + + + Substitutions + + 1048576 + 2147483647 + + + submenuAction: + + Substitutions + + + + Show Substitutions + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Smart Copy/Paste + f + 1048576 + 2147483647 + + + 1 + + + + Smart Quotes + g + 1048576 + 2147483647 + + + 2 + + + + Smart Dashes + + 2147483647 + + + + + + Smart Links + G + 1179648 + 2147483647 + + + 3 + + + + Text Replacement + + 2147483647 + + + + + + + + + Transformations + + 2147483647 + + + submenuAction: + + Transformations + + + + Make Upper Case + + 2147483647 + + + + + + Make Lower Case + + 2147483647 + + + + + + Capitalize + + 2147483647 + + + + + + + + + Speech + + 1048576 + 2147483647 + + + submenuAction: + + Speech + + + + Start Speaking + + 1048576 + 2147483647 + + + + + + Stop Speaking + + 1048576 + 2147483647 + + + + + + + + + + + + Format + + 2147483647 + + + submenuAction: + + Format + + + + Font + + 2147483647 + + + submenuAction: + + Font + + + + Show Fonts + t + 1048576 + 2147483647 + + + + + + Bold + b + 1048576 + 2147483647 + + + 2 + + + + Italic + i + 1048576 + 2147483647 + + + 1 + + + + Underline + u + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Bigger + + + 1048576 + 2147483647 + + + 3 + + + + Smaller + - + 1048576 + 2147483647 + + + 4 + + + + YES + YES + + + 2147483647 + + + + + + Kern + + 2147483647 + + + submenuAction: + + Kern + + + + Use Default + + 2147483647 + + + + + + Use None + + 2147483647 + + + + + + Tighten + + 2147483647 + + + + + + Loosen + + 2147483647 + + + + + + + + + Ligatures + + 2147483647 + + + submenuAction: + + Ligatures + + + + Use Default + + 2147483647 + + + + + + Use None + + 2147483647 + + + + + + Use All + + 2147483647 + + + + + + + + + Baseline + + 2147483647 + + + submenuAction: + + Baseline + + + + Use Default + + 2147483647 + + + + + + Superscript + + 2147483647 + + + + + + Subscript + + 2147483647 + + + + + + Raise + + 2147483647 + + + + + + Lower + + 2147483647 + + + + + + + + + YES + YES + + + 2147483647 + + + + + + Show Colors + C + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Copy Style + c + 1572864 + 2147483647 + + + + + + Paste Style + v + 1572864 + 2147483647 + + + + + _NSFontMenu + + + + + Text + + 2147483647 + + + submenuAction: + + Text + + + + Align Left + { + 1048576 + 2147483647 + + + + + + Center + | + 1048576 + 2147483647 + + + + + + Justify + + 2147483647 + + + + + + Align Right + } + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Writing Direction + + 2147483647 + + + submenuAction: + + Writing Direction + + + + YES + Paragraph + + 2147483647 + + + + + + CURlZmF1bHQ + + 2147483647 + + + + + + CUxlZnQgdG8gUmlnaHQ + + 2147483647 + + + + + + CVJpZ2h0IHRvIExlZnQ + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + YES + Selection + + 2147483647 + + + + + + CURlZmF1bHQ + + 2147483647 + + + + + + CUxlZnQgdG8gUmlnaHQ + + 2147483647 + + + + + + CVJpZ2h0IHRvIExlZnQ + + 2147483647 + + + + + + + + + YES + YES + + + 2147483647 + + + + + + Show Ruler + + 2147483647 + + + + + + Copy Ruler + c + 1310720 + 2147483647 + + + + + + Paste Ruler + v + 1310720 + 2147483647 + + + + + + + + + + + + View + + 1048576 + 2147483647 + + + submenuAction: + + View + + + + Show Toolbar + t + 1572864 + 2147483647 + + + + + + Customize Toolbar… + + 1048576 + 2147483647 + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + Window + + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 2147483647 + + + submenuAction: + + Help + + + + CocoaApplication Help + ? + 1048576 + 2147483647 + + + + + _NSHelpMenu + + + + _NSMainMenu + + + 15 + 2 + {{335, 390}, {480, 360}} + 1954021376 + CocoaApplication + NSWindow + + + + + 256 + {480, 360} + + {{0, 0}, {2560, 1418}} + {10000000000000, 10000000000000} + YES + + + AppDelegate + + + NSFontManager + + + + + + + terminate: + + + + 449 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + delegate + + + + 495 + + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + print: + + + + 86 + + + + runPageLayout: + + + + 87 + + + + clearRecentDocuments: + + + + 127 + + + + performClose: + + + + 193 + + + + toggleContinuousSpellChecking: + + + + 222 + + + + undo: + + + + 223 + + + + copy: + + + + 224 + + + + checkSpelling: + + + + 225 + + + + paste: + + + + 226 + + + + stopSpeaking: + + + + 227 + + + + cut: + + + + 228 + + + + showGuessPanel: + + + + 230 + + + + redo: + + + + 231 + + + + selectAll: + + + + 232 + + + + startSpeaking: + + + + 233 + + + + delete: + + + + 235 + + + + performZoom: + + + + 240 + + + + performFindPanelAction: + + + + 241 + + + + centerSelectionInVisibleArea: + + + + 245 + + + + toggleGrammarChecking: + + + + 347 + + + + toggleSmartInsertDelete: + + + + 355 + + + + toggleAutomaticQuoteSubstitution: + + + + 356 + + + + toggleAutomaticLinkDetection: + + + + 357 + + + + saveDocument: + + + + 362 + + + + revertDocumentToSaved: + + + + 364 + + + + runToolbarCustomizationPalette: + + + + 365 + + + + toggleToolbarShown: + + + + 366 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + unhideAllApplications: + + + + 370 + + + + newDocument: + + + + 373 + + + + openDocument: + + + + 374 + + + + raiseBaseline: + + + + 426 + + + + lowerBaseline: + + + + 427 + + + + copyFont: + + + + 428 + + + + subscript: + + + + 429 + + + + superscript: + + + + 430 + + + + tightenKerning: + + + + 431 + + + + underline: + + + + 432 + + + + orderFrontColorPanel: + + + + 433 + + + + useAllLigatures: + + + + 434 + + + + loosenKerning: + + + + 435 + + + + pasteFont: + + + + 436 + + + + unscript: + + + + 437 + + + + useStandardKerning: + + + + 438 + + + + useStandardLigatures: + + + + 439 + + + + turnOffLigatures: + + + + 440 + + + + turnOffKerning: + + + + 441 + + + + toggleAutomaticSpellingCorrection: + + + + 456 + + + + orderFrontSubstitutionsPanel: + + + + 458 + + + + toggleAutomaticDashSubstitution: + + + + 461 + + + + toggleAutomaticTextReplacement: + + + + 463 + + + + uppercaseWord: + + + + 464 + + + + capitalizeWord: + + + + 467 + + + + lowercaseWord: + + + + 468 + + + + pasteAsPlainText: + + + + 486 + + + + performFindPanelAction: + + + + 487 + + + + performFindPanelAction: + + + + 488 + + + + performFindPanelAction: + + + + 489 + + + + showHelp: + + + + 493 + + + + alignCenter: + + + + 518 + + + + pasteRuler: + + + + 519 + + + + toggleRuler: + + + + 520 + + + + alignRight: + + + + 521 + + + + copyRuler: + + + + 522 + + + + alignJustified: + + + + 523 + + + + alignLeft: + + + + 524 + + + + makeBaseWritingDirectionNatural: + + + + 525 + + + + makeBaseWritingDirectionLeftToRight: + + + + 526 + + + + makeBaseWritingDirectionRightToLeft: + + + + 527 + + + + makeTextWritingDirectionNatural: + + + + 528 + + + + makeTextWritingDirectionLeftToRight: + + + + 529 + + + + makeTextWritingDirectionRightToLeft: + + + + 530 + + + + performFindPanelAction: + + + + 535 + + + + addFontTrait: + + + + 421 + + + + addFontTrait: + + + + 422 + + + + modifyFont: + + + + 423 + + + + orderFrontFontPanel: + + + + 424 + + + + modifyFont: + + + + 425 + + + + window + + + + 532 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + + + + + + + + + + + + 19 + + + + + + + + 56 + + + + + + + + 217 + + + + + + + + 83 + + + + + + + + 81 + + + + + + + + + + + + + + + + + 75 + + + + + 78 + + + + + 72 + + + + + 82 + + + + + 124 + + + + + + + + 77 + + + + + 73 + + + + + 79 + + + + + 112 + + + + + 74 + + + + + 125 + + + + + + + + 126 + + + + + 205 + + + + + + + + + + + + + + + + + + + + + + 202 + + + + + 198 + + + + + 207 + + + + + 214 + + + + + 199 + + + + + 203 + + + + + 197 + + + + + 206 + + + + + 215 + + + + + 218 + + + + + + + + 216 + + + + + + + + 200 + + + + + + + + + + + + + 219 + + + + + 201 + + + + + 204 + + + + + 220 + + + + + + + + + + + + + 213 + + + + + 210 + + + + + 221 + + + + + 208 + + + + + 209 + + + + + 57 + + + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + + + 144 + + + + + 129 + + + + + 143 + + + + + 236 + + + + + 131 + + + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 295 + + + + + + + + 296 + + + + + + + + + 297 + + + + + 298 + + + + + 211 + + + + + + + + 212 + + + + + + + + + 195 + + + + + 196 + + + + + 346 + + + + + 348 + + + + + + + + 349 + + + + + + + + + + + + + + 350 + + + + + 351 + + + + + 354 + + + + + 371 + + + + + + + + 372 + + + + + 375 + + + + + + + + 376 + + + + + + + + + 377 + + + + + + + + 388 + + + + + + + + + + + + + + + + + + + + + + + 389 + + + + + 390 + + + + + 391 + + + + + 392 + + + + + 393 + + + + + 394 + + + + + 395 + + + + + 396 + + + + + 397 + + + + + + + + 398 + + + + + + + + 399 + + + + + + + + 400 + + + + + 401 + + + + + 402 + + + + + 403 + + + + + 404 + + + + + 405 + + + + + + + + + + + + 406 + + + + + 407 + + + + + 408 + + + + + 409 + + + + + 410 + + + + + 411 + + + + + + + + + + 412 + + + + + 413 + + + + + 414 + + + + + 415 + + + + + + + + + + + 416 + + + + + 417 + + + + + 418 + + + + + 419 + + + + + 420 + + + + + 450 + + + + + + + + 451 + + + + + + + + + + 452 + + + + + 453 + + + + + 454 + + + + + 457 + + + + + 459 + + + + + 460 + + + + + 462 + + + + + 465 + + + + + 466 + + + + + 485 + + + + + 490 + + + + + + + + 491 + + + + + + + + 492 + + + + + 494 + + + + + 496 + + + + + + + + 497 + + + + + + + + + + + + + + + + + 498 + + + + + 499 + + + + + 500 + + + + + 501 + + + + + 502 + + + + + 503 + + + + + + + + 504 + + + + + 505 + + + + + 506 + + + + + 507 + + + + + 508 + + + + + + + + + + + + + + + + 509 + + + + + 510 + + + + + 511 + + + + + 512 + + + + + 513 + + + + + 514 + + + + + 515 + + + + + 516 + + + + + 517 + + + + + 534 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{380, 496}, {480, 360}} + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 535 + + + + + ABCardController + NSObject + + id + id + id + id + id + id + id + + + + addCardViewField: + id + + + copy: + id + + + cut: + id + + + doDelete: + id + + + find: + id + + + paste: + id + + + saveChanges: + id + + + + ABCardView + NSButton + NSManagedObjectContext + NSSearchField + NSTextField + NSWindow + + + + mCardView + ABCardView + + + mEditButton + NSButton + + + mManagedObjectContext + NSManagedObjectContext + + + mSearchField + NSSearchField + + + mStatusTextField + NSTextField + + + mWindow + NSWindow + + + + IBProjectSource + ./Classes/ABCardController.h + + + + ABCardView + NSView + + id + id + + + + commitAndSave: + id + + + statusImageClicked: + id + + + + NSObjectController + NSImageView + NSView + ABNameFrameView + NSView + NSImage + ABImageView + + + + mBindingsController + NSObjectController + + + mBuddyStatusImage + NSImageView + + + mHeaderView + NSView + + + mNameView + ABNameFrameView + + + mNextKeyView + NSView + + + mUserImage + NSImage + + + mUserImageView + ABImageView + + + + IBProjectSource + ./Classes/ABCardView.h + + + + ABImageView + NSImageView + + id + id + id + id + + + + copy: + id + + + cut: + id + + + delete: + id + + + paste: + id + + + + IBProjectSource + ./Classes/ABImageView.h + + + + DVTBorderedView + DVTLayoutView_ML + + contentView + NSView + + + contentView + + contentView + NSView + + + + IBProjectSource + ./Classes/DVTBorderedView.h + + + + DVTDelayedMenuButton + NSButton + + IBProjectSource + ./Classes/DVTDelayedMenuButton.h + + + + DVTGradientImageButton + NSButton + + IBProjectSource + ./Classes/DVTGradientImageButton.h + + + + DVTImageAndTextCell + NSTextFieldCell + + IBProjectSource + ./Classes/DVTImageAndTextCell.h + + + + DVTImageAndTextColumn + NSTableColumn + + IBProjectSource + ./Classes/DVTImageAndTextColumn.h + + + + DVTLayoutView_ML + NSView + + IBProjectSource + ./Classes/DVTLayoutView_ML.h + + + + DVTOutlineView + NSOutlineView + + IBProjectSource + ./Classes/DVTOutlineView.h + + + + DVTSplitView + NSSplitView + + IBProjectSource + ./Classes/DVTSplitView.h + + + + DVTStackView_ML + DVTLayoutView_ML + + IBProjectSource + ./Classes/DVTStackView_ML.h + + + + DVTTableView + NSTableView + + IBProjectSource + ./Classes/DVTTableView.h + + + + DVTViewController + NSViewController + + IBProjectSource + ./Classes/DVTViewController.h + + + + HFController + NSObject + + selectAll: + id + + + selectAll: + + selectAll: + id + + + + IBProjectSource + ./Classes/HFController.h + + + + HFRepresenterTextView + NSView + + selectAll: + id + + + selectAll: + + selectAll: + id + + + + IBProjectSource + ./Classes/HFRepresenterTextView.h + + + + IBEditor + NSObject + + id + id + id + id + id + + + + changeFont: + id + + + performCopy: + id + + + performCut: + id + + + selectAll: + id + + + sizeSelectionToFit: + id + + + + IBProjectSource + ./Classes/IBEditor.h + + + + IDECapsuleListView + DVTStackView_ML + + dataSource + id + + + dataSource + + dataSource + id + + + + IBProjectSource + ./Classes/IDECapsuleListView.h + + + + IDEDMArrayController + NSArrayController + + IBProjectSource + ./Classes/IDEDMArrayController.h + + + + IDEDMEditor + IDEEditor + + DVTBorderedView + NSView + IDEDMEditorSourceListController + DVTSplitView + + + + bottomToolbarBorderView + DVTBorderedView + + + sourceListSplitViewPane + NSView + + + sourceListViewController + IDEDMEditorSourceListController + + + splitView + DVTSplitView + + + + IBProjectSource + ./Classes/IDEDMEditor.h + + + + IDEDMEditorController + IDEViewController + + IBProjectSource + ./Classes/IDEDMEditorController.h + + + + IDEDMEditorSourceListController + IDEDMEditorController + + DVTBorderedView + IDEDMEditor + DVTImageAndTextColumn + DVTOutlineView + NSTreeController + + + + borderedView + DVTBorderedView + + + parentEditor + IDEDMEditor + + + primaryColumn + DVTImageAndTextColumn + + + sourceListOutlineView + DVTOutlineView + + + sourceListTreeController + NSTreeController + + + + IBProjectSource + ./Classes/IDEDMEditorSourceListController.h + + + + IDEDMHighlightImageAndTextCell + DVTImageAndTextCell + + IBProjectSource + ./Classes/IDEDMHighlightImageAndTextCell.h + + + + IDEDataModelBrowserEditor + IDEDMEditorController + + IDEDataModelPropertiesTableController + IDECapsuleListView + NSArrayController + IDEDataModelPropertiesTableController + IDEDataModelEntityContentsEditor + IDEDataModelPropertiesTableController + + + + attributesTableViewController + IDEDataModelPropertiesTableController + + + capsuleView + IDECapsuleListView + + + entityArrayController + NSArrayController + + + fetchedPropertiesTableViewController + IDEDataModelPropertiesTableController + + + parentEditor + IDEDataModelEntityContentsEditor + + + relationshipsTableViewController + IDEDataModelPropertiesTableController + + + + IBProjectSource + ./Classes/IDEDataModelBrowserEditor.h + + + + IDEDataModelConfigurationEditor + IDEDMEditorController + + IDECapsuleListView + IDEDataModelEditor + IDEDataModelConfigurationTableController + + + + capsuleListView + IDECapsuleListView + + + parentEditor + IDEDataModelEditor + + + tableController + IDEDataModelConfigurationTableController + + + + IBProjectSource + ./Classes/IDEDataModelConfigurationEditor.h + + + + IDEDataModelConfigurationTableController + IDEDMEditorController + + NSArrayController + NSArrayController + IDEDataModelConfigurationEditor + XDTableView + + + + configurationsArrayController + NSArrayController + + + entitiesArrayController + NSArrayController + + + parentEditor + IDEDataModelConfigurationEditor + + + tableView + XDTableView + + + + IBProjectSource + ./Classes/IDEDataModelConfigurationTableController.h + + + + IDEDataModelDiagramEditor + IDEDMEditorController + + XDDiagramView + IDEDataModelEntityContentsEditor + + + + diagramView + XDDiagramView + + + parentEditor + IDEDataModelEntityContentsEditor + + + + IBProjectSource + ./Classes/IDEDataModelDiagramEditor.h + + + + IDEDataModelEditor + IDEDMEditor + + DVTDelayedMenuButton + DVTDelayedMenuButton + NSSegmentedControl + IDEDataModelConfigurationEditor + IDEDataModelEntityContentsEditor + IDEDataModelFetchRequestEditor + NSSegmentedControl + NSTabView + + + + addEntityButton + DVTDelayedMenuButton + + + addPropertyButton + DVTDelayedMenuButton + + + browserDiagramSegmentControl + NSSegmentedControl + + + configurationViewController + IDEDataModelConfigurationEditor + + + entityContentsViewController + IDEDataModelEntityContentsEditor + + + fetchRequestViewController + IDEDataModelFetchRequestEditor + + + hierarchySegmentControl + NSSegmentedControl + + + tabView + NSTabView + + + + IBProjectSource + ./Classes/IDEDataModelEditor.h + + + + IDEDataModelEntityContentsEditor + IDEDMEditorController + + IDEDataModelBrowserEditor + IDEDataModelDiagramEditor + IDEDataModelEditor + NSTabView + + + + browserViewController + IDEDataModelBrowserEditor + + + diagramViewController + IDEDataModelDiagramEditor + + + parentEditor + IDEDataModelEditor + + + tabView + NSTabView + + + + IBProjectSource + ./Classes/IDEDataModelEntityContentsEditor.h + + + + IDEDataModelFetchRequestEditor + IDEDMEditorController + + NSArrayController + IDEDataModelEditor + IDECapsuleListView + + + + entityController + NSArrayController + + + parentEditor + IDEDataModelEditor + + + tableView + IDECapsuleListView + + + + IBProjectSource + ./Classes/IDEDataModelFetchRequestEditor.h + + + + IDEDataModelPropertiesTableController + IDEDMEditorController + + IDEDMArrayController + NSTableColumn + NSArrayController + IDEDataModelBrowserEditor + IDEDMHighlightImageAndTextCell + XDTableView + + + + arrayController + IDEDMArrayController + + + entitiesColumn + NSTableColumn + + + entityArrayController + NSArrayController + + + parentEditor + IDEDataModelBrowserEditor + + + propertyNameAndImageCell + IDEDMHighlightImageAndTextCell + + + tableView + XDTableView + + + + IBProjectSource + ./Classes/IDEDataModelPropertiesTableController.h + + + + IDEDocDownloadsTableViewController + NSObject + + NSButtonCell + DVTTableView + IDEDocViewingPrefPaneController + + + + _downloadButtonCell + NSButtonCell + + + _tableView + DVTTableView + + + prefPaneController + IDEDocViewingPrefPaneController + + + + IBProjectSource + ./Classes/IDEDocDownloadsTableViewController.h + + + + IDEDocSetOutlineView + NSOutlineView + + IBProjectSource + ./Classes/IDEDocSetOutlineView.h + + + + IDEDocSetOutlineViewController + NSObject + + id + id + id + id + id + + + + getDocSetAction: + id + + + showProblemInfoForUpdate: + id + + + subscribeToPublisherAction: + id + + + unsubscribeFromPublisher: + id + + + updateDocSetAction: + id + + + + docSetOutlineView + IDEDocSetOutlineView + + + docSetOutlineView + + docSetOutlineView + IDEDocSetOutlineView + + + + IBProjectSource + ./Classes/IDEDocSetOutlineViewController.h + + + + IDEDocViewingPrefPaneController + IDEViewController + + id + id + id + id + id + id + id + id + id + id + id + + + + addSubscription: + id + + + checkForAndInstallUpdatesNow: + id + + + deleteDocSet: + id + + + downloadAction: + id + + + minimumFontSizeComboBoxAction: + id + + + minimumFontSizeEnabledAction: + id + + + showHelp: + id + + + showSubscriptionSheet: + id + + + subscriptionCancelAction: + id + + + toggleAutoCheckForAndInstallUpdates: + id + + + toggleDocSetInfo: + id + + + + DVTGradientImageButton + DVTGradientImageButton + DVTGradientImageButton + NSSplitView + NSView + NSView + DVTBorderedView + DVTBorderedView + NSButton + NSTextView + IDEDocSetOutlineViewController + IDEDocDownloadsTableViewController + NSComboBox + NSTextField + NSButton + NSTextField + NSWindow + NSButton + + + + _addButton + DVTGradientImageButton + + + _deleteButton + DVTGradientImageButton + + + _showInfoAreaButton + DVTGradientImageButton + + + _splitView + NSSplitView + + + _splitViewDocSetInfoSubview + NSView + + + _splitViewDocSetsListSubview + NSView + + + borderedViewAroundSplitView + DVTBorderedView + + + borderedViewBelowTable + DVTBorderedView + + + checkAndInstallNowButton + NSButton + + + docSetInfoTextView + NSTextView + + + docSetOutlineViewController + IDEDocSetOutlineViewController + + + downloadsTableViewController + IDEDocDownloadsTableViewController + + + minimumFontSizeControl + NSComboBox + + + noUpdatesAvailableMessage + NSTextField + + + showInfoButton + NSButton + + + subscriptionTextField + NSTextField + + + subscriptionWindow + NSWindow + + + validateAddSubscriptionButton + NSButton + + + + IBProjectSource + ./Classes/IDEDocViewingPrefPaneController.h + + + + IDEEditor + IDEViewController + + IBProjectSource + ./Classes/IDEEditor.h + + + + IDEViewController + DVTViewController + + IBProjectSource + ./Classes/IDEViewController.h + + + + IKImageView + + id + id + id + id + + + + copy: + id + + + crop: + id + + + cut: + id + + + paste: + id + + + + IBProjectSource + ./Classes/IKImageView.h + + + + NSDocument + + id + id + id + id + id + id + + + + printDocument: + id + + + revertDocumentToSaved: + id + + + runPageLayout: + id + + + saveDocument: + id + + + saveDocumentAs: + id + + + saveDocumentTo: + id + + + + IBProjectSource + ./Classes/NSDocument.h + + + + NSResponder + + _insertFindPattern: + id + + + _insertFindPattern: + + _insertFindPattern: + id + + + + IBProjectSource + ./Classes/NSResponder.h + + + + QLPreviewBubble + NSObject + + id + id + + + + hide: + id + + + show: + id + + + + parentWindow + NSWindow + + + parentWindow + + parentWindow + NSWindow + + + + IBProjectSource + ./Classes/QLPreviewBubble.h + + + + QTMovieView + + id + id + id + id + id + + + + showAll: + id + + + showCustomButton: + id + + + toggleLoops: + id + + + zoomIn: + id + + + zoomOut: + id + + + + IBProjectSource + ./Classes/QTMovieView.h + + + + WebView + + id + id + id + id + + + + reloadFromOrigin: + id + + + resetPageZoom: + id + + + zoomPageIn: + id + + + zoomPageOut: + id + + + + IBProjectSource + ./Classes/WebView.h + + + + XDDiagramView + NSView + + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + id + + + + _graphLayouterMenuItemAction: + id + + + _zoomPopUpButtonAction: + id + + + alignBottomEdges: + id + + + alignCentersHorizontallyInContainer: + id + + + alignCentersVerticallyInContainer: + id + + + alignHorizontalCenters: + id + + + alignLeftEdges: + id + + + alignRightEdges: + id + + + alignTopEdges: + id + + + alignVerticalCenters: + id + + + bringToFront: + id + + + collapseAllCompartments: + id + + + copy: + id + + + cut: + id + + + delete: + id + + + deleteBackward: + id + + + deleteForward: + id + + + deselectAll: + id + + + diagramZoomIn: + id + + + diagramZoomOut: + id + + + expandAllCompartments: + id + + + flipHorizontally: + id + + + flipVertically: + id + + + layoutGraphicsConcentrically: + id + + + layoutGraphicsHierarchically: + id + + + lock: + id + + + makeSameHeight: + id + + + makeSameWidth: + id + + + moveDown: + id + + + moveDownAndModifySelection: + id + + + moveLeft: + id + + + moveLeftAndModifySelection: + id + + + moveRight: + id + + + moveRightAndModifySelection: + id + + + moveUp: + id + + + moveUpAndModifySelection: + id + + + paste: + id + + + rollDownAllCompartments: + id + + + rollUpAllCompartments: + id + + + selectAll: + id + + + sendToBack: + id + + + sizeToFit: + id + + + toggleGridShown: + id + + + toggleHiddenGraphicsShown: + id + + + togglePageBreaksShown: + id + + + toggleRuler: + id + + + toggleSnapsToGrid: + id + + + unlock: + id + + + + _diagramController + IDEDataModelDiagramEditor + + + _diagramController + + _diagramController + IDEDataModelDiagramEditor + + + + IBProjectSource + ./Classes/XDDiagramView.h + + + + XDTableView + NSTableView + + showAllTableColumns: + id + + + showAllTableColumns: + + showAllTableColumns: + id + + + + IBProjectSource + ./Classes/XDTableView.h + + + + AppDelegate + NSObject + + id + id + + + + applicationShouldTerminate: + id + + + applicationWillFinishLaunching: + id + + + + IBProjectSource + ./Classes/AppDelegate.h + + + + + 0 + IBCocoaFramework + YES + 3 + + {11, 11} + {10, 3} + + YES + + diff --git a/examples/cocoa-application/CocoaApplication/main.m b/examples/cocoa-application/CocoaApplication/main.m new file mode 100644 index 00000000..18b23e7a --- /dev/null +++ b/examples/cocoa-application/CocoaApplication/main.m @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#import + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **)argv); +} diff --git a/examples/cocoa-touch-application/CocoaTouchApplication.qbs b/examples/cocoa-touch-application/CocoaTouchApplication.qbs new file mode 100644 index 00000000..4e09f81c --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication.qbs @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs 1.0 + +CppApplication { + Depends { condition: product.condition; name: "ib" } + condition: qbs.targetOS.contains("ios") + name: "Cocoa Touch Application" + + cpp.precompiledHeader: "CocoaTouchApplication/CocoaTouchApplication-Prefix.pch" + + cpp.frameworks: [ "UIKit", "Foundation", "CoreGraphics" ] + + Group { + prefix: "CocoaTouchApplication/" + files: [ + "AppDelegate.h", + "AppDelegate.m", + "CocoaTouchApplication-Info.plist", + "CocoaTouchApplication-Prefix.pch", + "Default-568h@2x.png", + "Default.png", + "Default@2x.png", + "DetailViewController.h", + "DetailViewController.m", + "MasterViewController.h", + "MasterViewController.m", + "main.m" + ] + } + + Group { + name: "Supporting Files" + prefix: "CocoaTouchApplication/en.lproj/" + files: [ + "DetailViewController_iPad.xib", + "DetailViewController_iPhone.xib", + "InfoPlist.strings", + "MasterViewController_iPad.xib", + "MasterViewController_iPhone.xib" + ] + } +} diff --git a/examples/cocoa-touch-application/CocoaTouchApplication.xcodeproj/project.pbxproj b/examples/cocoa-touch-application/CocoaTouchApplication.xcodeproj/project.pbxproj new file mode 100644 index 00000000..4dc8a6d4 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication.xcodeproj/project.pbxproj @@ -0,0 +1,348 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 14E3FEAA175FB2E800C857C6 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14E3FEA9175FB2E800C857C6 /* UIKit.framework */; }; + 14E3FEAC175FB2E800C857C6 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14E3FEAB175FB2E800C857C6 /* Foundation.framework */; }; + 14E3FEAE175FB2E800C857C6 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 14E3FEAD175FB2E800C857C6 /* CoreGraphics.framework */; }; + 14E3FEB4175FB2E800C857C6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 14E3FEB2175FB2E800C857C6 /* InfoPlist.strings */; }; + 14E3FEB6175FB2E800C857C6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 14E3FEB5175FB2E800C857C6 /* main.m */; }; + 14E3FEBA175FB2E800C857C6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 14E3FEB9175FB2E800C857C6 /* AppDelegate.m */; }; + 14E3FEBC175FB2E800C857C6 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 14E3FEBB175FB2E800C857C6 /* Default.png */; }; + 14E3FEBE175FB2E800C857C6 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 14E3FEBD175FB2E800C857C6 /* Default@2x.png */; }; + 14E3FEC0175FB2E800C857C6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 14E3FEBF175FB2E800C857C6 /* Default-568h@2x.png */; }; + 14E3FEC3175FB2E800C857C6 /* MasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 14E3FEC2175FB2E800C857C6 /* MasterViewController.m */; }; + 14E3FEC6175FB2E900C857C6 /* DetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 14E3FEC5175FB2E900C857C6 /* DetailViewController.m */; }; + 14E3FEC9175FB2E900C857C6 /* MasterViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 14E3FEC7175FB2E900C857C6 /* MasterViewController_iPhone.xib */; }; + 14E3FECC175FB2E900C857C6 /* MasterViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 14E3FECA175FB2E900C857C6 /* MasterViewController_iPad.xib */; }; + 14E3FECF175FB2E900C857C6 /* DetailViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 14E3FECD175FB2E900C857C6 /* DetailViewController_iPhone.xib */; }; + 14E3FED2175FB2E900C857C6 /* DetailViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 14E3FED0175FB2E900C857C6 /* DetailViewController_iPad.xib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 14E3FEA6175FB2E800C857C6 /* Cocoa Touch Application.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Cocoa Touch Application.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 14E3FEA9175FB2E800C857C6 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 14E3FEAB175FB2E800C857C6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 14E3FEAD175FB2E800C857C6 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 14E3FEB1175FB2E800C857C6 /* CocoaTouchApplication-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CocoaTouchApplication-Info.plist"; sourceTree = ""; }; + 14E3FEB3175FB2E800C857C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 14E3FEB5175FB2E800C857C6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 14E3FEB7175FB2E800C857C6 /* CocoaTouchApplication-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CocoaTouchApplication-Prefix.pch"; sourceTree = ""; }; + 14E3FEB8175FB2E800C857C6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 14E3FEB9175FB2E800C857C6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 14E3FEBB175FB2E800C857C6 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; + 14E3FEBD175FB2E800C857C6 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; + 14E3FEBF175FB2E800C857C6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; + 14E3FEC1175FB2E800C857C6 /* MasterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MasterViewController.h; sourceTree = ""; }; + 14E3FEC2175FB2E800C857C6 /* MasterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MasterViewController.m; sourceTree = ""; }; + 14E3FEC4175FB2E800C857C6 /* DetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DetailViewController.h; sourceTree = ""; }; + 14E3FEC5175FB2E900C857C6 /* DetailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DetailViewController.m; sourceTree = ""; }; + 14E3FEC8175FB2E900C857C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MasterViewController_iPhone.xib; sourceTree = ""; }; + 14E3FECB175FB2E900C857C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MasterViewController_iPad.xib; sourceTree = ""; }; + 14E3FECE175FB2E900C857C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/DetailViewController_iPhone.xib; sourceTree = ""; }; + 14E3FED1175FB2E900C857C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/DetailViewController_iPad.xib; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 14E3FEA3175FB2E800C857C6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 14E3FEAA175FB2E800C857C6 /* UIKit.framework in Frameworks */, + 14E3FEAC175FB2E800C857C6 /* Foundation.framework in Frameworks */, + 14E3FEAE175FB2E800C857C6 /* CoreGraphics.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 14E3FE9D175FB2E800C857C6 = { + isa = PBXGroup; + children = ( + 14E3FEAF175FB2E800C857C6 /* CocoaTouchApplication */, + 14E3FEA8175FB2E800C857C6 /* Frameworks */, + 14E3FEA7175FB2E800C857C6 /* Products */, + ); + sourceTree = ""; + }; + 14E3FEA7175FB2E800C857C6 /* Products */ = { + isa = PBXGroup; + children = ( + 14E3FEA6175FB2E800C857C6 /* Cocoa Touch Application.app */, + ); + name = Products; + sourceTree = ""; + }; + 14E3FEA8175FB2E800C857C6 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 14E3FEA9175FB2E800C857C6 /* UIKit.framework */, + 14E3FEAB175FB2E800C857C6 /* Foundation.framework */, + 14E3FEAD175FB2E800C857C6 /* CoreGraphics.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 14E3FEAF175FB2E800C857C6 /* CocoaTouchApplication */ = { + isa = PBXGroup; + children = ( + 14E3FEB8175FB2E800C857C6 /* AppDelegate.h */, + 14E3FEB9175FB2E800C857C6 /* AppDelegate.m */, + 14E3FEC1175FB2E800C857C6 /* MasterViewController.h */, + 14E3FEC2175FB2E800C857C6 /* MasterViewController.m */, + 14E3FEC4175FB2E800C857C6 /* DetailViewController.h */, + 14E3FEC5175FB2E900C857C6 /* DetailViewController.m */, + 14E3FEC7175FB2E900C857C6 /* MasterViewController_iPhone.xib */, + 14E3FECA175FB2E900C857C6 /* MasterViewController_iPad.xib */, + 14E3FECD175FB2E900C857C6 /* DetailViewController_iPhone.xib */, + 14E3FED0175FB2E900C857C6 /* DetailViewController_iPad.xib */, + 14E3FEB0175FB2E800C857C6 /* Supporting Files */, + ); + path = CocoaTouchApplication; + sourceTree = ""; + }; + 14E3FEB0175FB2E800C857C6 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 14E3FEB1175FB2E800C857C6 /* CocoaTouchApplication-Info.plist */, + 14E3FEB2175FB2E800C857C6 /* InfoPlist.strings */, + 14E3FEB5175FB2E800C857C6 /* main.m */, + 14E3FEB7175FB2E800C857C6 /* CocoaTouchApplication-Prefix.pch */, + 14E3FEBB175FB2E800C857C6 /* Default.png */, + 14E3FEBD175FB2E800C857C6 /* Default@2x.png */, + 14E3FEBF175FB2E800C857C6 /* Default-568h@2x.png */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 14E3FEA5175FB2E800C857C6 /* Cocoa Touch Application */ = { + isa = PBXNativeTarget; + buildConfigurationList = 14E3FED5175FB2E900C857C6 /* Build configuration list for PBXNativeTarget "Cocoa Touch Application" */; + buildPhases = ( + 14E3FEA2175FB2E800C857C6 /* Sources */, + 14E3FEA3175FB2E800C857C6 /* Frameworks */, + 14E3FEA4175FB2E800C857C6 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Cocoa Touch Application"; + productName = CocoaTouchApplication; + productReference = 14E3FEA6175FB2E800C857C6 /* Cocoa Touch Application.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 14E3FE9E175FB2E800C857C6 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0460; + ORGANIZATIONNAME = "Petroules Corporation"; + }; + buildConfigurationList = 14E3FEA1175FB2E800C857C6 /* Build configuration list for PBXProject "CocoaTouchApplication" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 14E3FE9D175FB2E800C857C6; + productRefGroup = 14E3FEA7175FB2E800C857C6 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 14E3FEA5175FB2E800C857C6 /* Cocoa Touch Application */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 14E3FEA4175FB2E800C857C6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 14E3FEB4175FB2E800C857C6 /* InfoPlist.strings in Resources */, + 14E3FEBC175FB2E800C857C6 /* Default.png in Resources */, + 14E3FEBE175FB2E800C857C6 /* Default@2x.png in Resources */, + 14E3FEC0175FB2E800C857C6 /* Default-568h@2x.png in Resources */, + 14E3FEC9175FB2E900C857C6 /* MasterViewController_iPhone.xib in Resources */, + 14E3FECC175FB2E900C857C6 /* MasterViewController_iPad.xib in Resources */, + 14E3FECF175FB2E900C857C6 /* DetailViewController_iPhone.xib in Resources */, + 14E3FED2175FB2E900C857C6 /* DetailViewController_iPad.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 14E3FEA2175FB2E800C857C6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 14E3FEB6175FB2E800C857C6 /* main.m in Sources */, + 14E3FEBA175FB2E800C857C6 /* AppDelegate.m in Sources */, + 14E3FEC3175FB2E800C857C6 /* MasterViewController.m in Sources */, + 14E3FEC6175FB2E900C857C6 /* DetailViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 14E3FEB2175FB2E800C857C6 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 14E3FEB3175FB2E800C857C6 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 14E3FEC7175FB2E900C857C6 /* MasterViewController_iPhone.xib */ = { + isa = PBXVariantGroup; + children = ( + 14E3FEC8175FB2E900C857C6 /* en */, + ); + name = MasterViewController_iPhone.xib; + sourceTree = ""; + }; + 14E3FECA175FB2E900C857C6 /* MasterViewController_iPad.xib */ = { + isa = PBXVariantGroup; + children = ( + 14E3FECB175FB2E900C857C6 /* en */, + ); + name = MasterViewController_iPad.xib; + sourceTree = ""; + }; + 14E3FECD175FB2E900C857C6 /* DetailViewController_iPhone.xib */ = { + isa = PBXVariantGroup; + children = ( + 14E3FECE175FB2E900C857C6 /* en */, + ); + name = DetailViewController_iPhone.xib; + sourceTree = ""; + }; + 14E3FED0175FB2E900C857C6 /* DetailViewController_iPad.xib */ = { + isa = PBXVariantGroup; + children = ( + 14E3FED1175FB2E900C857C6 /* en */, + ); + name = DetailViewController_iPad.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 14E3FED3175FB2E900C857C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 14E3FED4175FB2E900C857C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 6.1; + OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 14E3FED6175FB2E900C857C6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "CocoaTouchApplication/CocoaTouchApplication-Prefix.pch"; + INFOPLIST_FILE = "CocoaTouchApplication/CocoaTouchApplication-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 14E3FED7175FB2E900C857C6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "CocoaTouchApplication/CocoaTouchApplication-Prefix.pch"; + INFOPLIST_FILE = "CocoaTouchApplication/CocoaTouchApplication-Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 14E3FEA1175FB2E800C857C6 /* Build configuration list for PBXProject "CocoaTouchApplication" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 14E3FED3175FB2E900C857C6 /* Debug */, + 14E3FED4175FB2E900C857C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 14E3FED5175FB2E900C857C6 /* Build configuration list for PBXNativeTarget "Cocoa Touch Application" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 14E3FED6175FB2E900C857C6 /* Debug */, + 14E3FED7175FB2E900C857C6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 14E3FE9E175FB2E800C857C6 /* Project object */; +} diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.h b/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.h new file mode 100644 index 00000000..b5c92ff9 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +@interface AppDelegate : UIResponder +{ + UIWindow *_window; + UINavigationController *_navigationController; + UISplitViewController *_splitViewController; +} + +@property (nonatomic, retain) UIWindow *window; + +@property (nonatomic, retain) UINavigationController *navigationController; + +@property (nonatomic, retain) UISplitViewController *splitViewController; + +@end diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.m b/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.m new file mode 100644 index 00000000..ed4d9f31 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.m @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#import "AppDelegate.h" + +#import "MasterViewController.h" + +#import "DetailViewController.h" + +@implementation AppDelegate + +@synthesize window = _window; +@synthesize navigationController = _navigationController; +@synthesize splitViewController = _splitViewController; + +- (void)dealloc +{ + [_window release]; + [_navigationController release]; + [_splitViewController release]; + [super dealloc]; +} + +- (BOOL)application:(UIApplication *) __unused application didFinishLaunchingWithOptions:(NSDictionary *) __unused launchOptions +{ + self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; + // Override point for customization after application launch. + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + MasterViewController *masterViewController = [[[MasterViewController alloc] initWithNibName:@"MasterViewController_iPhone" bundle:nil] autorelease]; + self.navigationController = [[[UINavigationController alloc] initWithRootViewController:masterViewController] autorelease]; + self.window.rootViewController = self.navigationController; + } else { + MasterViewController *masterViewController = [[[MasterViewController alloc] initWithNibName:@"MasterViewController_iPad" bundle:nil] autorelease]; + UINavigationController *masterNavigationController = [[[UINavigationController alloc] initWithRootViewController:masterViewController] autorelease]; + + DetailViewController *detailViewController = [[[DetailViewController alloc] initWithNibName:@"DetailViewController_iPad" bundle:nil] autorelease]; + UINavigationController *detailNavigationController = [[[UINavigationController alloc] initWithRootViewController:detailViewController] autorelease]; + + masterViewController.detailViewController = detailViewController; + + self.splitViewController = [[[UISplitViewController alloc] init] autorelease]; + self.splitViewController.delegate = detailViewController; + self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController, detailNavigationController, nil]; + + self.window.rootViewController = self.splitViewController; + } + [self.window makeKeyAndVisible]; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *) __unused application +{ + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *) __unused application +{ + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *) __unused application +{ + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *) __unused application +{ + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *) __unused application +{ + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Info.plist b/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Info.plist new file mode 100644 index 00000000..f95cb455 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Info.plist @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.example.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarTintParameters + + UINavigationBar + + Style + UIBarStyleDefault + Translucent + + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Prefix.pch b/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Prefix.pch new file mode 100644 index 00000000..6a90a06f --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Prefix.pch @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#import + +#ifndef __IPHONE_4_0 +#warning "This project uses features only available in iOS SDK 4.0 and later." +#endif + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/Default-568h@2x.png b/examples/cocoa-touch-application/CocoaTouchApplication/Default-568h@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0891b7aabfcf3422423b109c8beed2bab838c607 GIT binary patch literal 18594 zcmeI4X;f257Jx&9fS`ixvS;&$x8J@slQFSel)6zJN=?13FB7H(lQjRkSy8x_-S~tvu2gzn1oS+dLcF#eqtq$ z%tf9TTvX?`)R@}3uBI;jzS-=ZR-Td&MHaS&;!0?Ni*#$#`n*~CcQK)Q9vAQ~TUpnI!j)a2biYK^R)M~A5wUDZhx?ULMX z3x1P&qt=trOY6P2U67L=m=U?F|5#Uj(eCueNTZaHs_ceWiHeET+j+tp3Jt9g(ekqP z2WOvfR{qV+9r+o4J5?qK>7;;^+I7tGv-i)es$X_D=EoKF+S?zsyj^oRFElP}c}JT< zd8SUs-?O?}2YD#ngKbnHgzHBcboxK_2r9l(?eNCl-pEzkJm}fY?WC*jnS?VBE4EpY zO$fEejz6fU;W2Kl>JeQBZBl-%Irg`obSlg*@4QB;Dd1H7^Oi5wvt4d{RZ!8Og?^aE z)k0$1g+V3fd(gdQ3d&q2q-FL*uy#}|bc^=VhFsl0jBgUGJ+-s3U8MK9A!YJJMxpci z5hJ%|{DwV48fZn0{n5l$N_KcSb#NKE4plB`9I6Zt=Z!~-zw0{9tg$L&Ju1F0X)Cy8 zKF;(&lJ>x)Jw(=;p~sF(Sd9VWGwFE2rnyS9!f^DZ8+aCLq zQ};>lcJ1GDLqjm6Hd>|Eabno@P`~Bn(~6^aD_#yoEH(a?Nm1S<;S+hSxI5d16^<1lEM3NPFi zkqPrpL)+ zgnseFikg`gJVBha1&7C4;O6>h=dt~`ND+;Zd?W(4v2JIb7Pt>Td42%M-Ju-XAH#Pns762L}K3 zDhvsRqN0Ni(1UrishD2YvV?4*h2iFj$+&N||Fn$4n|^NSU+o?~jq`0jVQt8T9l{7b zXiwwODFh2V!Q6sqP9S>WH$oOf$N~=d0-bqTlD61!=`&0eAP-F>XN?*|gtOXX{ zQVTWyYo4ZK0GAw!GHf|pz9`D;-bbb*5LBX*{bnz|+)$@&P9|ORM2o?95{;ejvo&r- zq8cBhTN6nn)7~W>54U)%-F_-b?YKdfk5I8MHcuzBD5)!;yv#Z&R&^y=@=>VTIMy#r zX&U<=BsPkdqcMe<_}2+>H%XKyrr5ZR8_KVe>ZqYN z^=^~TFD};;rHJ$U;{~w^hYojl4hRI@SH$^K{YEo=sg)WY87r!*7blQK&qnpDo0`Vn zkl)9u9g=mCh&ZCJS(L4yN3k0kQ zuvg$h2KEEk51T+O0JQ+r0`R>g{jvqM0Mr6d3qUOZwE!?PI7HY@CE|dr sfw?Q;rAv?G4&^^8-z_>&sWXMxvD*gPOU4CBe-*@OtE+wfmVJNyHv)PfH~;_u literal 0 HcmV?d00001 diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/Default.png b/examples/cocoa-touch-application/CocoaTouchApplication/Default.png new file mode 100644 index 0000000000000000000000000000000000000000..4c8ca6f693f96d511e9113c0eb59eec552354e42 GIT binary patch literal 6540 zcmeAS@N?(olHy`uVBq!ia0y~yU~~ZD2OMlbkt;o0To@QwR5G2N13aCb6#|O#(=u~X z85k@CTSM>X-wqM6>&y>YB4)1;;ojbLbbV-W^iFB1wa3^zCog^LCAReC4K0-?R_2{6 zrP*)4+_uWUy3w5N52M3PW_}MFMP9a~>YLvVZ1D_k*IMQ2QT^fwzoOb(*3gH$%aYWC zkHmcab=va2<#X%jakpJ;<1@F;k__#bwtC&%^D0v(FBh9K&$sK+<}2RJS609D)17$w ztdQP8(eLM8Ka}m_IQ@3wyMKP)l=oM4-?`YS_*P?4V_ORLPxsj&7Ju#kH;>6^Kp?T7~ zl+q?{UOOqV==?+d{=)5s|M~T1mwtH@+Z^$G&eEO9JNP^AX@3jZ*J*!!>lc|1-W%fA z@AOQpXZ_Lt>rxFXrGp*zLPiW@uo_c7C{As>j zWeX)wi+LTp_)@KYZCX{j;H?|1yXT4DnlS(Fr8gyP5|uaX_gLvaW0ScZdnG7o+u{T6 zFI-%d{ls*WuCDa5UJ@|RXv&ejZe}*BMkiWY51&pnRPw(hlykSzvj6e%mYz-GdvzBD zF10?szF_~!jS=?2HyQuPCvARXAe}C}WP|yQ*>5~~=*Nxq8+HHW1~FMDRCP^TcacKuk$ z(U#REVv)D!PhJ*ecH-ELFUrfyV&*)Z)>UCOuS?yd^L@Afk>ihynYPc{^CRwu+JHX+#$@YsC4c|l0tGigsn@jy) zXD($Ouk>H+V(Mr6NQT0S9BFM~V6nkj;1OBOz`zY;a|<&v%$g$sEJPk;hD4M^`1)8S z=jZArrsOB3>Q&?x097+E*i={nnYpPYi3%0DIeEoa6}C!X6;?ntNLXJ<0j#7X+g2&U zH$cHTzbI9~RL@Y)NXd>%K|#T$C?(A*$i)q+9mum)$|xx*u+rBrFE7_CH`dE9O4m2E zw6xSWFw!?N(gmu}Ew0QfNvzP#D^`XW0yD=YwK%ybv!En1KTiQ3|)OBHVcpi zp&D%TL4k-AsNfg_g$9~9p}$+4Ynr|VULLgiakg&)DD)EWO!OHC@snXr}UI${nVUP zpr1>Mf#G6^ng~;pt%^&NvQm>vU@-wn)!_JWN=(;B61LIDR86%A1?G9U(@`={MPdPF zbOKdd`R1o&rd7HmmZaJl85kPr8kp-EnTHsfS{ayIfdU*&4N@e5WSomq6HD@oLh|!- z?7;Dr3*ssm=^5w&a}>G?yzvAH17L|`#|6|0E4}QvA~xC{V_*wu2^AHZU}H9f($4F$btFf{}TLQXUhF5fht1@YV$^ z9BUdFV+73^nIsvRXRM40U}6b7z_6}kHbY}i1LK(xT@6Mi?F5GKBfbp|ZU-3BR*6kv zXcRSQ(0-)mprD+wTr)o_4I;(%zOu)+jEgNB)_SXCVoSa}|F?cfwR!69+L=W3IX z!UiU`0@ph%94Rb33Cpq^IY*r_8XBW%V>G9XmK&p`=xCiXTEmXEH%41uqixaAmicH0 zVYIt6!aI*K%s=kP-v##6IXGZ2Cama>{@)81;C?K-P&M2k<0!GL}5+H~XTq*@SQi|Ft z2*0X`$`8S!qO#)xBeJRkf?;t189=ZB6Imw-h=`q;FP(2UpWZvmJ@=k-@45M(dtb7r zyVEiaLk$=Vw#>zu;st}j6Jf9=m1+nXCFe!$1PrEZ%5Ze_ba8YX_9-*rJujiLuQmJo&2v+Cxes}ec zU|qeux&7*yz#W=X_|wGQskL7*OHNjwFs@sEC+64Hb$Z(#H21Gh$Pe2WzOubdr6fzg z{l{!k%OD?N5Z7j33SoK?YdV6Scm>})U+MIQLNRgIvkZQEc^mP9XBPg%y|S$~Br|;N zk?-!-(Qqh_mQ|6WINQ{hHAjBRV#O#!FkAJ+oxy`L#f8V45*VvWMJFBB5m zG6vOLtDvgoDjHlSq-*h5xM56O>Jjau2f2IxKItIb@coX4XTyf$^{LZG&lI|D95wN1 z!fo0)q>WV7-V;q|A?HR!*bgozJw%j98-~gwBKVV0;=hZIF>7oJSr2YjOWO*rSxz#& z;KXnDrJVZp;Yduiy1-H%s$ZFz6Q=x@$V_B@Tqwl?>6e;EHt|MiK<(#hXQMuj@Jseeh&eN{FxsQ$iw>D1aX1HMMlUbh?Z zmhY4eHffn5&LUbL_}o8|$JYz&$WFiLWmEg0ZPX+;W>@CxQz-%{E5+P7dH9&ey_y$R z@Zzje>2B%z!i!7Brqi{t5Y)~5>vpqRs~2aXD8DVE8vKl=`k(`duI1-k@?!pJ^HA6S zS;3WpuhjQHyoC>X>Xf8gze%_8^#+^RTV>V9&YPAWMjd~%xpSg?ON?kK^X*Pb(o8jR zz;DmaOWMMr6=M~K?MFx4_xDkARTxLJ@W@ohAx z5RD0jGgk?QL@H`VubD2k4}?VtB8@g`%hHBA$2pJ(gK5g1HMNysXEF_BNu-p!&+Qa8_APgopHWnRgg=TZZF*sXWTMQPD z!Q(Au5|+F;7M~`tWbsU98~NA{h0Y7%GB|t&n}w9OOABU4^X*V5xuN;rY(M#ouuqm) zyt!e?28fY!FgP?8GvBsMl_aM^UUVKiGFsleFN?t^<46kO#pF-cX0;sIOb(aM z)^jQgX^Z6pKA9mC@N)_aiHj9HxD2|?A@Y9B_h}(*v3%ek8CXc1Qy^jFPF&zrMa1OZ zSVaF{&ZY|(|H0XE&X>-XQz1`=fF2n@VKC_|h3jlKVM&-jmyMavllcYr`6LVtfq2ou zd+8zkkCB+2)rxq0Lkq_&Ad@g(O8;pAm96>tu79?81T@Z<;gm^3ZtPG-SR94Mr<3tm z9NrR3u*4I5aMlo(09g@8m_;%Rf+XiSa_KZao9n}7N0JrsV#;5Ucr+F*TTzQ8{%f3O zeIUy?WDS|-$LvMc@Z7320)tr}bfIka5hx9H;8H|%our=C+Do0CSFRWue14o5#r8v2 zw=|&r4*eMX%lgCV(ka?*j%H^UuP4LmBC(ON`)&7>NF-|PDRU{-7o`CU0HNbd&c~))@yl9IKu_ zXA+A-!khpP_yx=f#qt2_0ptmgBf4gF!{Y)MW6R$cC1d7@$Yb?+_j zYwfE^5_e`vhT zX=u3r>4$fsxP&apbm@Rcbyuc2T=giqZiMo9@9=oua6#YH0hO-1ak9^rJTPMM qY4Yr5Cu^v99p{E9VdroUHKlRW;M8#BJ^AOQE?e9wSHJo8(7yq;BYKSh literal 0 HcmV?d00001 diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.h b/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.h new file mode 100644 index 00000000..4a71f012 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +@interface DetailViewController : UIViewController +{ + id _detailItem; + UILabel *_detailDescriptionLabel; + UIPopoverController *_masterPopoverController; +} + +@property (nonatomic, retain) id detailItem; + +@property (nonatomic, retain) IBOutlet UILabel *detailDescriptionLabel; + +@end diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.m b/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.m new file mode 100644 index 00000000..85a5d6dd --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.m @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#import "DetailViewController.h" + +@interface DetailViewController () +@property (nonatomic, retain) UIPopoverController *masterPopoverController; +- (void)configureView; +@end + +@implementation DetailViewController + +@synthesize detailItem = _detailItem; +@synthesize detailDescriptionLabel = _detailDescriptionLabel; +@synthesize masterPopoverController = _masterPopoverController; + +- (void)dealloc +{ + [_detailItem release]; + [_detailDescriptionLabel release]; + [_masterPopoverController release]; + [super dealloc]; +} + +#pragma mark - Managing the detail item + +- (void)setDetailItem:(id)newDetailItem +{ + if (_detailItem != newDetailItem) { + [_detailItem release]; + _detailItem = [newDetailItem retain]; + + // Update the view. + [self configureView]; + } + + if (self.masterPopoverController != nil) { + [self.masterPopoverController dismissPopoverAnimated:YES]; + } +} + +- (void)configureView +{ + // Update the user interface for the detail item. + + if (self.detailItem) { + self.detailDescriptionLabel.text = [self.detailItem description]; + } +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. + [self configureView]; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + self.title = NSLocalizedString(@"Detail", @"Detail"); + } + return self; +} + +#pragma mark - Split view + +- (void)splitViewController:(UISplitViewController *) __unused splitController willHideViewController:(UIViewController *) __unused viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController +{ + barButtonItem.title = NSLocalizedString(@"Master", @"Master"); + [self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES]; + self.masterPopoverController = popoverController; +} + +- (void)splitViewController:(UISplitViewController *) __unused splitController willShowViewController:(UIViewController *) __unused viewController invalidatingBarButtonItem:(UIBarButtonItem *) __unused barButtonItem +{ + // Called when the view is shown again in the split view, invalidating the button and popover controller. + [self.navigationItem setLeftBarButtonItem:nil animated:YES]; + self.masterPopoverController = nil; +} + +@end diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.h b/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.h new file mode 100644 index 00000000..964ce3d9 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +@class DetailViewController; + +@interface MasterViewController : UITableViewController +{ + NSMutableArray *_objects; + DetailViewController *_detailViewController; +} + +@property (nonatomic, retain) DetailViewController *detailViewController; + +@end diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.m b/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.m new file mode 100644 index 00000000..6024d67c --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.m @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#import "MasterViewController.h" + +#import "DetailViewController.h" + +@implementation MasterViewController + +@synthesize detailViewController = _detailViewController; + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil +{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + self.title = NSLocalizedString(@"Master", @"Master"); + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { + self.clearsSelectionOnViewWillAppear = NO; + self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0); + } + } + return self; +} + +- (void)dealloc +{ + [_detailViewController release]; + [_objects release]; + [super dealloc]; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. + self.navigationItem.leftBarButtonItem = self.editButtonItem; + + UIBarButtonItem *addButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)] autorelease]; + self.navigationItem.rightBarButtonItem = addButton; +} + +- (void)didReceiveMemoryWarning +{ + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +- (void)insertNewObject:(id) __unused sender +{ + if (!_objects) { + _objects = [[NSMutableArray alloc] init]; + } + [_objects insertObject:[NSDate date] atIndex:0]; + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; + [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; +} + +#pragma mark - Table View + +- (NSInteger)numberOfSectionsInTableView:(UITableView *) __unused tableView +{ + return 1; +} + +- (NSInteger)tableView:(UITableView *) __unused tableView numberOfRowsInSection:(NSInteger) __unused section +{ + return _objects.count; +} + +// Customize the appearance of table view cells. +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + static NSString *CellIdentifier = @"Cell"; + + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; + if (cell == nil) { + cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; + } + } + + + NSDate *object = [_objects objectAtIndex:indexPath.row]; + cell.textLabel.text = [object description]; + return cell; +} + +- (BOOL)tableView:(UITableView *) __unused tableView canEditRowAtIndexPath:(NSIndexPath *) __unused indexPath +{ + // Return NO if you do not want the specified item to be editable. + return YES; +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (editingStyle == UITableViewCellEditingStyleDelete) { + [_objects removeObjectAtIndex:indexPath.row]; + [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; + } else if (editingStyle == UITableViewCellEditingStyleInsert) { + // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view. + } +} + +/* +// Override to support rearranging the table view. +- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath +{ +} +*/ + +/* +// Override to support conditional rearranging of the table view. +- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath +{ + // Return NO if you do not want the item to be re-orderable. + return YES; +} +*/ + +- (void)tableView:(UITableView *) __unused tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + NSDate *object = [_objects objectAtIndex:indexPath.row]; + if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { + if (!self.detailViewController) { + self.detailViewController = [[[DetailViewController alloc] initWithNibName:@"DetailViewController_iPhone" bundle:nil] autorelease]; + } + self.detailViewController.detailItem = object; + [self.navigationController pushViewController:self.detailViewController animated:YES]; + } else { + self.detailViewController.detailItem = object; + } +} + +@end diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/DetailViewController_iPad.xib b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/DetailViewController_iPad.xib new file mode 100644 index 00000000..884dc206 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/DetailViewController_iPad.xib @@ -0,0 +1,223 @@ + + + + 1536 + 12A206j + 2519 + 1172.1 + 613.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 1856 + + + IBNSLayoutConstraint + IBProxyObject + IBUILabel + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBIPadFramework + + + IBFirstResponder + IBIPadFramework + + + + 274 + + + + 298 + {{20, 495}, {728, 18}} + + + + 3 + MQA + + YES + NO + IBIPadFramework + + 1 + 10 + Detail view content goes here + + 1 + MCAwIDAAA + + 1 + + 1 + 4 + + + Helvetica + 14 + 16 + + + + {{0, 20}, {768, 1004}} + + + + NO + + 2 + + IBIPadFramework + + + + + + + view + + + + 12 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 8 + + + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 5 + 22 + 2 + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + + + 5 + 0 + + 5 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + + + + + + 81 + + + + + + 94 + + + + + 97 + + + + + 98 + + + + + + + DetailViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 98 + + + 0 + IBIPadFramework + YES + 3 + YES + 1856 + + diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/DetailViewController_iPhone.xib b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/DetailViewController_iPhone.xib new file mode 100644 index 00000000..6c380252 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/DetailViewController_iPhone.xib @@ -0,0 +1,253 @@ + + + + 1536 + 12A269 + 2835 + 1187 + 624.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 1919 + + + IBNSLayoutConstraint + IBProxyObject + IBUILabel + IBUIView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + + + + 298 + {{20, 265}, {280, 18}} + + + + + 3 + MQA + + YES + NO + IBCocoaTouchFramework + Detail view content goes here + + 1 + MCAwIDAAA + darkTextColor + + + 1 + 10 + 1 + + 1 + 4 + + + Helvetica + 14 + 16 + + + + {{0, 20}, {320, 548}} + + + + + 3 + MQA + + 2 + + + + + IBUIScreenMetrics + + YES + + + + + + {320, 568} + {568, 320} + + + IBCocoaTouchFramework + Retina 4 Full Screen + 2 + + IBCocoaTouchFramework + + + + + + + view + + + + 3 + + + + detailDescriptionLabel + + + + 6 + + + + + + 0 + + + + + + 1 + + + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 5 + 22 + 2 + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + + + 5 + 0 + + 5 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + + + + + + -1 + + + File's Owner + + + -2 + + + + + 4 + + + + + 7 + + + + + 9 + + + + + 11 + + + + + + + DetailViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 11 + + + 0 + IBCocoaTouchFramework + YES + 3 + YES + 1919 + + diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/InfoPlist.strings b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/InfoPlist.strings new file mode 100644 index 00000000..b92732c7 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/InfoPlist.strings @@ -0,0 +1 @@ +/* Localized versions of Info.plist keys */ diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/MasterViewController_iPad.xib b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/MasterViewController_iPad.xib new file mode 100644 index 00000000..2b4dba21 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/MasterViewController_iPad.xib @@ -0,0 +1,152 @@ + + + + 1536 + 12A206j + 2519 + 1172.1 + 613.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 1856 + + + IBProxyObject + IBUITableView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBIPadFramework + + + IBFirstResponder + IBIPadFramework + + + + 274 + {{0, 20}, {320, 832}} + + + + 3 + MQA + + YES + + 2 + + + IBUISplitViewMasterSimulatedSizeMetrics + + YES + + + + + + {320, 852} + {320, 768} + + + IBIPadFramework + Master + IBUISplitViewController + + IBUISplitViewControllerContentSizeLocation + IBUISplitViewControllerContentSizeLocationMaster + + + IBIPadFramework + YES + 1 + 0 + YES + 44 + 22 + 22 + + + + + + + view + + + + 3 + + + + dataSource + + + + 4 + + + + delegate + + + + 5 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 2 + + + + + + + MasterViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 5 + + + 0 + IBIPadFramework + YES + 3 + YES + 1856 + + diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/MasterViewController_iPhone.xib b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/MasterViewController_iPhone.xib new file mode 100644 index 00000000..1000ecf2 --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/en.lproj/MasterViewController_iPhone.xib @@ -0,0 +1,147 @@ + + + + 1536 + 12A269 + 2835 + 1187 + 624.00 + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + 1919 + + + IBProxyObject + IBUITableView + + + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + PluginDependencyRecalculationVersion + + + + + IBFilesOwner + IBCocoaTouchFramework + + + IBFirstResponder + IBCocoaTouchFramework + + + + 274 + {{0, 20}, {320, 548}} + + + + + 3 + MQA + + YES + + + IBUIScreenMetrics + + YES + + + + + + {320, 568} + {568, 320} + + + IBCocoaTouchFramework + Retina 4 Full Screen + 2 + + IBCocoaTouchFramework + YES + 1 + 0 + YES + 44 + 22 + 22 + + + + + + + view + + + + 3 + + + + dataSource + + + + 4 + + + + delegate + + + + 5 + + + + + + 0 + + + + + + -1 + + + File's Owner + + + -2 + + + + + 2 + + + + + + + MasterViewController + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + UIResponder + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + com.apple.InterfaceBuilder.IBCocoaTouchPlugin + + + + + + 5 + + + 0 + IBCocoaTouchFramework + YES + 3 + YES + 1919 + + diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/main.m b/examples/cocoa-touch-application/CocoaTouchApplication/main.m new file mode 100644 index 00000000..7665efcf --- /dev/null +++ b/examples/cocoa-touch-application/CocoaTouchApplication/main.m @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ +#import + +#import "AppDelegate.h" + +int main(int argc, char *argv[]) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + [pool release]; + return retVal; +} diff --git a/examples/code-generator/code-generator.qbs b/examples/code-generator/code-generator.qbs new file mode 100644 index 00000000..572bebd0 --- /dev/null +++ b/examples/code-generator/code-generator.qbs @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ +import qbs + +Project { + // A code generator that outputs a "Hello World" C++ program. + CppApplication { + name: "hwgen" + files: ["hwgen.cpp"] + bundle.isBundle: false + } + + // Generate and build a hello-world application. + CppApplication { + name: "hello-world" + Depends { name: "hwgen" } + Rule { + inputs: ["qbs"] // needed to trigger this rule + Artifact { + filePath: "main.cpp" + fileTags: ["cpp"] + } + prepare: { + var hwgen; + for (var i in product.dependencies) { + var dep = product.dependencies[i]; + if (dep.name != "hwgen") + continue; + hwgen = dep.buildDirectory + "/" + dep.targetName; + } + var cmd = new Command(hwgen, [output.filePath]); + cmd.description = "generating C++ source"; + return cmd; + } + } + } +} diff --git a/examples/code-generator/hwgen.cpp b/examples/code-generator/hwgen.cpp new file mode 100644 index 00000000..2dd1aa77 --- /dev/null +++ b/examples/code-generator/hwgen.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main(int argc, char **argv) +{ + if (argc < 2) + return 1; + FILE *f = fopen(argv[1], "w"); + if (!f) + return 2; + fprintf(f, "#include \n\n" + "int main()\n" + "{\n printf(\"Hello World!\\n\");\n return 0;\n}\n"); + fclose(f); + return 0; +} diff --git a/examples/collidingmice/collidingmice.qbs b/examples/collidingmice/collidingmice.qbs new file mode 100644 index 00000000..9be12f53 --- /dev/null +++ b/examples/collidingmice/collidingmice.qbs @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs 1.0 + +Application { + name : "CollidingMice" + destinationDirectory: "bin" + + Depends { + name: "Qt" + submodules: ["widgets"] + } + + files : [ + "main.cpp", + "mouse.cpp", + "mouse.h", + "mice.qrc" + ] +} + diff --git a/examples/collidingmice/images/cheese.jpg b/examples/collidingmice/images/cheese.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dea5795fd0b7a4dfa46bf7274d068885d700914e GIT binary patch literal 3029 zcmaKtcU03$7RP_7bYf^qkzS;UAXO4Np@=|e0s_*4QWPoDrAzNkil7t;hyo%V0g*^m zX_1a-02Llx-~~{!dG4OweShqJ=bStD&Yd~ub7$t9OPQv822L4b3@`u)3Or7X3xUF*e+dSM{T3WfLqkhL1E-^-r>CQ1{JTI9 z2m}U&(ZS(#O!Rd0ObBMG5QyKK{xvcFGy40ve@B$p0FoAP030A7BmhQ&AV?5p0e}LG z6auJ>ib(_dOH}8#pfCs+4gzRs>F60iRCvgLXQ>NRcKGk)DF94G14F@Z8VJ>Z{Xk#{ z5(>ZsSY$P^a8@TlpC~rD%u*Wm7g|C+W@BG2ayV}b`$q4GXv>>tl{L8dy;0B+%PxPZ zc-q3%zjvHOD;h&be}*$QM;BAkXfbAj|)kBF(hPECdNrg|_ zhi2D~sO5vHlrZXb!lASf$SrC;NC@;VVi=Z64EKr3lq-F)&DJyaMM#U?%-MJ6qOiPn zG)I=XNSRB+8ydfH1(Ht8X^U*??VRBhRn+AYbM?1W+AV+Cdro{}=|^l%MdRO*F;@S9 z{-4bM2bnSpFhYJ~A^~;aSaZU=EWRJ5l(Py3@W3|19|XV_#NE7m=ivbY~VGswbngP#v%wyeF0m9Kl7b{seu z$9LB40QPONY$KJkl#{EWTasboYa)gyS9T>N?_j zIZ}CGoDs*NFM_%j!Dyu9ApM9kdVJ}ae)Ec_(RMv=skijeZ65*oR-{AGvgw35KJA-` z$L#2)KBOO-Yiz^9e*ObE$}HRlkd}Gat@x_HD*uT+--p8R^c!qmEs8rIx9kV_X}=@5 zrfLbe)?L6O@wlLmL+Qq&?2)SZLy1|^i9u!YCeC{EJYC>TqBN87el^*$$Y^Z3PSw}PE<;uXh+EuMZbyQ{76hQm!km^hl9tMfomtH7bC z+-^0{YRc;8gBZBEaV*YnD2*Ys`x&N02918FhR1IiKMKc@OG;$KSmQ$?C}YR!h8x6# z5e9q}Hke&~5?!C*BRw8f7|Y=i>iS9)?$me3Fw@XqFrVz7cEOSuPjh=NC7nT4_#r|ni*fXU_692e)IKxGfSjt$vY+XA%ae) zSdD-XtJ8h=F1xISPSxZ9!?PdzD8O9)jF6OGY#T}|v|({nT-iF4-STRHI!@2mq*)JM zXd^eOdKcYO8%)r0TAj<~*rNb?7JG;02k!m3>O9l)$~EK&?PRl>H3~o(KUPCXM^=x} zw^SL*d`p{E*>v;aF%<~r*#qessD$Z*hiw{es+s)|`x;TWe|gn;)L_C3n~*wQQWLfC zFlOpCPEGugt*=m=?6-EGmFsH3Ws{q<`^oWLO*v75Mj%Io*)v$cp94H`@C_D%$bk83 zkwKnr(#^StX7^p4Myt8qH2IU6QC?g|S7=rJRm?p`8=o_w>upX{px#Iq3Fh*Y(NI2^@LW+(~H_^sHpp zfs2VCr^X(c5_xZN$v6;wMgY{?JbyG}Ik>F=Rj@_orxm;b=;QNxWp5OI>m74B1?yDD zG=yFe?c-FNwyi=(DR4H$GRj7V7KbHVlrI82Lbi;rY;9zq3gTK7=zvVyt1ZK)5DHBx z&zL-;q^qBUwpE1BFB!)on91z-^m%DSH0=k7Wb%}dA@yO1$J&G9(>&WDA@_MB_(J^o z_XY{uMLfjh%g-C{u^DeLc3bAWa7(kzp#YAr!)e5d;Hcw1JF(V|JmYZ$+X%7m zHI7{sH9u&Sn{d_JYhOyBZ!lz9t!U@)9rS+lt9gU!XLm!y>oO z_%7#Z;m+f7fP}|SHPqE|hAv(7ljVocpC4-aW$m@ED=_a}FN$Y-MjtY@l4QxoI(qO& zYUw>X`Lo-zt%TsGzGf!&2Ye<*DAkYUiIk7W%C9HOYlG}NwX*dbVyD^&5|J(TGtCNf z1QKx0O8@co#CCG=t`2WXP_*Jxm$FEhqL9%0tVdNAVH(9pB*(lpyB#(@^ari1_NNjR zQ;P_C4hx8(MW~BmM{yvw#Vc3hv**d>AXj1bIimNT<5kM!F&aA+kr!l6lo9q3_Hv&9 z&6uU$zMOoI8{6@+v^|dHRu4Da8IV7C?kksx6c&|b$$*jC-QvIA+=M?>P#vsQp%a$V ze|v_vEOxwPI#PqC#=lGjO(M3%iDd@KC)5spwzfza7;HMtA<-9KyC*-i{?g2)Kjd~O ziGyg=8E;n)hc=1=qs=D}UZ_vqeqK)9SY(f&M~%3!Xy|i#m-OFp*FfK@&r+(+rWBH*u-d}w|+QjA*P20>#yl`}ti73FL zQCB|)t|MD`Yxqhug5O&*X%fkyB=A*syyjFbZ-Vx#TawaDm?f{5oGwbk-384`m(mMy z3DJ|2rOrBkXaMSoD#=-aN=l4CAIoJvsU!^7*`mAK!%~C`{MQ8HhvjVMby3&`rbN7l zo74{n)QaU|FEJe}GXRHWfQjj5;? zWaK;@aP`E!V`&CVGBzn_HngoWz?A)3IxYER@?-TuUoaHDUY;^squvw{lvzxV2Kl2k z1sm0tgfZ*jHqExPci5UA-?3y2Sd5s8%h-y?r(+}6ANZ00`Cd-`_=4cxVb8l&qBU$@ zpPHa6ffAoV=Ig#Tdq1&D>HWh89P6Al<5*ivTr9qZnG~Hr+?Pz3U=(0B_dio)x!k(}F@}Zit~99y zXX905#&$NzdroRx3l6hu^BK4aHY3RaVWL)8;IiIg?!(z|$r4r71^uX_@HdHi()X@~ z=88Qs%@gmccFmjo5^-;}y<=y~e5i5f@!(RyLy<2!7==!6i9Z6nyCRvK`H=(zHFsAC Vi6{N-mmDs<0OvLl=N>_s`3L5NTuJ}{ literal 0 HcmV?d00001 diff --git a/examples/collidingmice/main.cpp b/examples/collidingmice/main.cpp new file mode 100644 index 00000000..7bd074ef --- /dev/null +++ b/examples/collidingmice/main.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mouse.h" + +#include +#include +#include +#include +#include + +#include + +static const int MouseCount = 7; + +//! [0] +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + qsrand(QTime(0,0,0).secsTo(QTime::currentTime())); +//! [0] + +//! [1] + QGraphicsScene scene; + scene.setSceneRect(-300, -300, 600, 600); +//! [1] //! [2] + scene.setItemIndexMethod(QGraphicsScene::NoIndex); +//! [2] + +//! [3] + for (int i = 0; i < MouseCount; ++i) { + Mouse *mouse = new Mouse; + mouse->setPos(::sin((i * 6.28) / MouseCount) * 200, + ::cos((i * 6.28) / MouseCount) * 200); + scene.addItem(mouse); + } +//! [3] + +//! [4] + QGraphicsView view(&scene); + view.setRenderHint(QPainter::Antialiasing); + view.setBackgroundBrush(QPixmap(":/images/cheese.jpg")); +//! [4] //! [5] + view.setCacheMode(QGraphicsView::CacheBackground); + view.setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + view.setDragMode(QGraphicsView::ScrollHandDrag); +//! [5] //! [6] + view.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Colliding Mice")); + view.resize(400, 300); + view.show(); + + QTimer timer; + QObject::connect(&timer, &QTimer::timeout, &scene, &QGraphicsScene::advance); + timer.start(1000 / 33); + + return app.exec(); +} +//! [6] diff --git a/examples/collidingmice/mice.qrc b/examples/collidingmice/mice.qrc new file mode 100644 index 00000000..accdb4d0 --- /dev/null +++ b/examples/collidingmice/mice.qrc @@ -0,0 +1,5 @@ + + + images/cheese.jpg + + diff --git a/examples/collidingmice/mouse.cpp b/examples/collidingmice/mouse.cpp new file mode 100644 index 00000000..4485e0ae --- /dev/null +++ b/examples/collidingmice/mouse.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mouse.h" + +#include +#include +#include + +#include + +static const double Pi = 3.14159265358979323846264338327950288419717; +static double TwoPi = 2.0 * Pi; + +static qreal normalizeAngle(qreal angle) +{ + while (angle < 0) + angle += TwoPi; + while (angle > TwoPi) + angle -= TwoPi; + return angle; +} + +//! [0] +Mouse::Mouse() + : angle(0), speed(0), mouseEyeDirection(0), + color(qrand() % 256, qrand() % 256, qrand() % 256) +{ + setRotation(qrand() % (360 * 16)); +} +//! [0] + +//! [1] +QRectF Mouse::boundingRect() const +{ + qreal adjust = 0.5; + return QRectF(-18 - adjust, -22 - adjust, + 36 + adjust, 60 + adjust); +} +//! [1] + +//! [2] +QPainterPath Mouse::shape() const +{ + QPainterPath path; + path.addRect(-10, -20, 20, 40); + return path; +} +//! [2] + +//! [3] +void Mouse::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + // Body + painter->setBrush(color); + painter->drawEllipse(-10, -20, 20, 40); + + // Eyes + painter->setBrush(Qt::white); + painter->drawEllipse(-10, -17, 8, 8); + painter->drawEllipse(2, -17, 8, 8); + + // Nose + painter->setBrush(Qt::black); + painter->drawEllipse(QRectF(-2, -22, 4, 4)); + + // Pupils + painter->drawEllipse(QRectF(-8.0 + mouseEyeDirection, -17, 4, 4)); + painter->drawEllipse(QRectF(4.0 + mouseEyeDirection, -17, 4, 4)); + + // Ears + painter->setBrush(scene()->collidingItems(this).isEmpty() ? Qt::darkYellow : Qt::red); + painter->drawEllipse(-17, -12, 16, 16); + painter->drawEllipse(1, -12, 16, 16); + + // Tail + QPainterPath path(QPointF(0, 20)); + path.cubicTo(-5, 22, -5, 22, 0, 25); + path.cubicTo(5, 27, 5, 32, 0, 30); + path.cubicTo(-5, 32, -5, 42, 0, 35); + painter->setBrush(Qt::NoBrush); + painter->drawPath(path); +} +//! [3] + +//! [4] +void Mouse::advance(int step) +{ + if (!step) + return; +//! [4] + // Don't move too far away +//! [5] + QLineF lineToCenter(QPointF(0, 0), mapFromScene(0, 0)); + if (lineToCenter.length() > 150) { + qreal angleToCenter = ::acos(lineToCenter.dx() / lineToCenter.length()); + if (lineToCenter.dy() < 0) + angleToCenter = TwoPi - angleToCenter; + angleToCenter = normalizeAngle((Pi - angleToCenter) + Pi / 2); + + if (angleToCenter < Pi && angleToCenter > Pi / 4) { + // Rotate left + angle += (angle < -Pi / 2) ? 0.25 : -0.25; + } else if (angleToCenter >= Pi && angleToCenter < (Pi + Pi / 2 + Pi / 4)) { + // Rotate right + angle += (angle < Pi / 2) ? 0.25 : -0.25; + } + } else if (::sin(angle) < 0) { + angle += 0.25; + } else if (::sin(angle) > 0) { + angle -= 0.25; +//! [5] //! [6] + } +//! [6] + + // Try not to crash with any other mice +//! [7] + QList dangerMice = scene()->items(QPolygonF() + << mapToScene(0, 0) + << mapToScene(-30, -50) + << mapToScene(30, -50)); + foreach (QGraphicsItem *item, dangerMice) { + if (item == this) + continue; + + QLineF lineToMouse(QPointF(0, 0), mapFromItem(item, 0, 0)); + qreal angleToMouse = ::acos(lineToMouse.dx() / lineToMouse.length()); + if (lineToMouse.dy() < 0) + angleToMouse = TwoPi - angleToMouse; + angleToMouse = normalizeAngle((Pi - angleToMouse) + Pi / 2); + + if (angleToMouse >= 0 && angleToMouse < Pi / 2) { + // Rotate right + angle += 0.5; + } else if (angleToMouse <= TwoPi && angleToMouse > (TwoPi - Pi / 2)) { + // Rotate left + angle -= 0.5; +//! [7] //! [8] + } +//! [8] //! [9] + } +//! [9] + + // Add some random movement +//! [10] + if (dangerMice.size() > 1 && (qrand() % 10) == 0) { + if (qrand() % 1) + angle += (qrand() % 100) / 500.0; + else + angle -= (qrand() % 100) / 500.0; + } +//! [10] + +//! [11] + speed += (-50 + qrand() % 100) / 100.0; + + qreal dx = ::sin(angle) * 10; + mouseEyeDirection = (qAbs(dx / 5) < 1) ? 0 : dx / 5; + + setRotation(rotation() + dx); + setPos(mapToParent(0, -(3 + sin(speed) * 3))); +} +//! [11] diff --git a/examples/collidingmice/mouse.h b/examples/collidingmice/mouse.h new file mode 100644 index 00000000..c04e60e5 --- /dev/null +++ b/examples/collidingmice/mouse.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOUSE_H +#define MOUSE_H + +#include + +//! [0] +class Mouse : public QGraphicsItem +{ +public: + Mouse(); + + QRectF boundingRect() const; + QPainterPath shape() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget); + +protected: + void advance(int step); + +private: + qreal angle; + qreal speed; + qreal mouseEyeDirection; + QColor color; +}; +//! [0] + +#endif diff --git a/examples/examples.qbs b/examples/examples.qbs new file mode 100644 index 00000000..565b1703 --- /dev/null +++ b/examples/examples.qbs @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs + +Project { + references: [ + "app-and-lib/app_and_lib.qbs", + "cocoa-application/CocoaApplication.qbs", + "code-generator/code-generator.qbs", + "collidingmice/collidingmice.qbs", + "helloworld-complex/hello.qbs", + "helloworld-minimal/hello.qbs", + "helloworld-qt/hello.qbs", + "install-bundle/install-bundle.qbs", + ] +} diff --git a/examples/helloworld-complex/hello.qbs b/examples/helloworld-complex/hello.qbs new file mode 100644 index 00000000..a9f7b8fe --- /dev/null +++ b/examples/helloworld-complex/hello.qbs @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs 1.0 + +Project { + property bool hasSpecialFeature: true + Application { + name: 'HelloWorld-Complex' + + Depends { name: 'cpp' } + cpp.defines: ['SOMETHING'] + + + files: [ + "src/foo.h", + "src/foo.cpp" + ] + + Group { + condition: project.hasSpecialFeature + prefix: "src/" + files: ["specialfeature.cpp", "specialfeature.h"] + } + + Group { + cpp.defines: { + var defines = outer.concat([ + 'HAVE_MAIN_CPP', + cpp.debugInformation ? '_DEBUG' : '_RELEASE' + ]); + if (project.hasSpecialFeature) + defines.push("HAS_SPECIAL_FEATURE"); + return defines; + } + prefix: "src/" + files: [ + 'main.cpp' + ] + } + } +} diff --git a/examples/helloworld-complex/src/foo.cpp b/examples/helloworld-complex/src/foo.cpp new file mode 100644 index 00000000..681e8dce --- /dev/null +++ b/examples/helloworld-complex/src/foo.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SOMETHING +# error missing define SOMETHING +#endif + +int someUsefulFunction() +{ + return 156; +} + diff --git a/examples/helloworld-complex/src/foo.h b/examples/helloworld-complex/src/foo.h new file mode 100644 index 00000000..eda499d7 --- /dev/null +++ b/examples/helloworld-complex/src/foo.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FOO_H +#define FOO_H + +int someUsefulFunction(); + +#endif + diff --git a/examples/helloworld-complex/src/main.cpp b/examples/helloworld-complex/src/main.cpp new file mode 100644 index 00000000..a86ef43d --- /dev/null +++ b/examples/helloworld-complex/src/main.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "foo.h" + +#ifdef HAS_SPECIAL_FEATURE +#include "specialfeature.h" +#endif + +#include + +#ifndef HAVE_MAIN_CPP +# error missing define HAVE_MAIN_CPP +#endif + +#ifndef SOMETHING +# error missing define SOMETHING +#endif + +int main() +{ + someUsefulFunction(); +#ifdef _DEBUG + puts("Hello World! (debug version)"); +#else + puts("Hello World! (release version)"); +#endif +#ifdef HAS_SPECIAL_FEATURE + bragAboutSpecialFeature(); +#endif +} + diff --git a/examples/helloworld-complex/src/specialfeature.cpp b/examples/helloworld-complex/src/specialfeature.cpp new file mode 100644 index 00000000..a2e7c5a8 --- /dev/null +++ b/examples/helloworld-complex/src/specialfeature.cpp @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "specialfeature.h" + +#include + +void bragAboutSpecialFeature() +{ + std::cout << "I have a special feature!" << std::endl; +} diff --git a/examples/helloworld-complex/src/specialfeature.h b/examples/helloworld-complex/src/specialfeature.h new file mode 100644 index 00000000..d209c37d --- /dev/null +++ b/examples/helloworld-complex/src/specialfeature.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HELLO_SPECIAL_FEATURE +#define HELLO_SPECIAL_FEATURE + +void bragAboutSpecialFeature(); + +#endif diff --git a/examples/helloworld-minimal/hello.qbs b/examples/helloworld-minimal/hello.qbs new file mode 100644 index 00000000..aa539978 --- /dev/null +++ b/examples/helloworld-minimal/hello.qbs @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs + +CppApplication { + name: "HelloWorld-minimal" + files: "main.cpp" +} diff --git a/examples/helloworld-minimal/main.cpp b/examples/helloworld-minimal/main.cpp new file mode 100644 index 00000000..0c2d35ca --- /dev/null +++ b/examples/helloworld-minimal/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + std::cout << "Hello, World!" << std::endl; +} diff --git a/examples/helloworld-qt/hello.qbs b/examples/helloworld-qt/hello.qbs new file mode 100644 index 00000000..de58d3a8 --- /dev/null +++ b/examples/helloworld-qt/hello.qbs @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs + +QtApplication { + name: "HelloWorld-Qt" + files: "main.cpp" +} diff --git a/examples/helloworld-qt/main.cpp b/examples/helloworld-qt/main.cpp new file mode 100644 index 00000000..1a128d4c --- /dev/null +++ b/examples/helloworld-qt/main.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +int main() +{ + QTextStream(stdout) << QCoreApplication::translate("hello", "Hello, World!") << endl; +} diff --git a/examples/install-bundle/coreutils.cpp b/examples/install-bundle/coreutils.cpp new file mode 100644 index 00000000..f269d080 --- /dev/null +++ b/examples/install-bundle/coreutils.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "coreutils.h" + +int foo() +{ + return 42; +} diff --git a/examples/install-bundle/coreutils.h b/examples/install-bundle/coreutils.h new file mode 100644 index 00000000..fcf21a33 --- /dev/null +++ b/examples/install-bundle/coreutils.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int foo(); diff --git a/examples/install-bundle/install-bundle.qbs b/examples/install-bundle/install-bundle.qbs new file mode 100644 index 00000000..a1b58e3e --- /dev/null +++ b/examples/install-bundle/install-bundle.qbs @@ -0,0 +1,57 @@ +import qbs +import qbs.FileInfo + +Project { + CppApplication { + Depends { name: "coreutils" } + Depends { name: "Qt"; submodules: ["core", "gui", "widgets"] } + + name: "window" + targetName: bundle.isBundle ? "Window" : "window" + files: ["main.cpp"] + + property bool install: true + property string installDir: bundle.isBundle ? "Applications" : (qbs.targetOS.contains("windows") ? "" : "bin") + + Group { + fileTagsFilter: ["application"] + qbs.install: install + qbs.installDir: bundle.isBundle ? FileInfo.joinPaths(installDir, FileInfo.path(bundle.executablePath)) : installDir + } + + Group { + fileTagsFilter: ["aggregate_infoplist"] + qbs.install: install && bundle.isBundle && !bundle.embedInfoPlist + qbs.installDir: FileInfo.joinPaths(installDir, FileInfo.path(bundle.infoPlistPath)) + } + + Group { + fileTagsFilter: ["pkginfo"] + qbs.install: install && bundle.isBundle + qbs.installDir: FileInfo.joinPaths(installDir, FileInfo.path(bundle.pkgInfoPath)) + } + } + + DynamicLibrary { + Depends { name: "cpp" } + + name: "coreutils" + targetName: bundle.isBundle ? "CoreUtils" : "coreutils" + files: ["coreutils.cpp", "coreutils.h"] + + property bool install: true + property string installDir: bundle.isBundle ? "Library/Frameworks" : (qbs.targetOS.contains("windows") ? "" : "lib") + + Group { + fileTagsFilter: ["dynamiclibrary", "dynamiclibrary_symlink", "dynamiclibrary_import"] + qbs.install: install + qbs.installDir: bundle.isBundle ? FileInfo.joinPaths(installDir, FileInfo.path(bundle.executablePath)) : installDir + } + + Group { + fileTagsFilter: ["aggregate_infoplist"] + qbs.install: install && bundle.isBundle && !bundle.embedInfoPlist + qbs.installDir: FileInfo.joinPaths(installDir, FileInfo.path(bundle.infoPlistPath)) + } + } +} diff --git a/examples/install-bundle/main.cpp b/examples/install-bundle/main.cpp new file mode 100644 index 00000000..b3d80700 --- /dev/null +++ b/examples/install-bundle/main.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QWidget window; + window.show(); + return app.exec(); +} diff --git a/qbs-resources/imports/QbsApp.qbs b/qbs-resources/imports/QbsApp.qbs new file mode 100644 index 00000000..f252b336 --- /dev/null +++ b/qbs-resources/imports/QbsApp.qbs @@ -0,0 +1,29 @@ +import qbs +import qbs.FileInfo + +QbsProduct { + Depends { name: "qbscore" } + Depends { name: "cpp" } + type: "application" + consoleApplication: true + destinationDirectory: "bin" + cpp.includePaths: [ + "../shared", // for the logger + ] + cpp.cxxLanguageVersion: "c++11" + Group { + fileTagsFilter: product.type + qbs.install: true + qbs.installDir: qbsbuildconfig.appInstallDir + } + Group { + name: "logging" + prefix: FileInfo.joinPaths(product.sourceDirectory, "../shared/logging") + '/' + files: [ + "coloredoutput.cpp", + "coloredoutput.h", + "consolelogger.cpp", + "consolelogger.h" + ] + } +} diff --git a/qbs-resources/imports/QbsAutotest.qbs b/qbs-resources/imports/QbsAutotest.qbs new file mode 100644 index 00000000..66e07667 --- /dev/null +++ b/qbs-resources/imports/QbsAutotest.qbs @@ -0,0 +1,25 @@ +import qbs +import qbs.FileInfo + +QtApplication { + type: ["application", "autotest"] + consoleApplication: true + property string testName + name: "tst_" + testName + Depends { name: "Qt.testlib" } + Depends { name: "qbscore" } + Depends { name: "qbsbuildconfig" } + cpp.includePaths: "../../../src" + cpp.cxxLanguageVersion: "c++11" + destinationDirectory: "bin" + Group { + name: "logging" + prefix: FileInfo.joinPaths(product.sourceDirectory, "../../../src/app/shared/logging") + '/' + files: [ + "coloredoutput.cpp", + "coloredoutput.h", + "consolelogger.cpp", + "consolelogger.h" + ] + } +} diff --git a/qbs-resources/imports/QbsFunctions/functions.js b/qbs-resources/imports/QbsFunctions/functions.js new file mode 100644 index 00000000..8a842ad6 --- /dev/null +++ b/qbs-resources/imports/QbsFunctions/functions.js @@ -0,0 +1,18 @@ +function qbsVersion() { return "1.7.0"; } + +function versionIsAtLeast(actualVersion, expectedVersion) +{ + var actualVersionParts = actualVersion.split('.').map(function(item) { + return parseInt(item, 10); + }); + var expectedVersionParts = expectedVersion.split('.').map(function(item) { + return parseInt(item, 10); + }); + for (var i = 0; i < expectedVersionParts.length; ++i) { + if (actualVersionParts[i] > expectedVersionParts[i]) + return true; + if (actualVersionParts[i] < expectedVersionParts[i]) + return false; + } + return i === expectedVersionParts.length || expectedVersionParts[i] === 0; +} diff --git a/qbs-resources/imports/QbsLibrary.qbs b/qbs-resources/imports/QbsLibrary.qbs new file mode 100644 index 00000000..218da894 --- /dev/null +++ b/qbs-resources/imports/QbsLibrary.qbs @@ -0,0 +1,40 @@ +import qbs +import QbsFunctions + +QbsProduct { + Depends { name: "cpp" } + Depends { name: "bundle" } + version: QbsFunctions.qbsVersion() + type: Qt.core.staticBuild ? "staticlibrary" : "dynamiclibrary" + targetName: (qbs.enableDebugCode && qbs.targetOS.contains("windows")) ? (name + 'd') : name + destinationDirectory: qbs.targetOS.contains("windows") ? "bin" : qbsbuildconfig.libDirName + cpp.defines: base.concat(visibilityType === "static" ? ["QBS_STATIC_LIB"] : ["QBS_LIBRARY"]) + cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined + // ### Uncomment the following line in 1.8 + //cpp.soVersion: version.replace(/\.\d+$/, '') + cpp.visibility: "minimal" + cpp.cxxLanguageVersion: "c++11" + bundle.isBundle: false + property bool visibilityType: Qt.core.staticBuild ? "static" : "dynamic" + property string headerInstallPrefix: "/include/qbs" + Group { + fileTagsFilter: product.type.concat("dynamiclibrary_symlink") + qbs.install: install + qbs.installDir: qbsbuildconfig.libInstallDir + } + Group { + fileTagsFilter: ["dynamiclibrary_import"] + qbs.install: install + qbs.installDir: qbsbuildconfig.importLibInstallDir + } + + Export { + Depends { name: "cpp" } + Depends { name: "Qt"; submodules: ["core"] } + Depends { name: "qbsbuildconfig" } + + cpp.rpaths: qbsbuildconfig.libRPaths + cpp.includePaths: [product.sourceDirectory] + cpp.defines: product.visibilityType === "static" ? ["QBS_STATIC_LIB"] : [] + } +} diff --git a/qbs-resources/imports/QbsProduct.qbs b/qbs-resources/imports/QbsProduct.qbs new file mode 100644 index 00000000..d882852d --- /dev/null +++ b/qbs-resources/imports/QbsProduct.qbs @@ -0,0 +1,17 @@ +import qbs +import QbsFunctions + +Product { + Depends { name: "qbsbuildconfig" } + Depends { name: "Qt.core" } + property string minimumQtVersion: "5.4.0" + property bool install: true + cpp.defines: { + var res = ["QT_NO_CAST_FROM_ASCII", "QT_NO_PROCESS_COMBINED_ARGUMENT_START"]; + if (qbs.toolchain.contains("msvc")) + res.push("_SCL_SECURE_NO_WARNINGS"); + return res; + } + cpp.enableExceptions: true + condition: QbsFunctions.versionIsAtLeast(Qt.core.version, minimumQtVersion) +} diff --git a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs new file mode 100644 index 00000000..001882b1 --- /dev/null +++ b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs @@ -0,0 +1,26 @@ +import qbs + +Module { + property bool enableUnitTests: false + property bool enableProjectFileUpdates: false + property bool enableRPath: true + property bool installApiHeaders: true + property string libDirName: "lib" + property string appInstallDir: "bin" + property string libInstallDir: qbs.targetOS.contains("windows") ? "bin" : libDirName + property string importLibInstallDir: libDirName + property string libexecInstallDir: "libexec/qbs" + property string relativeLibexecPath: "../" + libexecInstallDir + property string relativePluginsPath: "../" + libDirName + property string relativeSearchPath: ".." + property stringList libRPaths: { + if (!enableRPath) + return undefined; + if (qbs.targetOS.contains("linux")) + return ["$ORIGIN/../" + libDirName]; + if (qbs.targetOS.contains("macos")) + return ["@loader_path/../" + libDirName] + } + property string resourcesInstallDir: "" + property string pluginsInstallDir: libDirName +} diff --git a/qbs.pro b/qbs.pro new file mode 100644 index 00000000..af6ba896 --- /dev/null +++ b/qbs.pro @@ -0,0 +1,59 @@ +requires(!cross_compile) + +defineTest(minQtVersion) { + maj = $$1 + min = $$2 + patch = $$3 + isEqual(QT_MAJOR_VERSION, $$maj) { + isEqual(QT_MINOR_VERSION, $$min) { + isEqual(QT_PATCH_VERSION, $$patch) { + return(true) + } + greaterThan(QT_PATCH_VERSION, $$patch) { + return(true) + } + } + greaterThan(QT_MINOR_VERSION, $$min) { + return(true) + } + } + greaterThan(QT_MAJOR_VERSION, $$maj) { + return(true) + } + return(false) +} + +!minQtVersion(5, 4, 0) { + message("Cannot build qbs with Qt version $${QT_VERSION}.") + error("Use at least Qt 5.4.0.") +} + +TEMPLATE = subdirs +corelib.file = src/lib/corelib/corelib.pro +setupqtprofilelib.subdir = src/lib/qtprofilesetup +setupqtprofilelib.depends = corelib +src_app.subdir = src/app +src_app.depends = setupqtprofilelib +src_libexec.subdir = src/libexec +src_plugins.subdir = src/plugins +tests.depends = corelib src_plugins +SUBDIRS += \ + corelib\ + setupqtprofilelib\ + src_app\ + src_libexec\ + src_plugins\ + static.pro\ + tests + +OTHER_FILES += \ + doc/*.qdoc \ + doc/reference/*.qdoc \ + doc/reference/items/*.qdoc \ + doc/reference/jsextensions/*.qdoc \ + doc/reference/modules/*.qdoc \ + doc/qbs.qdocconf \ + doc/config/qbs-project.qdocconf + +include(qbs_version.pri) +include(doc/doc.pri) diff --git a/qbs.qbs b/qbs.qbs new file mode 100644 index 00000000..85e5f6a1 --- /dev/null +++ b/qbs.qbs @@ -0,0 +1,59 @@ +import qbs 1.0 + +Project { + minimumQbsVersion: "1.6" + qbsSearchPaths: ["qbs-resources"] + property bool withExamples: false + + references: [ + "dist/dist.qbs", + "doc/doc.qbs", + "share/share.qbs", + "src/src.qbs", + "tests/auto/auto.qbs", + "tests/fuzzy-test/fuzzy-test.qbs", + "tests/benchmarker/benchmarker.qbs", + ] + + SubProject { + filePath: "examples/examples.qbs" + Properties { + condition: parent.withExamples + } + } + + Product { + name: "qmake project files for qbs" + files: ["**/*.pr[io]"] + } + + AutotestRunner { + Depends { name: "Qt.core" } + Depends { name: "qbs resources" } + Depends { name: "qbs_cpp_scanner" } + Depends { name: "qbs_qt_scanner" } + environment: { + var env = base; + if (qbs.hostOS.contains("windows") && qbs.targetOS.contains("windows")) { + var path = ""; + for (var i = 0; i < env.length; ++i) { + if (env[i].startsWith("PATH=")) { + path = env[i].substring(5); + break; + } + } + path = Qt.core.binPath + ";" + path; + var arrayElem = "PATH=" + path; + if (i < env.length) + env[i] = arrayElem; + else + env.push(arrayElem); + } + if (qbs.hostOS.contains("darwin") && qbs.targetOS.contains("darwin")) { + env.push("DYLD_FRAMEWORK_PATH=" + Qt.core.libPath); + env.push("DYLD_LIBRARY_PATH=" + Qt.core.libPath); + } + return env; + } + } +} diff --git a/qbs_version.pri b/qbs_version.pri new file mode 100644 index 00000000..55dff2e6 --- /dev/null +++ b/qbs_version.pri @@ -0,0 +1,4 @@ +QBS_VERSION = 1.7.0 +QBS_VERSION_MAJ = $$section(QBS_VERSION, ., 0, 0) +QBS_VERSION_MIN = $$section(QBS_VERSION, ., 1, 1) +DEFINES += QBS_VERSION=\\\"$$QBS_VERSION\\\" diff --git a/share/qbs/imports/qbs/BundleTools/bundle-tools.js b/share/qbs/imports/qbs/BundleTools/bundle-tools.js new file mode 100644 index 00000000..f8198370 --- /dev/null +++ b/share/qbs/imports/qbs/BundleTools/bundle-tools.js @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = loadExtension("qbs.FileInfo"); +var DarwinTools = loadExtension("qbs.DarwinTools"); +var PropertyList = loadExtension("qbs.PropertyList"); + +function destinationDirectoryForResource(product, input) { + var path = product.destinationDirectory; + var inputFilePath = FileInfo.joinPaths(input.baseDir, input.fileName); + var key = DarwinTools.localizationKey(inputFilePath); + if (key) { + path = FileInfo.joinPaths(path, localizedResourcesFolderPath(product, key)); + var subPath = DarwinTools.relativeResourcePath(inputFilePath); + if (subPath && subPath !== '.') + path = FileInfo.joinPaths(path, subPath); + } else { + path = FileInfo.joinPaths(path, product.moduleProperty("bundle", "unlocalizedResourcesFolderPath")); + } + return path; +} + +function localizedResourcesFolderPath(product, key) { + return FileInfo.joinPaths(product.moduleProperty("bundle", "unlocalizedResourcesFolderPath"), key + product.moduleProperty("bundle", "localizedResourcesFolderSuffix")); +} + +function infoPlistContents(infoPlistFilePath) { + if (infoPlistFilePath === undefined) + return undefined; + + var plist = new PropertyList(); + try { + plist.readFromFile(infoPlistFilePath); + return plist.toObject(); + } finally { + plist.clear(); + } +} + +function infoPlistFormat(infoPlistFilePath) { + if (infoPlistFilePath === undefined) + return undefined; + + var plist = new PropertyList(); + try { + plist.readFromFile(infoPlistFilePath); + return plist.format(); + } finally { + plist.clear(); + } +} diff --git a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js new file mode 100644 index 00000000..aceae7a9 --- /dev/null +++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = loadExtension("qbs.FileInfo"); +var Utilities = loadExtension("qbs.Utilities"); + +var _deviceMap = { + "mac": undefined, // only devices have an ID + "iphone": 1, + "ipad": 2, + "tv": 3, + "watch": 4, + "car": 5 +}; + +var _platformMap = { + "ios": "iPhone", + "macos": "MacOSX", + "tvos": "AppleTV", + "watchos": "Watch" +}; + +var _platformDeviceMap = { + "ios": ["iphone", "ipad"], + "macos": ["mac"], + "tvos": ["tv"], + "watchos": ["watch"] +} + +/** + * Returns the numeric identifier corresponding to an Apple device name + * (i.e. for use by TARGETED_DEVICE_FAMILY). + */ +function appleDeviceNumber(deviceName) { + return _deviceMap[deviceName]; +} + +/** + * Returns the list of target devices available for the given qbs target OS list. + */ +function targetDevices(targetOS) { + for (var key in _platformDeviceMap) { + if (targetOS.contains(key)) + return _platformDeviceMap[key]; + } +} + +/** + * Returns the TARGETED_DEVICE_FAMILY string given a list of target device names. + */ +function targetedDeviceFamily(deviceNames) { + return deviceNames.map(function (deviceName) { + return appleDeviceNumber(deviceName); + }).join(","); +} + +/** + * Returns the most appropriate Apple platform name given a targetOS list. + */ +function applePlatformName(targetOSList, platformType) { + return applePlatformDirectoryName(targetOSList, platformType).toLowerCase(); +} + +/** + * Returns the most appropriate Apple platform directory name given a targetOS list and version. + */ +function applePlatformDirectoryName(targetOSList, platformType, version, throwOnError) { + var suffixMap = { + "device": "OS", + "simulator": "Simulator" + }; + + for (var key in _platformMap) { + if (targetOSList.contains(key)) + return _platformMap[key] + (suffixMap[platformType] || "") + (version || ""); + } + + if (throwOnError || throwOnError === undefined) + throw("No Apple platform corresponds to target OS list: " + targetOSList); +} + +/** + * Returns the localization of the resource at the given path, + * or undefined if the path does not contain an {xx}.lproj path segment. + */ +function localizationKey(path) { + return _resourceFileProperties(path)[0]; +} + +/** + * Returns the path of a localized resource at the given path, + * relative to its containing {xx}.lproj directory, or '.' + * if the resource is unlocalized (not contained in an lproj directory). + */ +function relativeResourcePath(path) { + return _resourceFileProperties(path)[1]; +} + +function _resourceFileProperties(path) { + var lprojKey = ".lproj/"; + var lproj = path.indexOf(lprojKey); + if (lproj >= 0) { + // The slash preceding XX.lproj + var slashIndex = path.slice(0, lproj).lastIndexOf('/'); + if (slashIndex >= 0) { + var localizationKey = path.slice(slashIndex + 1, lproj); + var relativeResourcePath = FileInfo.path(path.slice(lproj + lprojKey.length)); + return [ localizationKey, relativeResourcePath ]; + } + } + + return [ undefined, '.' ]; +} + +/** + * Recursively perform variable replacements in an environment dictionary. + * + * JSON.stringify(expandPlistEnvironmentVariables({a:"$(x)3$$(y)",b:{t:"%$(y) $(k)"}}, + * {x:"X",y:"Y"}, true)) + * Warning undefined variable k in variable expansion + * => {"a":"X3$Y","b":{"t":"%Y $(k)"}} + */ +function expandPlistEnvironmentVariables(obj, env, warn) { + // Possible syntaxes for wrapping an environment variable name + var syntaxes = [ + {"open": "${", "close": "}"}, + {"open": "$(", "close": ")"}, + {"open": "@", "close": "@"} + ]; + + /** + * Finds the first index of a replacement starting with one of the supported syntaxes + * This is needed so we don't do recursive substitutions + */ + function indexOfReplacementStart(syntaxes, str, offset) { + var syntax; + var idx = str.length; + for (var i in syntaxes) { + var j = str.indexOf(syntaxes[i].open, offset); + if (j !== -1 && j < idx) { + syntax = syntaxes[i]; + idx = j; + } + } + return { "syntax": syntax, "index": idx === str.length ? -1 : idx }; + } + + function expandRecursive(obj, env, checked) { + checked.push(obj); + for (var key in obj) { + var value = obj[key]; + var type = typeof(value); + if (type === "object") { + if (checked.indexOf(value) !== -1) + continue; + expandRecursive(value, env, checked); + } + if (type !== "string") + continue; + var repl = indexOfReplacementStart(syntaxes, value); + var i = repl.index; + var changes = false; + while (i !== -1) { + var j = value.indexOf(repl.syntax.close, i + repl.syntax.open.length); + if (j === -1) + break; + var varParts = value.slice(i + repl.syntax.open.length, j).split(':'); + var varName = varParts[0]; + var varFormatter = varParts[1]; + var varValue = env[varName]; + if (undefined === varValue) { + // skip replacement + if (warn) + console.warn("undefined variable " + varName + " in variable expansion"); + i = j + repl.syntax.close.length; + } else { + changes = true; + varValue = String(varValue); + if (varFormatter !== undefined) + varFormatter = varFormatter.toLowerCase(); + if (varFormatter === "rfc1034identifier") + varValue = Utilities.rfc1034Identifier(varValue); + value = value.slice(0, i) + varValue + value.slice(j + repl.syntax.close.length); + // avoid recursive substitutions to avoid potentially infinite loops + i += varValue.length; + } + repl = indexOfReplacementStart(syntaxes, value, i); + i = repl.index; + } + if (changes) + obj[key] = value; + } + } + expandRecursive(obj, env, []); + return obj; +} + +/** + * Recursively removes any undefined, null, or empty string values from the property list. + */ +function cleanPropertyList(plist) { + if (typeof(plist) !== "object") + return; + + for (var key in plist) { + if (plist[key] === undefined || plist[key] === null || plist[key] === "") + delete plist[key]; + else + cleanPropertyList(plist[key]); + } +} + +function _codeSignTimestampFlags(product) { + // If signingTimestamp is undefined, do not specify the flag at all - + // this uses the system-specific default behavior + var signingTimestamp = product.moduleProperty("xcode", "signingTimestamp"); + if (signingTimestamp !== undefined) { + // If signingTimestamp is an empty string, specify the flag but do + // not specify a value - this uses a default Apple-provided server + var flag = "--timestamp"; + if (signingTimestamp) + flag += "=" + signingTimestamp; + return [flag]; + } + + return []; +} diff --git a/share/qbs/imports/qbs/ModUtils/utils.js b/share/qbs/imports/qbs/ModUtils/utils.js new file mode 100644 index 00000000..800a58bc --- /dev/null +++ b/share/qbs/imports/qbs/ModUtils/utils.js @@ -0,0 +1,579 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var Environment = loadExtension("qbs.Environment"); +var File = loadExtension("qbs.File"); +var FileInfo = loadExtension("qbs.FileInfo"); +var Process = loadExtension("qbs.Process"); +var TemporaryDir = loadExtension("qbs.TemporaryDir"); +var Utilities = loadExtension("qbs.Utilities"); + +function sanitizedList(list, product, fullPropertyName) { + if (!Array.isArray(list)) + return list; + var filterFunc = function(elem) { + if (elem.length === 0) { + console.warn("Removing empty string from value of property '" + fullPropertyName + + "' in product '" + product.name + "'."); + return false; + } + return true; + } + return list.filter(filterFunc); +} + +function checkCompatibilityMode(project, minimumQbsVersion, message) { + if (Utilities.versionCompare(project.minimumQbsVersion || "1.3", minimumQbsVersion) < 0) { + console.warn([message || "", + "This message can be silenced by setting your Project's " + + "minimumQbsVersion to " + minimumQbsVersion + + " (and the new behavior will take effect)."].join(" ")); + return true; + } + + return false; +} + +function artifactInstalledFilePath(artifact) { + var relativeInstallDir = artifact.moduleProperty("qbs", "installDir"); + var installPrefix = artifact.moduleProperty("qbs", "installPrefix"); + var installSourceBase = artifact.moduleProperty("qbs", "installSourceBase"); + var targetDir = FileInfo.joinPaths(artifact.moduleProperty("qbs", "installRoot"), + installPrefix, relativeInstallDir); + if (installSourceBase) { + if (!FileInfo.isAbsolutePath(installSourceBase)) + throw "installSourceBase is not an absolute path"; + if (!artifact.filePath.startsWith(installSourceBase)) + throw "artifact file path doesn't start with the value of qbs.installSourceBase"; + return FileInfo.joinPaths(targetDir, artifact.filePath.substr(installSourceBase.length + 1)); + } + return FileInfo.joinPaths(targetDir, artifact.fileName); +} + +/** + * Given a list of file tags, returns the file tag (one of [c, cpp, objc, objcpp]) + * corresponding to the C-family language the file should be compiled as. + * + * If no such tag is found, undefined is returned. If more than one match is + * found, an exception is thrown. + */ +function fileTagForTargetLanguage(fileTags) { + var srcTags = ["c", "cpp", "objc", "objcpp", "asm", "asm_cpp"]; + var pchTags = ["c_pch", "cpp_pch", "objc_pch", "objcpp_pch"]; + + var canonicalTag = undefined; + var foundTagCount = 0; + for (var i = 0; i < fileTags.length; ++i) { + var idx = srcTags.indexOf(fileTags[i]); + if (idx === -1) + idx = pchTags.indexOf(fileTags[i]); + + if (idx !== -1) { + canonicalTag = srcTags[idx]; + if (++foundTagCount > 1) + break; + } + } + + if (foundTagCount > 1) + throw ("source files cannot be identified as more than one language"); + + return foundTagCount == 1 ? canonicalTag : undefined; +} + +/** + * Returns the name of a language-specific property given the file tag + * for that property, and the base property name. + * + * If \a fileTag is undefined, the language-agnostic property name is returned. + * + * @param propertyName flags, platformFlags, precompiledHeader + * @param fileTag c, cpp, objc, objcpp + */ +function languagePropertyName(propertyName, fileTag) { + if (!fileTag) + fileTag = "common"; + + var asm = { + "flags": "assemblerFlags", + "platformFlags": "platformAssemblerFlags" + }; + + var map = { + "c": { + "flags": "cFlags", + "platformFlags": "platformCFlags", + "usePrecompiledHeader": "useCPrecompiledHeader" + }, + "cpp": { + "flags": "cxxFlags", + "platformFlags": "platformCxxFlags", + "usePrecompiledHeader": "useCxxPrecompiledHeader" + }, + "objc": { + "flags": "objcFlags", + "platformFlags": "platformObjcFlags", + "usePrecompiledHeader": "useObjcPrecompiledHeader" + }, + "objcpp": { + "flags": "objcxxFlags", + "platformFlags": "platformObjcxxFlags", + "usePrecompiledHeader": "useObjcxxPrecompiledHeader" + }, + "common": { + "flags": "commonCompilerFlags", + "platformFlags": "platformCommonCompilerFlags" + }, + "asm": asm, + "asm_cpp": asm + }; + + var lang = map[fileTag]; + if (!lang) + return propertyName; + + return lang[propertyName] || propertyName; +} + +function modulePropertiesFromArtifacts(product, artifacts, moduleName, propertyName, langFilter) { + var result = product.moduleProperty( + moduleName, languagePropertyName(propertyName, langFilter)) || []; + for (var i in artifacts) { + var artifactProp = artifacts[i].moduleProperty( + moduleName, languagePropertyName(propertyName, langFilter)); + if (artifactProp) + result = result.concat(artifactProp); + } + return sanitizedList(result, product, moduleName + "." + propertyName); +} + +function moduleProperty(product, propertyName, langFilter) +{ + return sanitizedList(product.moduleProperty(product.moduleName, + languagePropertyName(propertyName, langFilter)), + product, product.moduleName + "." + propertyName); +} + +/** + * Returns roughly the same value as moduleProperty for a product, but ensures that all of the + * given input artifacts share the same value of said property, as a sort of sanity check. + * + * This allows us to verify that users do not, for example, try to set different values on input + * artifacts for which the value is input specific (not product specific), but which must be the + * same for all inputs. + */ +function modulePropertyFromArtifacts(product, artifacts, moduleName, propertyName, langFilter) { + var values = [product.moduleProperty(moduleName, languagePropertyName(propertyName, langFilter))]; + for (var i in artifacts) { + var value = artifacts[i].moduleProperty(moduleName, languagePropertyName(propertyName, langFilter)); + if (!values.contains(value)) { + values.push(value); + } + } + + if (values.length !== 1) { + throw "The value of " + [moduleName, propertyName].join(".") + + " must be identical for the following input artifacts: " + + artifacts.map(function (artifact) { return artifact.filePath; }); + } + + return values[0]; +} + +function dumpProperty(key, value, level) { + var indent = ""; + for (var k = 0; k < level; ++k) + indent += " "; + console.info(indent + key + ": " + value); +} + +function traverseObject(obj, func, level) { + if (!level) + level = 0; + var i, children = {}; + for (i in obj) { + if (typeof(obj[i]) === "object" && !(obj[i] instanceof Array)) + children[i] = obj[i]; + else + func.apply(this, [i, obj[i], level]); + } + level++; + for (i in children) { + func.apply(this, [i, children[i], level - 1]); + traverseObject(children[i], func, level); + } + level--; +} + +function dumpObject(obj, description) { + if (!description) + description = "object dump"; + console.info("+++++++++ " + description + " +++++++++"); + traverseObject(obj, dumpProperty); +} + +function concatAll() { + var result = []; + for (var i = 0; i < arguments.length; ++i) { + var arg = arguments[i]; + if (arg === undefined) + continue; + else if (arg instanceof Array) + result = result.concat(arg); + else + result.push(arg); + } + return result; +} + +function allFileTags(fileTaggers) { + var tags = []; + for (var ext in fileTaggers) + tags = tags.uniqueConcat(fileTaggers[ext]); + return tags; +} + +/** + * Flattens an environment dictionary (string keys to arrays or strings) + * into a string list containing items like \c key=value1:value2:value3 + */ +function flattenEnvironmentDictionary(dict, pathListSeparator) { + var list = []; + for (var i in dict) + list.push(i + "=" + dict[i]); + return list; +} + +var EnvironmentVariable = (function () { + function EnvironmentVariable(name, separator, convertPathSeparators) { + if (!name) + throw "EnvironmentVariable c'tor needs a name as first argument."; + this.name = name; + this.value = Environment.getEnv(name) || ""; + this.separator = separator || ""; + this.convertPathSeparators = convertPathSeparators || false; + } + EnvironmentVariable.prototype.prepend = function (v) { + if (this.value.length > 0 && this.value.charAt(0) !== this.separator) + this.value = this.separator + this.value; + if (this.convertPathSeparators) + v = FileInfo.toWindowsSeparators(v); + this.value = v + this.value; + }; + + EnvironmentVariable.prototype.append = function (v) { + if (this.value.length > 0) + this.value += this.separator; + if (this.convertPathSeparators) + v = FileInfo.toWindowsSeparators(v); + this.value += v; + }; + + EnvironmentVariable.prototype.set = function () { + Environment.putEnv(this.name, this.value); + }; + + EnvironmentVariable.prototype.unset = function () { + Environment.unsetEnv(this.name); + }; + + return EnvironmentVariable; +})(); + +var PropertyValidator = (function () { + function PropertyValidator(moduleName) { + this.requiredProperties = {}; + this.propertyValidators = []; + if (!moduleName) + throw "PropertyValidator c'tor needs a module name as a first argument."; + this.moduleName = moduleName; + } + PropertyValidator.prototype.setRequiredProperty = function (propertyName, propertyValue, message) { + this.requiredProperties[propertyName] = { propertyValue: propertyValue, message: message }; + }; + + PropertyValidator.prototype.addRangeValidator = function (propertyName, propertyValue, min, max, allowFloats) { + var message = []; + if (min !== undefined) + message.push(">= " + min); + if (max !== undefined) + message.push("<= " + max); + + this.addCustomValidator(propertyName, propertyValue, function (value) { + if (typeof value !== "number") + return false; + if (!allowFloats && value % 1 !== 0) + return false; + if (min !== undefined && value < min) + return false; + if (max !== undefined && value > max) + return false; + return true; + }, "must be " + (!allowFloats ? "an integer " : "") + message.join(" and ")); + }; + + PropertyValidator.prototype.addVersionValidator = function (propertyName, propertyValue, minComponents, maxComponents, allowSuffixes) { + if (minComponents !== undefined && (typeof minComponents !== "number" || minComponents % 1 !== 0 || minComponents < 1)) + throw "minComponents must be at least 1"; + if (maxComponents !== undefined && (typeof maxComponents !== "number" || maxComponents % 1 !== 0 || maxComponents < minComponents)) + throw "maxComponents must be >= minComponents"; + + this.addCustomValidator(propertyName, propertyValue, function (value) { + if (typeof value !== "string") + return false; + return value && value.match("^[0-9]+(\\.[0-9]+){" + ((minComponents - 1) || 0) + "," + ((maxComponents - 1) || "") + "}" + (!allowSuffixes ? "$" : "")) !== null; + }, "must be a version number with " + (minComponents === maxComponents + ? minComponents : (minComponents + " to " + maxComponents)) + + (minComponents === maxComponents && minComponents === 1 + ? " component" : " components")); + }; + + PropertyValidator.prototype.addFileNameValidator = function (propertyName, propertyValue) { + this.addCustomValidator(propertyName, propertyValue, function (value) { + return !/[/?<>\\:*|"\u0000-\u001f\u0080-\u009f]/.test(propertyValue) + && propertyValue !== "." && propertyValue !== ".."; + }, "cannot contain reserved or control characters and cannot be \".\" or \"..\""); + }; + + PropertyValidator.prototype.addCustomValidator = function (propertyName, propertyValue, validator, message) { + this.propertyValidators.push({ + propertyName: propertyName, + propertyValue: propertyValue, + validator: validator, + message: message + }); + }; + + PropertyValidator.prototype.validate = function (throwOnError) { + var i; + var lines; + + // Find any missing properties + var missingProperties = {}; + for (i in this.requiredProperties) { + var propValue = this.requiredProperties[i].propertyValue; + if (propValue === undefined || propValue === null || propValue === "") { + missingProperties[i] = this.requiredProperties[i]; + } + } + + // Find any properties that don't satisfy their validator function + var invalidProperties = {}; + for (var j = 0; j < this.propertyValidators.length; ++j) { + var v = this.propertyValidators[j]; + if (!v.validator(v.propertyValue)) { + var messages = invalidProperties[v.propertyName] || []; + messages.push(v.message); + invalidProperties[v.propertyName] = messages; + } + } + + var errorMessage = ""; + if (Object.keys(missingProperties).length > 0) { + errorMessage += "The following properties are not set. Set them in your profile or product:\n"; + lines = []; + for (i in missingProperties) { + var obj = missingProperties[i]; + lines.push(this.moduleName + "." + i + ((obj && obj.message) ? (": " + obj.message) : "")); + } + errorMessage += lines.join("\n"); + } + + if (Object.keys(invalidProperties).length > 0) { + if (errorMessage) + errorMessage += "\n"; + errorMessage += "The following properties have invalid values:\n"; + lines = []; + for (i in invalidProperties) { + for (j in invalidProperties[i]) { + lines.push(this.moduleName + "." + i + ": " + invalidProperties[i][j]); + } + } + errorMessage += lines.join("\n"); + } + + if (throwOnError !== false && errorMessage.length > 0) + throw errorMessage; + + return errorMessage.length == 0; + }; + return PropertyValidator; +})(); + +var BlackboxOutputArtifactTracker = (function () { + function BlackboxOutputArtifactTracker() { + } + BlackboxOutputArtifactTracker.prototype.artifacts = function (outputDirectory) { + var process; + var fakeOutputDirectory; + try { + fakeOutputDirectory = new TemporaryDir(); + if (!fakeOutputDirectory.isValid()) + throw "could not create temporary directory"; + process = new Process(); + if (this.commandEnvironmentFunction) { + var env = this.commandEnvironmentFunction(fakeOutputDirectory.path()); + for (var key in env) + process.setEnv(key, env[key]); + } + process.exec(this.command, this.commandArgsFunction(fakeOutputDirectory.path()), true); + var artifacts = []; + if (this.fileTaggers) { + var files = this.findFiles(fakeOutputDirectory.path()); + for (var i = 0; i < files.length; ++i) + artifacts.push(this.createArtifact(fakeOutputDirectory.path(), files[i])); + } + if (this.processStdOutFunction) + artifacts = artifacts.concat(this.processStdOutFunction(process.readStdOut())); + artifacts = this.fixArtifactPaths(artifacts, outputDirectory, fakeOutputDirectory.path()); + return artifacts; + } + finally { + if (process) + process.close(); + if (fakeOutputDirectory) + fakeOutputDirectory.remove(); + } + }; + BlackboxOutputArtifactTracker.prototype.createArtifact = function (root, filePath) { + for (var ext in this.fileTaggers) { + if (filePath.endsWith(ext)) { + return { + filePath: filePath, + fileTags: this.fileTaggers[ext] + }; + } + } + if (!this.defaultFileTags) { + var relFilePath = (filePath.startsWith(root + '/') || filePath.startsWith(root + '\\')) + ? filePath.substring(root.length + 1) + : filePath; + throw "BlackboxOutputArtifactTracker: no matching file taggers for path '" + + relFilePath + "'. Set defaultFileTags to an array of file tags to " + + "apply to files not tagged by the fileTaggers map, which was:\n" + + JSON.stringify(this.fileTaggers, undefined, 4); + } + return { + filePath: filePath, + fileTags: this.defaultFileTags + }; + }; + // TODO: Use File.directoryEntries + BlackboxOutputArtifactTracker.prototype.findFiles = function (dir) { + var proc; + try { + proc = new Process(); + if (this.hostOS && this.hostOS.contains("windows")) + proc.exec(this.shellPath, ["/C", "dir", FileInfo.toWindowsSeparators(dir), + "/B", "/S", "/A:-D"], true); + else + proc.exec("find", [dir, "-type", "f"], true); + return proc.readStdOut().trim().split(/\r?\n/).map( + function(p) { return FileInfo.fromWindowsSeparators(p); }); + } + finally { + if (proc) + proc.close(); + } + }; + BlackboxOutputArtifactTracker.prototype.fixArtifactPaths = function (artifacts, realBasePath, fakeBasePath) { + for (var i = 0; i < artifacts.length; ++i) + artifacts[i].filePath = realBasePath + + artifacts[i].filePath.substr(fakeBasePath.length); + return artifacts; + }; + return BlackboxOutputArtifactTracker; +})(); + +function guessArchitecture(m) { + function hasAnyOf(m, tokens) { + for (var i = 0; i < tokens.length; ++i) { + if (m[tokens[i]] !== undefined) + return true; + } + } + + var architecture; + if (m) { + // based on the search algorithm from qprocessordetection.h in qtbase + if (hasAnyOf(m, ["__arm__", "__TARGET_ARCH_ARM", "_M_ARM", "__aarch64__"])) { + if (hasAnyOf(m, ["__aarch64__"])) { + architecture = "arm64"; + } else { + architecture = "arm"; + + var foundSubarch = false; + for (var i = 7; i >= 4; --i) { + var codes = ["zk", "tej", "te", "t2"].concat([].concat.apply([], + new Array(26)).map(function(_, i) { return String.fromCharCode(122 - i); })); + for (var j = 0; j < codes.length; ++j) { + if (m["__ARM_ARCH_" + i + codes[j].toUpperCase() + "__"] !== undefined) { + architecture += "v" + i + codes[j].toLowerCase(); + foundSubarch = true; + break; + } + } + + if (i === 7 && m["_ARM_ARCH_7"] !== undefined) { + architecture += "v7"; + foundSubarch = true; + } + + if (foundSubarch) + break; + } + } + } else if (hasAnyOf(m, ["__i386", "__i386__", "_M_IX86"])) { + architecture = "x86"; + } else if (hasAnyOf(m, ["__x86_64", "__x86_64__", "__amd64", "_M_X64", "_M_AMD64"])) { + architecture = "x86_64"; + } else if (hasAnyOf(m, ["__ia64", "__ia64__", "_M_IA64"])) { + architecture = "ia64"; + } else if (hasAnyOf(m, ["__mips", "__mips__", "_M_MRX000"])) { + architecture = "mips"; + if (hasAnyOf(m, ["_MIPS_ARCH_MIPS64", "__mips64"])) + architecture += "64"; + } else if (hasAnyOf(m, ["__ppc__", "__ppc", "__powerpc__", + "_ARCH_COM", "_ARCH_PWR", "_ARCH_PPC", "_M_MPPC", "_M_PPC"])) { + architecture = "ppc"; + if (hasAnyOf(m, ["__ppc64__", "__powerpc64__", "__64BIT__"])) + architecture += "64"; + } else if (hasAnyOf(m, ["__s390__"])) { + if (hasAnyOf(m, ["__s390x__"])) + architecture = "s390x"; + } else if (hasAnyOf(m, ["__sparc__"])) { + architecture = "sparc"; + if (hasAnyOf(m, ["__sparc64__"])) + architecture += "64"; + } + } + + return Utilities.canonicalArchitecture(architecture); +} diff --git a/share/qbs/imports/qbs/PathTools/path-tools.js b/share/qbs/imports/qbs/PathTools/path-tools.js new file mode 100644 index 00000000..c39ddcab --- /dev/null +++ b/share/qbs/imports/qbs/PathTools/path-tools.js @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = loadExtension("qbs.FileInfo"); + +function applicationFileName(product) { + return product.moduleProperty("cpp", "executablePrefix") + + product.targetName + + product.moduleProperty("cpp", "executableSuffix"); +} + +function applicationFilePath(product) { + if (product.moduleProperty("bundle", "isBundle")) + return product.moduleProperty("bundle", "executablePath"); + else + return applicationFileName(product); +} + +function loadableModuleFileName(product) { + return product.moduleProperty("cpp", "loadableModulePrefix") + + product.targetName + + product.moduleProperty("cpp", "loadableModuleSuffix"); +} + +function loadableModuleFilePath(product) { + if (product.moduleProperty("bundle", "isBundle")) + return product.moduleProperty("bundle", "executablePath"); + else + return loadableModuleFileName(product); +} + +function staticLibraryFileName(product) { + return product.moduleProperty("cpp", "staticLibraryPrefix") + + product.targetName + + product.moduleProperty("cpp", "staticLibrarySuffix"); +} + +function staticLibraryFilePath(product) { + if (product.moduleProperty("bundle", "isBundle")) + return product.moduleProperty("bundle", "executablePath"); + else + return staticLibraryFileName(product); +} + +function dynamicLibraryFileName(product, version, maxParts) { + // If no override version was given, use the product's version + // We specifically want to differentiate between undefined and i.e. + // empty string as empty string should be taken to mean "no version" + // and undefined should be taken to mean "use the product's version" + // (which could still end up being "no version") + if (version === undefined) + version = product.moduleProperty("cpp", "internalVersion"); + + // If we have a version number, potentially strip off some components + maxParts = parseInt(maxParts, 10); + if (maxParts === 0) + version = undefined; + else if (maxParts && version) + version = version.split('.').slice(0, maxParts).join('.'); + + // Start with prefix + name (i.e. libqbs, qbs) + var fileName = product.moduleProperty("cpp", "dynamicLibraryPrefix") + product.targetName; + + // For Mach-O images, append the version number if there is one (i.e. libqbs.1.0.0) + var imageFormat = product.moduleProperty("cpp", "imageFormat"); + if (version && imageFormat === "macho") { + fileName += "." + version; + version = undefined; + } + + // Append the suffix (i.e. libqbs.1.0.0.dylib, libqbs.so, qbs.dll) + fileName += product.moduleProperty("cpp", "dynamicLibrarySuffix"); + + // For ELF images, append the version number if there is one (i.e. libqbs.so.1.0.0) + if (version && imageFormat === "elf") + fileName += "." + version; + + return fileName; +} + +function dynamicLibraryFilePath(product, version, maxParts) { + if (product.moduleProperty("bundle", "isBundle")) + return product.moduleProperty("bundle", "executablePath"); + else + return dynamicLibraryFileName(product, version, maxParts); +} + +function importLibraryFilePath(product) { + return product.moduleProperty("cpp", "dynamicLibraryPrefix") + + product.targetName + + product.moduleProperty("cpp", "dynamicLibraryImportSuffix"); +} + +function debugInfoIsBundle(product) { + var flags = product.moduleProperty("cpp", "dsymutilFlags"); + return !flags.contains("-f") && !flags.contains("--flat"); +} + +function debugInfoFileName(product) { + var suffix = ""; + + // For dSYM bundles, the DWARF debug info file has no suffix + if (!product.moduleProperty("qbs", "targetOS").contains("darwin") + || !debugInfoIsBundle(product)) + suffix = product.moduleProperty("cpp", "debugInfoSuffix"); + + if (!product.moduleProperty("bundle", "isBundle")) { + if (product.type.contains("application")) + return applicationFileName(product) + suffix; + else if (product.type.contains("dynamiclibrary")) + return dynamicLibraryFileName(product) + suffix; + else if (product.type.contains("loadablemodule")) + return loadableModuleFileName(product) + suffix; + else if (product.type.contains("staticlibrary")) + return staticLibraryFileName(product) + suffix; + } + + return product.targetName + suffix; +} + +function debugInfoBundlePath(product) { + if (!debugInfoIsBundle(product)) + return undefined; + var suffix = product.moduleProperty("cpp", "debugInfoBundleSuffix"); + if (product.moduleProperty("qbs", "targetOS").contains("darwin") + && product.moduleProperty("bundle", "isBundle")) + return product.moduleProperty("bundle", "bundleName") + suffix; + return debugInfoFileName(product) + suffix; +} + +function debugInfoFilePath(product) { + var name = debugInfoFileName(product); + if (product.moduleProperty("qbs", "targetOS").contains("darwin") && debugInfoIsBundle(product)) { + return FileInfo.joinPaths(debugInfoBundlePath(product), "Contents", "Resources", "DWARF", + name); + } else if (product.moduleProperty("bundle", "isBundle")) { + return FileInfo.joinPaths(product.moduleProperty("bundle", "executableFolderPath"), name); + } + + return name; +} + +function debugInfoPlistFilePath(product) { + if (!debugInfoIsBundle(product)) + return undefined; + return FileInfo.joinPaths(debugInfoBundlePath(product), "Contents", "Info.plist"); +} + +// Returns whether the string looks like a library filename +function isLibraryFileName(product, fileName, prefix, suffixes, isShared) { + var suffix, i; + var os = product.moduleProperty("qbs", "targetOS"); + for (i = 0; i < suffixes.length; ++i) { + suffix = suffixes[i]; + if (isShared && os.contains("unix") && !os.contains("darwin")) + suffix += "(\\.[0-9]+){0,3}"; + if (fileName.match("^" + prefix + ".+?\\" + suffix + "$")) + return true; + } + return false; +} + +function frameworkExecutablePath(frameworkPath) { + var suffix = ".framework"; + var isAbsoluteFrameworkPath = frameworkPath.slice(-suffix.length) === suffix; + if (isAbsoluteFrameworkPath) { + var frameworkName = FileInfo.fileName(frameworkPath).slice(0, -suffix.length); + return FileInfo.joinPaths(frameworkPath, frameworkName); + } + return undefined; +} + +// pathList is also a string, using the respective separator +function prependOrSetPath(path, pathList, separator) { + if (!pathList || pathList.length === 0) + return path; + return path + separator + pathList; +} diff --git a/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs b/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs new file mode 100644 index 00000000..5673d227 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.Environment +import qbs.File +import qbs.FileInfo + +PathProbe { + // Inputs + property stringList hostOS: qbs.hostOS + property path sdkPath + + environmentPaths: Environment.getEnv("ANDROID_NDK_ROOT") + platformPaths: { + var paths = []; + if (sdkPath) + paths.push(FileInfo.joinPaths(sdkPath, "ndk-bundle")); + if (qbs.hostOS.contains("windows")) + paths.push(FileInfo.joinPaths(Environment.getEnv("LOCALAPPDATA"), + "Android", "sdk", "ndk-bundle")); + if (qbs.hostOS.contains("macos")) + paths.push(FileInfo.joinPaths(Environment.getEnv("HOME"), + "Library", "Android", "sdk", "ndk-bundle")); + if (qbs.hostOS.contains("linux")) + paths.push(FileInfo.joinPaths(Environment.getEnv("HOME"), + "Android", "Sdk", "ndk-bundle")); + return paths; + } + + // Outputs + property var hostArch + property stringList toolchains: [] + + configure: { + var i, j, allPaths = (environmentPaths || []).concat(platformPaths || []); + for (i in allPaths) { + var platforms = []; + if (hostOS.contains("windows")) + platforms.push("windows-x86_64", "windows"); + if (hostOS.contains("darwin")) + platforms.push("darwin-x86_64", "darwin-x86"); + if (hostOS.contains("linux")) + platforms.push("linux-x86_64", "linux-x86"); + for (j in platforms) { + if (File.exists(FileInfo.joinPaths(allPaths[i], "prebuilt", platforms[j]))) { + path = allPaths[i]; + hostArch = platforms[j]; + toolchains = File.directoryEntries(FileInfo.joinPaths(path, "toolchains"), + File.Dirs | File.NoDotAndDotDot); + found = true; + return; + } + } + } + } +} diff --git a/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs b/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs new file mode 100644 index 00000000..acd5c000 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.Environment +import qbs.File +import qbs.FileInfo +import "../../../modules/Android/sdk/utils.js" as SdkUtils + +PathProbe { + environmentPaths: Environment.getEnv("ANDROID_HOME") + platformPaths: { + if (qbs.hostOS.contains("windows")) + return [FileInfo.joinPaths(Environment.getEnv("LOCALAPPDATA"), "Android", "sdk")]; + if (qbs.hostOS.contains("macos")) + return [FileInfo.joinPaths(Environment.getEnv("HOME"), "Library", "Android", "sdk")]; + if (qbs.hostOS.contains("linux")) + return [FileInfo.joinPaths(Environment.getEnv("HOME"), "Android", "Sdk")]; + } + + // Outputs + property var buildToolsVersions + property string buildToolsVersion + property var platforms + property string platform + + configure: { + var i, allPaths = (environmentPaths || []).concat(platformPaths || []); + for (i in allPaths) { + if (File.exists(FileInfo.joinPaths(allPaths[i], "tools", "android"))) { + path = allPaths[i]; + buildToolsVersions = SdkUtils.availableBuildToolsVersions(path) + buildToolsVersion = buildToolsVersions[buildToolsVersions.length - 1]; + platforms = SdkUtils.availableSdkPlatforms(path) + platform = platforms[platforms.length - 1]; + found = true; + return; + } + } + } +} diff --git a/share/qbs/imports/qbs/Probes/BinaryProbe.qbs b/share/qbs/imports/qbs/Probes/BinaryProbe.qbs new file mode 100644 index 00000000..b99ca603 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/BinaryProbe.qbs @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 + +PathProbe { + nameSuffixes: qbs.hostOS.contains("windows") ? [".com", ".exe", ".bat", ".cmd"] : undefined + platformPaths: undefined + platformEnvironmentPaths: [ "PATH" ] +} diff --git a/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs b/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs new file mode 100644 index 00000000..196e776f --- /dev/null +++ b/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 + +PathProbe { + platformPaths: [ + "~/Library/Frameworks", + "/usr/local/lib", + "/Library/Frameworks", + "/System/Library/Frameworks" + ] + + nameFilter: { + return function(name) { + return name + ".framework"; + } + } +} diff --git a/share/qbs/imports/qbs/Probes/GccProbe.qbs b/share/qbs/imports/qbs/Probes/GccProbe.qbs new file mode 100644 index 00000000..734f1132 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/GccProbe.qbs @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.ModUtils +import "../../../modules/cpp/gcc.js" as Gcc + +PathProbe { + // Inputs + property string compilerFilePath + property string preferredArchitecture + property string preferredMachineType + property stringList flags: [] + + property bool _haveArchFlag: qbs.targetOS.contains("darwin") + property string _nullDevice: qbs.nullDevice + property stringList _toolchain: qbs.toolchain + property string _pathListSeparator: qbs.pathListSeparator + property stringList _targetOS: qbs.targetOS + property string _sysroot: qbs.sysroot + + // Outputs + property string architecture + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property stringList libraryPaths + property stringList frameworkPaths + + configure: { + var args = flags; + if (_haveArchFlag) { + if (preferredArchitecture) + args.push("-arch", preferredArchitecture); + } else { + if (preferredArchitecture === "i386") + args.push("-m32"); + else if (preferredArchitecture === "x86_64") + args.push("-m64"); + + if (preferredMachineType) + args.push("-march=" + preferredMachineType); + } + + var macros = Gcc.dumpMacros(compilerFilePath, args, _nullDevice); + var defaultPaths = Gcc.dumpDefaultPaths(compilerFilePath, args, _nullDevice, + _pathListSeparator, _targetOS, _sysroot); + found = !!macros && !!defaultPaths; + + includePaths = defaultPaths.includePaths; + libraryPaths = defaultPaths.libraryPaths; + frameworkPaths = defaultPaths.frameworkPaths; + + // We have to dump the compiler's macros; -dumpmachine is not suitable because it is not + // always complete (for example, the subarch is not included for arm architectures). + architecture = ModUtils.guessArchitecture(macros) || preferredArchitecture; + + if (_toolchain.contains("clang")) { + versionMajor = parseInt(macros["__clang_major__"], 10); + versionMinor = parseInt(macros["__clang_minor__"], 10); + versionPatch = parseInt(macros["__clang_patchlevel__"], 10); + } else { + versionMajor = parseInt(macros["__GNUC__"], 10); + versionMinor = parseInt(macros["__GNUC_MINOR__"], 10); + versionPatch = parseInt(macros["__GNUC_PATCHLEVEL__"], 10); + } + } +} diff --git a/share/qbs/imports/qbs/Probes/IncludeProbe.qbs b/share/qbs/imports/qbs/Probes/IncludeProbe.qbs new file mode 100644 index 00000000..5b90671e --- /dev/null +++ b/share/qbs/imports/qbs/Probes/IncludeProbe.qbs @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 + +PathProbe { + pathSuffixes: [ "include" ] + platformEnvironmentPaths: { + if (qbs.toolchain.contains('msvc')) + return [ "INCLUDE" ]; + return undefined; + } +} diff --git a/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs b/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs new file mode 100644 index 00000000..d0e756d1 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.Utilities + +PathProbe { + // Outputs + property var version + + configure: { + var keySuffix = "Microsoft\\Windows\\CurrentVersion\\Uninstall\\Inno Setup 5_is1"; + var keys = [ + "HKEY_LOCAL_MACHINE\\SOFTWARE\\" + keySuffix, + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\" + keySuffix + ]; + for (var i in keys) { + var v = Utilities.getNativeSetting(keys[i], "DisplayVersion"); + if (v) { + path = Utilities.getNativeSetting(keys[i], "InstallLocation"); + version = v; + found = path && version; + return; + } + } + } +} diff --git a/share/qbs/imports/qbs/Probes/JdkProbe.qbs b/share/qbs/imports/qbs/Probes/JdkProbe.qbs new file mode 100644 index 00000000..1c39b8e1 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/JdkProbe.qbs @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.Environment +import qbs.File +import qbs.FileInfo +import qbs.Process +import "../../../modules/java/utils.js" as JavaUtils + +PathProbe { + // Inputs + property stringList hostOS: qbs.hostOS + property string architecture: qbs.architecture + + environmentPaths: Environment.getEnv("JAVA_HOME") + platformPaths: [ + "/usr/lib/jvm/default-java", // Debian/Ubuntu + "/etc/alternatives/java_sdk_openjdk", // Fedora + "/usr/lib/jvm/default" // Arch + ] + + // Outputs + property var version + + configure: { + path = JavaUtils.findJdkPath(hostOS, architecture, environmentPaths, platformPaths); + if (path) + version = JavaUtils.findJdkVersion(FileInfo.joinPaths(path, "bin", "javac")); + found = path && version; + } +} diff --git a/share/qbs/imports/qbs/Probes/MsvcProbe.qbs b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs new file mode 100644 index 00000000..05a30743 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.ModUtils +import qbs.Utilities + +PathProbe { + // Inputs + property string compilerFilePath + property string preferredArchitecture + + // Outputs + property string architecture + property int versionMajor + property int versionMinor + property int versionPatch + property var buildEnv + + configure: { + var info = Utilities.msvcCompilerInfo(compilerFilePath); + found = !!info && !!info.macros && !!info.buildEnvironment; + + var macros = info.macros; + architecture = ModUtils.guessArchitecture(macros); + + var ver = macros["_MSC_FULL_VER"]; + + versionMajor = parseInt(ver.substr(0, 2), 10); + versionMinor = parseInt(ver.substr(2, 2), 10); + versionPatch = parseInt(ver.substr(4), 10); + + buildEnv = info.buildEnvironment; + + if (preferredArchitecture && Utilities.canonicalArchitecture(preferredArchitecture) + !== Utilities.canonicalArchitecture(architecture)) { + throw "'" + preferredArchitecture + + "' differs from the architecture produced by this compiler (" + + architecture + ")"; + } + } +} diff --git a/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs b/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs new file mode 100644 index 00000000..64ab3201 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.Environment +import qbs.FileInfo + +BinaryProbe { + names: ["node", "nodejs"] + platformPaths: { + var paths = base; + if (qbs.hostOS.contains("windows")) { + var env32 = Environment.getEnv("PROGRAMFILES(X86)"); + var env64 = Environment.getEnv("PROGRAMFILES"); + if (env64 === env32 && env64.endsWith(" (x86)")) + env64 = env64.slice(0, -(" (x86)".length)); // QTBUG-3845 + paths.push(FileInfo.joinPaths(env64, "nodejs")); + paths.push(FileInfo.joinPaths(env32, "nodejs")); + } + return paths; + } +} diff --git a/share/qbs/imports/qbs/Probes/NpmProbe.qbs b/share/qbs/imports/qbs/Probes/NpmProbe.qbs new file mode 100644 index 00000000..611899b0 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/NpmProbe.qbs @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import "path-probe.js" as PathProbeConfigure +import "../../../modules/nodejs/nodejs.js" as NodeJs + +NodeJsProbe { + names: ["npm"] + + // Outputs + property path npmBin + property path npmRoot + property path npmPrefix + + configure: { + var result = PathProbeConfigure.configure(names, nameSuffixes, nameFilter, pathPrefixes, + pathSuffixes, platformPaths, environmentPaths, + platformEnvironmentPaths, qbs.pathListSeparator); + result.npmBin = result.found ? NodeJs.findLocation(result.filePath, "bin") : undefined; + result.npmRoot = result.found ? NodeJs.findLocation(result.filePath, "root") : undefined; + result.npmPrefix = result.found ? NodeJs.findLocation(result.filePath, "prefix") : undefined; + + found = result.found; + path = result.path; + filePath = result.filePath; + fileName = result.fileName; + npmBin = result.npmBin; + npmRoot = result.npmRoot; + npmPrefix = result.npmPrefix; + } +} diff --git a/share/qbs/imports/qbs/Probes/PathProbe.qbs b/share/qbs/imports/qbs/Probes/PathProbe.qbs new file mode 100644 index 00000000..38103e41 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/PathProbe.qbs @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import "path-probe.js" as PathProbeConfigure + +Probe { + // Inputs + property stringList names + property stringList nameSuffixes + property var nameFilter + property pathList pathPrefixes + property stringList pathSuffixes + property pathList platformPaths: [ '/usr', '/usr/local' ] + property pathList environmentPaths + property pathList platformEnvironmentPaths + + // Output + property string path + property string filePath + property string fileName + + configure: { + var result = PathProbeConfigure.configure(names, nameSuffixes, nameFilter, pathPrefixes, + pathSuffixes, platformPaths, environmentPaths, + platformEnvironmentPaths, qbs.pathListSeparator); + found = result.found; + path = result.path; + filePath = result.filePath; + fileName = result.fileName; + } +} diff --git a/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs b/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs new file mode 100644 index 00000000..5b738fec --- /dev/null +++ b/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.Process +import qbs.FileInfo + +Probe { + // Inputs + property string sysroot: qbs.sysroot + property string executable: 'pkg-config' + property string name + property string minVersion + property string exactVersion + property string maxVersion + property stringList libDirs // Full, non-sysrooted paths, mirroring the environment variable + + // Output + property stringList cflags + property stringList libs + property string modversion + + configure: { + if (!name) + throw '"name" must be specified'; + var p = new Process(); + try { + var args = [ name ]; + if (minVersion !== undefined) + args.push(name + ' >= ' + minVersion); + if (exactVersion !== undefined) + args.push(name + ' = ' + exactVersion); + if (maxVersion !== undefined) + args.push(name + ' <= ' + maxVersion); + var libDirsToSet = libDirs; + if (sysroot) { + p.setEnv("PKG_CONFIG_SYSROOT_DIR", sysroot); + if (!libDirsToSet) { + libDirsToSet = [ + sysroot + "/usr/lib/pkgconfig", + sysroot + "/usr/share/pkgconfig" + ]; + } + } + if (libDirsToSet) + p.setEnv("PKG_CONFIG_LIBDIR", libDirsToSet.join(qbs.pathListSeparator)); + if (p.exec(executable, args.concat([ '--cflags' ])) === 0) { + cflags = p.readStdOut().trim(); + cflags = cflags ? cflags.split(/\s/) : []; + if (p.exec(executable, args.concat([ '--libs' ])) === 0) { + libs = p.readStdOut().trim(); + libs = libs ? libs.split(/\s/) : []; + if (p.exec(executable, args.concat([ '--modversion' ])) === 0) { + modversion = p.readStdOut().trim(); + found = true; + console.info("PkgConfigProbe: found library " + name); + return; + } + } + } + found = false; + cflags = undefined; + libs = undefined; + } finally { + p.close(); + } + } +} diff --git a/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs b/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs new file mode 100644 index 00000000..c7be0000 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import "path-probe.js" as PathProbeConfigure +import "../../../modules/typescript/typescript.js" as TypeScript + +BinaryProbe { + id: tsc + names: ["tsc"] + pathPrefixes: [packageManagerBinPath] + + // Inputs + property path interpreterPath + property path packageManagerBinPath + property path packageManagerRootPath + + // Outputs + property var version + + configure: { + if (!condition) + return; + if (!interpreterPath) + throw '"interpreterPath" must be specified'; + if (!packageManagerBinPath) + throw '"packageManagerBinPath" must be specified'; + if (!packageManagerRootPath) + throw '"packageManagerRootPath" must be specified'; + + var result = PathProbeConfigure.configure(names, nameSuffixes, nameFilter, pathPrefixes, + pathSuffixes, platformPaths, environmentPaths, + platformEnvironmentPaths, qbs.pathListSeparator); + result.version = result.found + ? TypeScript.findTscVersion(result.filePath, interpreterPath) + : undefined; + if (FileInfo.fromNativeSeparators(packageManagerBinPath) !== result.path || + !File.exists(FileInfo.fromNativeSeparators(packageManagerRootPath, "typescript"))) { + result = { found: false }; + } + + found = result.found; + path = result.path; + filePath = result.filePath; + fileName = result.fileName; + version = result.version; + } +} diff --git a/share/qbs/imports/qbs/Probes/WiXProbe.qbs b/share/qbs/imports/qbs/Probes/WiXProbe.qbs new file mode 100644 index 00000000..c754162b --- /dev/null +++ b/share/qbs/imports/qbs/Probes/WiXProbe.qbs @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.Utilities + +PathProbe { + // Inputs + property string registryKey: { + var keys = [ + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Installer XML", + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows Installer XML" + ]; + for (var i in keys) { + var groups = Utilities.nativeSettingGroups(keys[i]).filter(function (v) { + return v.match(/^([0-9]+)\.([0-9]+)$/); + }); + + groups.sort(function (a, b) { + var re = /^([0-9]+)\.([0-9]+)$/; + a = a.match(re); + b = b.match(re); + a = {major: a[1], minor: a[2]}; + b = {major: b[1], minor: b[2]}; + if (a.major === b.major) + return b.minor - a.minor; + return b.major - a.major; + }); + + for (var j in groups) { + var fullKey = keys[i] + "\\" + groups[j]; + if (Utilities.getNativeSetting(fullKey, "ProductVersion")) + return fullKey; + } + } + } + + // Outputs + property var root + property var version + + configure: { + var key = registryKey; + path = Utilities.getNativeSetting(key, "InstallFolder"); + root = Utilities.getNativeSetting(key, "InstallRoot"); + version = Utilities.getNativeSetting(key, "ProductVersion"); + found = path && root && version; + } +} diff --git a/share/qbs/imports/qbs/Probes/path-probe.js b/share/qbs/imports/qbs/Probes/path-probe.js new file mode 100644 index 00000000..5e74605f --- /dev/null +++ b/share/qbs/imports/qbs/Probes/path-probe.js @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var Environment = loadExtension("qbs.Environment"); +var File = loadExtension("qbs.File"); +var FileInfo = loadExtension("qbs.FileInfo"); +var ModUtils = loadExtension("qbs.ModUtils"); + +function configure(names, nameSuffixes, nameFilter, pathPrefixes, pathSuffixes, platformPaths, + environmentPaths, platformEnvironmentPaths, pathListSeparator) { + if (!names) + throw '"names" must be specified'; + var _names = ModUtils.concatAll(names); + if (nameFilter) + _names = _names.map(function(n) { return nameFilter(n); }); + _names = ModUtils.concatAll.apply(undefined, _names.map(function(name) { + return (nameSuffixes || [""]).map(function(suffix) { return name + suffix; }); + })); + // FIXME: Suggest how to obtain paths from system + var _paths = ModUtils.concatAll(pathPrefixes, platformPaths); + // FIXME: Add getenv support + var envs = ModUtils.concatAll(platformEnvironmentPaths, environmentPaths); + for (var i = 0; i < envs.length; ++i) { + var value = Environment.getEnv(envs[i]) || ''; + if (value.length > 0) + _paths = _paths.concat(value.split(pathListSeparator)); + } + var _suffixes = ModUtils.concatAll('', pathSuffixes); + _paths = _paths.map(function(p) { return FileInfo.fromNativeSeparators(p); }); + _suffixes = _suffixes.map(function(p) { return FileInfo.fromNativeSeparators(p); }); + for (i = 0; i < _names.length; ++i) { + for (var j = 0; j < _paths.length; ++j) { + for (var k = 0; k < _suffixes.length; ++k) { + var _filePath = FileInfo.joinPaths(_paths[j], _suffixes[k], _names[i]); + if (File.exists(_filePath)) { + return { + found: true, + filePath: _filePath, + + // Manually specify the path components that constitute _filePath rather + // than using the FileInfo.path and FileInfo.fileName functions because we + // want to break _filePath into its constituent parts based on the input + // originally given by the user. For example, the FileInfo functions would + // produce a different result if any of the items in the names property + // contained more than a single path component. + fileName: _names[i], + path: FileInfo.joinPaths(_paths[j], _suffixes[k]), + } + } + } + } + } + + return { found: false }; +} diff --git a/share/qbs/imports/qbs/UnixUtils/unix-utils.js b/share/qbs/imports/qbs/UnixUtils/unix-utils.js new file mode 100644 index 00000000..62d1c13a --- /dev/null +++ b/share/qbs/imports/qbs/UnixUtils/unix-utils.js @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = loadExtension("qbs.FileInfo"); + +function soname(product, outputFileName) { + var soVersion = product.moduleProperty("cpp", "soVersion"); + if (product.moduleProperty("qbs", "targetOS").contains("darwin")) { + // If this is a bundle, ignore the parameter and use the relative path to the bundle binary + // For example: qbs.framework/Versions/1/qbs + if (product.moduleProperty("bundle", "isBundle")) + outputFileName = product.moduleProperty("bundle", "executablePath"); + } else if (soVersion) { + // For non-Darwin platforms, append the shared library major version number to the soname + // For example: libqbscore.so.1 + var version = product.moduleProperty("cpp", "internalVersion"); + if (version) { + outputFileName = outputFileName.substr(0, outputFileName.length - version.length) + + soVersion; + } else { + outputFileName += "." + soVersion; + } + } + + // Prepend the soname prefix + // For example, @rpath/libqbscore.dylib or /usr/lib/libqbscore.so.1 + var prefix = product.moduleProperty("cpp", "sonamePrefix"); + if (prefix) + outputFileName = FileInfo.joinPaths(prefix, outputFileName); + + return outputFileName; +} diff --git a/share/qbs/imports/qbs/WindowsUtils/windows-utils.js b/share/qbs/imports/qbs/WindowsUtils/windows-utils.js new file mode 100644 index 00000000..136fb3cf --- /dev/null +++ b/share/qbs/imports/qbs/WindowsUtils/windows-utils.js @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +function characterSetDefines(charset) { + var defines = []; + if (charset === "unicode") + defines.push("UNICODE", "_UNICODE"); + else if (charset === "mbcs") + defines.push("_MBCS"); + return defines; +} + +function canonicalizeVersion(version) { + switch (version) { + case "7": + return "6.1"; + case "8": + return "6.2"; + case "8.1": + return "6.3"; + default: + return version; + } +} + +function knownWindowsVersions() { + // Add new Windows versions to this list when they are released + return ['10.0', '6.3', '6.2', '6.1', '6.0', '5.2', '5.1', '5.0', '4.0']; +} + +function isValidWindowsVersion(systemVersion) { + var realVersions = knownWindowsVersions(); + for (i in realVersions) + if (systemVersion === realVersions[i]) + return true; + + return false; +} + +function getWindowsVersionInFormat(systemVersion, format) { + if (!systemVersion) + return undefined; + + var major = parseInt(systemVersion.split('.')[0], 10); + var minor = parseInt(systemVersion.split('.')[1], 10); + + switch (format) { + case "hex": + // https://msdn.microsoft.com/en-us/library/6sehtctf.aspx + return "0x" + ("0000" + ((major << 8) | minor).toString(16)).slice(-4); + case "subsystem": + // https://msdn.microsoft.com/en-us/library/fcc1zstk.aspx + return major + '.' + (minor < 10 ? '0' : '') + minor; + default: + throw ("Unrecognized Windows version format " + format + ". Must be in {hex, subsystem}."); + } +} diff --git a/share/qbs/imports/qbs/base/AndroidApk.qbs b/share/qbs/imports/qbs/base/AndroidApk.qbs new file mode 100644 index 00000000..44bd1257 --- /dev/null +++ b/share/qbs/imports/qbs/base/AndroidApk.qbs @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.FileInfo + +Product { + type: ["android.apk"] + Depends { name: "Android.sdk" } + + property string packageName: name + property bool automaticSources: true + property bool legacyLayout: false + + property path sourceSetDir: legacyLayout ? undefined : "src/main" + property path resourcesDir: FileInfo.joinPaths(sourceSetDir, "res") + property path assetsDir: FileInfo.joinPaths(sourceSetDir, "assets") + property path sourcesDir: FileInfo.joinPaths(sourceSetDir, legacyLayout ? "src" : "java") + property path manifestFile: FileInfo.joinPaths(sourceSetDir, "AndroidManifest.xml") + + Group { + name: "java sources" + condition: product.automaticSources + prefix: product.sourcesDir + '/' + files: "**/*.java" + } + + Group { + name: "android resources" + condition: product.automaticSources + fileTags: ["android.resources"] + prefix: product.resourcesDir + '/' + files: "**/*" + } + + Group { + name: "android assets" + condition: product.automaticSources + fileTags: ["android.assets"] + prefix: product.assetsDir + '/' + files: "**/*" + } + + Group { + name: "manifest" + condition: product.automaticSources + files: [manifestFile] + } +} diff --git a/share/qbs/imports/qbs/base/Application.qbs b/share/qbs/imports/qbs/base/Application.qbs new file mode 100644 index 00000000..fb9a429a --- /dev/null +++ b/share/qbs/imports/qbs/base/Application.qbs @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +Product { + type: { + if (isForAndroid && !consoleApplication) + return ["dynamiclibrary", "android.nativelibrary"]; + return ["application"]; + } + + property bool isForAndroid: qbs.targetOS.contains("android") + property stringList architectures: isForAndroid ? ["armv5te"] : undefined + + Depends { name: "bundle" } + + profiles: architectures + ? architectures.map(function(arch) { return project.profile + '-' + arch; }) + : [project.profile] +} diff --git a/share/qbs/imports/qbs/base/ApplicationExtension.qbs b/share/qbs/imports/qbs/base/ApplicationExtension.qbs new file mode 100644 index 00000000..1034f509 --- /dev/null +++ b/share/qbs/imports/qbs/base/ApplicationExtension.qbs @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +XPCService { + Depends { name: "xcode" } + + type: base.concat(["applicationextension"]) + + cpp.entryPoint: "_NSExtensionMain" + cpp.frameworks: { + var frameworks = base.concat(["Foundation"]); + if (qbs.targetOS.contains("macos") && parseInt(xcode.sdkVersion.split(".")[1], 10) < 11 || + qbs.targetOS.contains("ios") && parseInt(xcode.sdkVersion.split(".")[0], 10) < 9) { + frameworks = base.concat(["/System/Library/PrivateFrameworks/PlugInKit.framework"]); + } + return frameworks; + } + + cpp.requireAppExtensionSafeApi: true + + xpcServiceType: undefined + property var extensionAttributes + property string extensionPointIdentifier + property string extensionPrincipalClass + + bundle.infoPlist: { + var infoPlist = base; + infoPlist["NSExtension"] = { + "NSExtensionAttributes": extensionAttributes || {}, + "NSExtensionPointIdentifier": extensionPointIdentifier, + "NSExtensionPrincipalClass": extensionPrincipalClass + }; + return infoPlist; + } +} diff --git a/share/qbs/imports/qbs/base/AutotestRunner.qbs b/share/qbs/imports/qbs/base/AutotestRunner.qbs new file mode 100644 index 00000000..426b1ee8 --- /dev/null +++ b/share/qbs/imports/qbs/base/AutotestRunner.qbs @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Utilities + +Product { + name: "autotest-runner" + type: ["autotest-result"] + builtByDefault: false + property stringList arguments: [] + property stringList environment: ModUtils.flattenEnvironmentDictionary(qbs.commonRunEnvironment) + property bool limitToSubProject: true + property stringList wrapper: [] + Depends { + productTypes: "autotest" + limitToSubProject: product.limitToSubProject + } + Rule { + inputsFromDependencies: "application" + Artifact { + filePath: Utilities.getHash(input.filePath) + ".result.dummy" // Will never exist. + fileTags: "autotest-result" + alwaysUpdated: false + } + prepare: { + var commandFilePath; + var installed = input.moduleProperty("qbs", "install"); + if (installed) + commandFilePath = ModUtils.artifactInstalledFilePath(input); + if (!commandFilePath || !File.exists(commandFilePath)) + commandFilePath = input.filePath; + var fullCommandLine = product.wrapper + .concat([commandFilePath]) + .concat(product.arguments); + var cmd = new Command(fullCommandLine[0], fullCommandLine.slice(1)); + cmd.description = "Running test " + input.fileName; + cmd.environment = product.environment; + return cmd; + } + } +} diff --git a/share/qbs/imports/qbs/base/CppApplication.qbs b/share/qbs/imports/qbs/base/CppApplication.qbs new file mode 100644 index 00000000..9b7de2bf --- /dev/null +++ b/share/qbs/imports/qbs/base/CppApplication.qbs @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 + +Application { + Depends { name: "cpp" } +} + diff --git a/share/qbs/imports/qbs/base/DynamicLibrary.qbs b/share/qbs/imports/qbs/base/DynamicLibrary.qbs new file mode 100644 index 00000000..32d8fec4 --- /dev/null +++ b/share/qbs/imports/qbs/base/DynamicLibrary.qbs @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +Library { + type: ["dynamiclibrary"].concat(isForAndroid ? ["android.nativelibrary"] : []) +} diff --git a/share/qbs/imports/qbs/base/InnoSetup.qbs b/share/qbs/imports/qbs/base/InnoSetup.qbs new file mode 100644 index 00000000..5ea076eb --- /dev/null +++ b/share/qbs/imports/qbs/base/InnoSetup.qbs @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +Product { + Depends { name: "innosetup"; condition: qbs.targetOS.contains("windows") } + type: ["innosetup.exe"] +} diff --git a/share/qbs/imports/qbs/base/InstallPackage.qbs b/share/qbs/imports/qbs/base/InstallPackage.qbs new file mode 100644 index 00000000..9e621aed --- /dev/null +++ b/share/qbs/imports/qbs/base/InstallPackage.qbs @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.FileInfo +import qbs.ModUtils +import qbs.TextFile + +Product { + type: ["archiver.archive"] + builtByDefault: false + Depends { name: "archiver" } + archiver.type: "tar" + archiver.workingDirectory: qbs.installRoot + + Rule { + multiplex: true + inputsFromDependencies: ["installable"] + Artifact { + filePath: product.name + ".tarlist" + fileTags: ["archiver.input-list"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode =function() { + var ofile = new TextFile(output.filePath, TextFile.WriteOnly); + try { + for (var i = 0; i < inputs["installable"].length; ++i) { + var inp = inputs["installable"][i]; + var installRoot = inp.moduleProperty("qbs", "installRoot"); + var installedFilePath = ModUtils.artifactInstalledFilePath(inp); + ofile.writeLine(FileInfo.relativePath(installRoot, installedFilePath)); + } + } finally { + ofile.close(); + } + }; + return [cmd]; + } + } +} diff --git a/share/qbs/imports/qbs/base/JavaClassCollection.qbs b/share/qbs/imports/qbs/base/JavaClassCollection.qbs new file mode 100644 index 00000000..23b975a0 --- /dev/null +++ b/share/qbs/imports/qbs/base/JavaClassCollection.qbs @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +Product { + Depends { name: "java" } + type: ["java.class"] +} diff --git a/share/qbs/imports/qbs/base/JavaJarFile.qbs b/share/qbs/imports/qbs/base/JavaJarFile.qbs new file mode 100644 index 00000000..39ab9ec4 --- /dev/null +++ b/share/qbs/imports/qbs/base/JavaJarFile.qbs @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +Product { + Depends { name: "java" } + type: ["java.jar"] + property string entryPoint +} diff --git a/share/qbs/imports/qbs/base/Library.qbs b/share/qbs/imports/qbs/base/Library.qbs new file mode 100644 index 00000000..ea5bd295 --- /dev/null +++ b/share/qbs/imports/qbs/base/Library.qbs @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +Product { + type: { + if (qbs.targetOS.contains("ios") && parseInt(cpp.minimumIosVersion, 10) < 8) + return ["staticlibrary"]; + return ["dynamiclibrary"].concat(isForAndroid ? ["android.nativelibrary"] : []); + } + + property bool isForAndroid: qbs.targetOS.contains("android") + property stringList architectures: isForAndroid ? ["armv5te"] : undefined + + Depends { name: "bundle" } + + profiles: architectures + ? architectures.map(function(arch) { return project.profile + '-' + arch; }) + : [project.profile] +} diff --git a/share/qbs/imports/qbs/base/LoadableModule.qbs b/share/qbs/imports/qbs/base/LoadableModule.qbs new file mode 100644 index 00000000..05234de6 --- /dev/null +++ b/share/qbs/imports/qbs/base/LoadableModule.qbs @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +Product { + Depends { name: "bundle" } + type: qbs.targetOS.contains("darwin") ? ["loadablemodule"] : ["dynamiclibrary"] +} diff --git a/share/qbs/imports/qbs/base/NSISSetup.qbs b/share/qbs/imports/qbs/base/NSISSetup.qbs new file mode 100644 index 00000000..1362f271 --- /dev/null +++ b/share/qbs/imports/qbs/base/NSISSetup.qbs @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +Product { + Depends { name: "nsis"; condition: qbs.targetOS.contains("windows") } + type: ["nsissetup"] +} diff --git a/share/qbs/imports/qbs/base/NetModule.qbs b/share/qbs/imports/qbs/base/NetModule.qbs new file mode 100644 index 00000000..0f52ffe4 --- /dev/null +++ b/share/qbs/imports/qbs/base/NetModule.qbs @@ -0,0 +1,4 @@ +Product { + Depends { name: "cli" } + type: ["cli.netmodule"] +} diff --git a/share/qbs/imports/qbs/base/NodeJSApplication.qbs b/share/qbs/imports/qbs/base/NodeJSApplication.qbs new file mode 100644 index 00000000..1908310e --- /dev/null +++ b/share/qbs/imports/qbs/base/NodeJSApplication.qbs @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +Product { + Depends { name: "nodejs" } +} diff --git a/share/qbs/imports/qbs/base/QtApplication.qbs b/share/qbs/imports/qbs/base/QtApplication.qbs new file mode 100644 index 00000000..b9e8f260 --- /dev/null +++ b/share/qbs/imports/qbs/base/QtApplication.qbs @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +CppApplication { + Depends { name: "Qt.core" } +} diff --git a/share/qbs/imports/qbs/base/QtGuiApplication.qbs b/share/qbs/imports/qbs/base/QtGuiApplication.qbs new file mode 100644 index 00000000..1d92142f --- /dev/null +++ b/share/qbs/imports/qbs/base/QtGuiApplication.qbs @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +CppApplication { + Depends { name: "Qt.gui" } + Depends { + name: "Qt" + submodules: Qt.gui.defaultQpaPlugin + condition: linkDefaultQpaPlugin && Qt.gui.defaultQpaPlugin + } + property bool linkDefaultQpaPlugin: Qt.gui.isStaticLibrary +} diff --git a/share/qbs/imports/qbs/base/StaticLibrary.qbs b/share/qbs/imports/qbs/base/StaticLibrary.qbs new file mode 100644 index 00000000..1609bdce --- /dev/null +++ b/share/qbs/imports/qbs/base/StaticLibrary.qbs @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +Library { + type: ["staticlibrary"] +} diff --git a/share/qbs/imports/qbs/base/WindowsInstallerPackage.qbs b/share/qbs/imports/qbs/base/WindowsInstallerPackage.qbs new file mode 100644 index 00000000..791fc947 --- /dev/null +++ b/share/qbs/imports/qbs/base/WindowsInstallerPackage.qbs @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +Product { + Depends { name: "wix"; condition: qbs.targetOS.contains("windows") } + type: ["msi"] +} diff --git a/share/qbs/imports/qbs/base/WindowsSetupPackage.qbs b/share/qbs/imports/qbs/base/WindowsSetupPackage.qbs new file mode 100644 index 00000000..5175e9d8 --- /dev/null +++ b/share/qbs/imports/qbs/base/WindowsSetupPackage.qbs @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +Product { + Depends { name: "wix"; condition: qbs.targetOS.contains("windows") } + type: ["wixsetup"] +} diff --git a/share/qbs/imports/qbs/base/XPCService.qbs b/share/qbs/imports/qbs/base/XPCService.qbs new file mode 100644 index 00000000..0e8e1eb7 --- /dev/null +++ b/share/qbs/imports/qbs/base/XPCService.qbs @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +Application { + type: base.concat(["xpcservice"]) + + condition: qbs.targetOS.contains("darwin") + + property string xpcServiceType: "Application" + + bundle.infoPlist: { + var infoPlist = base; + if (xpcServiceType) { + infoPlist["XPCService"] = { + "ServiceType": xpcServiceType + }; + } + return infoPlist; + } +} diff --git a/share/qbs/modules/Android/ndk/ndk.qbs b/share/qbs/modules/Android/ndk/ndk.qbs new file mode 100644 index 00000000..4bc4736a --- /dev/null +++ b/share/qbs/modules/Android/ndk/ndk.qbs @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes + +import "utils.js" as NdkUtils + +Module { + Probes.AndroidNdkProbe { + id: ndkProbe + environmentPaths: [ndkDir].concat(base) + } + + readonly property string abi: NdkUtils.androidAbi(qbs.architecture) + PropertyOptions { + name: "abi" + description: "Corresponds to the 'APP_ABI' variable in an Android.mk file." + allowedValues: ["arm64-v8a", "armeabi", "armeabi-v7a", "mips", "mips64", "x86", "x86_64"] + } + + property string appStl: "system" + PropertyOptions { + name: "appStl" + description: "Corresponds to the 'APP_STL' variable in an Android.mk file." + allowedValues: [ + "system", "gabi++_static", "gabi++_shared", "stlport_static", "stlport_shared", + "gnustl_static", "gnustl_shared", "c++_static", "c++_shared" + ] + } + + property string toolchainVersion: latestToolchainVersion + PropertyOptions { + name: "toolchainVersion" + description: "Corresponds to the 'NDK_TOOLCHAIN_VERSION' variable in an Android.mk file." + } + + property string hostArch: ndkProbe.hostArch + property string ndkDir: ndkProbe.path + property string platform: "android-9" + + // Internal properties. + property stringList availableToolchains: ndkProbe.toolchains + + property stringList availableToolchainVersions: { + var tcs = availableToolchains; + var versions = []; + for (var i = 0; i < tcs.length; ++i) { + if ((qbs.toolchain.contains("clang") && tcs[i].startsWith("llvm-")) + || toolchainDirPrefixAbis.contains(tcs[i].split("-")[0])) { + var re = /\-((?:[0-9]+)\.(?:[0-9]+))$/; + var m = tcs[i].match(re); + if (m) + versions.push(m[1]); + } + } + + // Sort by version number + versions.sort(function (a, b) { + var re = /^([0-9]+)\.([0-9]+)$/; + a = a.match(re); + a = {major: a[1], minor: a[2]}; + b = b.match(re); + b = {major: b[1], minor: b[2]}; + if (a.major === b.major) + return a.minor - b.minor; + return a.major - b.major; + }); + + return versions; + } + + property string latestToolchainVersion: availableToolchainVersions + [availableToolchainVersions.length - 1] + + property int platformVersion: { + if (platform) { + var match = platform.match(/^android-([0-9]+)$/); + if (match !== null) { + return parseInt(match[1], 10); + } + } + } + + property stringList abis: { + var list = ["armeabi", "armeabi-v7a"]; + if (platformVersion >= 9) + list.push("mips", "x86"); + if (platformVersion >= 21) + list.push("arm64-v8a", "mips64", "x86_64"); + return list; + } + + property stringList toolchainDirPrefixAbis: { + var list = ["arm"]; + if (platformVersion >= 9) + list.push("mipsel", "x86"); + if (platformVersion >= 21) + list.push("aarch64", "mips64el", "x86_64"); + return list; + } + + property string toolchainVersionNumber: { + var prefix = "clang"; + if (toolchainVersion && toolchainVersion.startsWith(prefix)) + return toolchainVersion.substr(prefix.length); + return toolchainVersion; + } + + property string gdbserverFileName: "gdbserver" + + property string armMode: abi && abi.startsWith("armeabi") + ? (qbs.buildVariant === "debug" ? "arm" : "thumb") + : undefined; + PropertyOptions { + name: "armMode" + description: "Determines the instruction set for armeabi configurations." + allowedValues: ["arm", "thumb"] + } + + validate: { + var validator = new ModUtils.PropertyValidator("Android.ndk"); + validator.setRequiredProperty("abi", abi); + validator.setRequiredProperty("appStl", appStl); + validator.setRequiredProperty("toolchainVersion", toolchainVersion); + validator.setRequiredProperty("hostArch", hostArch); + validator.setRequiredProperty("ndkDir", ndkDir); + validator.setRequiredProperty("platform", platform); + validator.setRequiredProperty("toolchainVersionNumber", toolchainVersionNumber); + return validator.validate(); + } +} diff --git a/share/qbs/modules/Android/ndk/utils.js b/share/qbs/modules/Android/ndk/utils.js new file mode 100644 index 00000000..062730b9 --- /dev/null +++ b/share/qbs/modules/Android/ndk/utils.js @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +function abiNameToDirName(abiName) { + switch (abiName) { + case "armeabi": + case "armeabi-v7a": + return "arm"; + case "arm64-v8a": + return "arm64"; + default: + return abiName; + } +} + +function androidAbi(arch) { + switch (arch) { + case "arm64": + return "arm64-v8a"; + case "armv5": + case "armv5te": + return "armeabi"; + case "armv7": + case "armv7a": + return "armeabi-v7a"; + case "mips": + case "mipsel": + return "mips"; + case "mips64": + case "mips64el": + return "mips64"; + default: + return arch; + } +} + +function commonCompilerFlags(buildVariant, abi, armMode) { + var flags = ["-ffunction-sections", "-funwind-tables", + "-Wa,--noexecstack", "-Werror=format-security"]; + + if (buildVariant === "debug") + flags.push("-fno-omit-frame-pointer", "-fno-strict-aliasing"); + if (buildVariant === "release") + flags.push("-fomit-frame-pointer"); + + if (abi === "arm64-v8a") { + flags.push("-fpic", "-fstack-protector", "-funswitch-loops", "-finline-limit=300"); + if (buildVariant === "release") + flags.push("-fstrict-aliasing"); + } + + if (abi === "armeabi" || abi === "armeabi-v7a") { + flags.push("-fpic", "-fstack-protector", "-finline-limit=64"); + + if (abi === "armeabi") + flags.push("-mtune=xscale", "-msoft-float"); + + if (abi === "armeabi-v7a") { + flags.push("-mfpu=vfpv3-d16"); + flags.push("-mfloat-abi=softfp"); + } + + if (buildVariant === "release") + flags.push("-fno-strict-aliasing"); + } + + if (abi === "mips" || abi === "mips64") { + flags.push("-fpic", "-finline-functions", "-fmessage-length=0", + "-fno-inline-functions-called-once", "-fgcse-after-reload", + "-frerun-cse-after-loop", "-frename-registers"); + if (buildVariant === "release") + flags.push("-funswitch-loops", "-finline-limit=300", "-fno-strict-aliasing"); + } + + if (abi === "x86" || abi === "x86_64") { + flags.push("-fstack-protector", "-funswitch-loops", "-finline-limit=300"); + if (buildVariant === "release") + flags.push("-fstrict-aliasing"); + } + + if (armMode) + flags.push("-m" + armMode); + + return flags; +} + +function commonLinkerFlags(abi) { + var flags = ["-z", "noexecstack", "-z", "relro", "-z", "now"]; + + if (abi === "armeabi-v7a") { + flags.push("--fix-cortex-a8"); + } + + return flags; +} diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs new file mode 100644 index 00000000..f665835f --- /dev/null +++ b/share/qbs/modules/Android/sdk/sdk.qbs @@ -0,0 +1,305 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes +import qbs.TextFile +import qbs.Utilities +import "utils.js" as SdkUtils + +Module { + Probes.AndroidSdkProbe { + id: sdkProbe + environmentPaths: [sdkDir].concat(base) + } + + Probes.AndroidNdkProbe { + id: ndkProbe + sdkPath: sdkProbe.path + environmentPaths: [ndkDir].concat(base) + } + + property path sdkDir: sdkProbe.path + property path ndkDir: ndkProbe.path + property string buildToolsVersion: sdkProbe.buildToolsVersion + property var buildToolsVersionParts: buildToolsVersion ? buildToolsVersion.split('.').map(function(item) { return parseInt(item, 10); }) : [] + property int buildToolsVersionMajor: buildToolsVersionParts[0] + property int buildToolsVersionMinor: buildToolsVersionParts[1] + property int buildToolsVersionPatch: buildToolsVersionParts[2] + property string platform: sdkProbe.platform + + // Internal properties. + property int platformVersion: { + if (platform) { + var match = platform.match(/^android-([0-9]+)$/); + if (match !== null) { + return parseInt(match[1], 10); + } + } + } + + property string platformJavaVersion: { + if (platformVersion >= 21) + return "1.7"; + return "1.5"; + } + + property path buildToolsDir: FileInfo.joinPaths(sdkDir, "build-tools", buildToolsVersion) + property path aaptFilePath: FileInfo.joinPaths(buildToolsDir, "aapt") + property path aidlFilePath: FileInfo.joinPaths(buildToolsDir, "aidl") + property path dxFilePath: FileInfo.joinPaths(buildToolsDir, "dx") + property path zipalignFilePath: FileInfo.joinPaths(buildToolsDir, "zipalign") + property path androidJarFilePath: FileInfo.joinPaths(sdkDir, "platforms", platform, + "android.jar") + property path generatedJavaFilesBaseDir: FileInfo.joinPaths(product.buildDirectory, "gen") + property path generatedJavaFilesDir: FileInfo.joinPaths(generatedJavaFilesBaseDir, + product.packageName.split('.').join('/')) + + Depends { name: "java" } + java.languageVersion: platformJavaVersion + java.runtimeVersion: platformJavaVersion + java.bootClassPaths: androidJarFilePath + + FileTagger { + patterns: ["AndroidManifest.xml"] + fileTags: ["android.manifest"] + } + + FileTagger { + patterns: ["*.aidl"] + fileTags: ["android.aidl"] + } + + + Rule { + inputs: ["android.aidl"] + Artifact { + filePath: FileInfo.joinPaths(Utilities.getHash(input.filePath), + input.completeBaseName + ".java") + fileTags: ["java.java"] + } + + prepare: { + var aidl = ModUtils.moduleProperty(product, "aidlFilePath"); + cmd = new Command(aidl, [input.filePath, output.filePath]); + cmd.description = "Processing " + input.fileName; + return [cmd]; + } + } + + Rule { + multiplex: true + inputs: ["android.resources", "android.assets", "android.manifest"] + + outputFileTags: ["android.ap_", "java.java"] + outputArtifacts: { + var artifacts = [{ + filePath: product.name + ".ap_", + fileTags: ["android.ap_"] + }]; + + var resources = inputs["android.resources"]; + if (resources && resources.length) { + artifacts.push({ + filePath: FileInfo.joinPaths( + ModUtils.moduleProperty(product, "generatedJavaFilesDir"), + "R.java"), + fileTags: ["java.java"] + }); + } + + return artifacts; + } + + prepare: { + var manifestFilePath = inputs["android.manifest"][0].filePath; + var args = ["package", "-f", "-m", "--no-crunch", + "-M", manifestFilePath, + "-I", ModUtils.moduleProperty(product, "androidJarFilePath"), + "-F", outputs["android.ap_"][0].filePath, "--generate-dependencies"]; + var resources = inputs["android.resources"]; + if (resources && resources.length) + args.push("-S", product.resourcesDir, + "-J", ModUtils.moduleProperty(product, "generatedJavaFilesBaseDir")); + if (product.moduleProperty("qbs", "buildVariant") === "debug") + args.push("--debug-mode"); + if (File.exists(product.assetsDir)) + args.push("-A", product.assetsDir); + var cmd = new Command(ModUtils.moduleProperty(product, "aaptFilePath"), args); + cmd.description = "Processing resources"; + return [cmd]; + } + } + + Rule { + inputs: ["android.manifest"] // FIXME: Workaround for the fact that rules need inputs + Artifact { + filePath: FileInfo.joinPaths(ModUtils.moduleProperty(product, "generatedJavaFilesDir"), + "BuildConfig.java") + fileTags: ["java.java"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Generating BuildConfig.java"; + cmd.sourceCode = function() { + var debugValue = product.moduleProperty("qbs", "buildVariant") === "debug" + ? "true" : "false"; + var ofile = new TextFile(output.filePath, TextFile.WriteOnly); + ofile.writeLine("package " + product.packageName + ";") + ofile.writeLine("public final class BuildConfig {"); + ofile.writeLine(" public final static boolean DEBUG = " + debugValue + ";"); + ofile.writeLine("}"); + ofile.close(); + }; + return [cmd]; + } + } + + Rule { + multiplex: true + inputs: ["java.class"] + Artifact { + filePath: "classes.dex" + fileTags: ["android.dex"] + } + prepare: { + var dxFilePath = ModUtils.moduleProperty(product, "dxFilePath"); + var args = ["--dex", "--output", output.filePath, + product.moduleProperty("java", "classFilesDir")]; + var cmd = new Command(dxFilePath, args); + cmd.description = "Creating " + output.fileName; + return [cmd]; + } + } + + Rule { + multiplex: true + inputsFromDependencies: [ + "android.gdbserver-info", "android.stl-info", "android.nativelibrary" + ] + outputFileTags: ["android.gdbserver", "android.stl", "android.nativelibrary-deployed"] + outputArtifacts: { + var libArtifacts = []; + if (inputs["android.nativelibrary"]) { + for (var i = 0; i < inputs["android.nativelibrary"].length; ++i) { + var inp = inputs["android.nativelibrary"][i]; + var destDir = FileInfo.joinPaths("lib", + inp.moduleProperty("Android.ndk", "abi")); + libArtifacts.push({ + filePath: FileInfo.joinPaths(destDir, inp.fileName), + fileTags: ["android.nativelibrary-deployed"] + }); + } + } + var gdbServerArtifacts = SdkUtils.outputArtifactsFromInfoFiles(inputs, + product, "android.gdbserver-info", "android.gdbserver"); + var stlArtifacts = SdkUtils.outputArtifactsFromInfoFiles(inputs, product, + "android.stl-info", "android.deployed-stl"); + return libArtifacts.concat(gdbServerArtifacts).concat(stlArtifacts); + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Pre-packaging native binaries"; + cmd.sourceCode = function() { + if (inputs["android.nativelibrary"]) { + for (var i = 0; i < inputs["android.nativelibrary"].length; ++i) { + for (var j = 0; j < outputs["android.nativelibrary-deployed"].length; ++j) { + var inp = inputs["android.nativelibrary"][i]; + var outp = outputs["android.nativelibrary-deployed"][j]; + var inpAbi = inp.moduleProperty("Android.ndk", "abi"); + var outpAbi = FileInfo.fileName(outp.baseDir); + if (inp.fileName === outp.fileName && inpAbi === outpAbi) { + File.copy(inp.filePath, outp.filePath); + break; + } + } + } + } + var pathsSpecs = SdkUtils.sourceAndTargetFilePathsFromInfoFiles(inputs, product, + "android.gdbserver-info"); + for (i = 0; i < pathsSpecs.sourcePaths.length; ++i) + File.copy(pathsSpecs.sourcePaths[i], pathsSpecs.targetPaths[i]); + pathsSpecs = SdkUtils.sourceAndTargetFilePathsFromInfoFiles(inputs, product, + "android.stl-info"); + for (i = 0; i < pathsSpecs.sourcePaths.length; ++i) + File.copy(pathsSpecs.sourcePaths[i], pathsSpecs.targetPaths[i]); + }; + return [cmd]; + } + } + + // TODO: ApkBuilderMain is deprecated. Do we have to provide our own tool directly + // accessing com.android.sdklib.build.ApkBuilder or is there a simpler way? + Rule { + multiplex: true + inputs: [ + "android.dex", "android.ap_", "android.gdbserver", "android.stl", + "android.nativelibrary-deployed" + ] + Artifact { + filePath: product.targetName + ".apk.unaligned" + fileTags: ["android.apk.unaligned"] + } + prepare: { + var args = ["-classpath", FileInfo.joinPaths(ModUtils.moduleProperty(product, "sdkDir"), + "tools/lib/sdklib.jar"), + "com.android.sdklib.build.ApkBuilderMain", output.filePath, + "-z", inputs["android.ap_"][0].filePath, + "-f", inputs["android.dex"][0].filePath]; + if (product.moduleProperty("qbs", "buildVariant") === "debug") + args.push("-d"); + if (inputs["android.nativelibrary-deployed"]) + args.push("-nf", FileInfo.joinPaths(product.buildDirectory, "lib")); + var cmd = new Command(product.moduleProperty("java", "interpreterFilePath"), args); + cmd.description = "Generating " + output.fileName; + return [cmd]; + } + } + + Rule { + multiplex: true + inputs: ["android.apk.unaligned"] + Artifact { + filePath: product.targetName + ".apk" + fileTags: ["android.apk"] + } + prepare: { + var zipalign = ModUtils.moduleProperty(product, "zipalignFilePath"); + var args = ["-f", "4", inputs["android.apk.unaligned"][0].filePath, output.filePath]; + var cmd = new Command(zipalign, args); + cmd.description = "Creating " + output.fileName; + return [cmd]; + } + } +} diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js new file mode 100644 index 00000000..b1648070 --- /dev/null +++ b/share/qbs/modules/Android/sdk/utils.js @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var File = loadExtension("qbs.File"); +var FileInfo = loadExtension("qbs.FileInfo"); +var TextFile = loadExtension("qbs.TextFile"); + +function sourceAndTargetFilePathsFromInfoFiles(inputs, product, inputTag) +{ + var sourceFilePaths = []; + var targetFilePaths = []; + var inputsLength = inputs[inputTag] ? inputs[inputTag].length : 0; + for (var i = 0; i < inputsLength; ++i) { + var infoFile = new TextFile(inputs[inputTag][i].filePath, TextFile.ReadOnly); + var sourceFilePath = infoFile.readLine(); + var targetFilePath = FileInfo.joinPaths(product.buildDirectory, infoFile.readLine()); + if (!targetFilePaths.contains(targetFilePath)) { + sourceFilePaths.push(sourceFilePath); + targetFilePaths.push(targetFilePath); + } + infoFile.close(); + } + return { sourcePaths: sourceFilePaths, targetPaths: targetFilePaths }; +} + +function outputArtifactsFromInfoFiles(inputs, product, inputTag, outputTag) +{ + var pathSpecs = sourceAndTargetFilePathsFromInfoFiles(inputs, product, inputTag) + var artifacts = []; + for (i = 0; i < pathSpecs.targetPaths.length; ++i) + artifacts.push({filePath: pathSpecs.targetPaths[i], fileTags: [outputTag]}); + return artifacts; +} + +function availableSdkPlatforms(sdkDir) { + var re = /^android-([0-9]+)$/; + var platforms = File.directoryEntries(FileInfo.joinPaths(sdkDir, "platforms"), + File.Dirs | File.NoDotAndDotDot); + var versions = []; + for (var i = 0; i < platforms.length; ++i) { + var match = platforms[i].match(re); + if (match !== null) { + versions.push(platforms[i]); + } + } + + versions.sort(function (a, b) { + return parseInt(a.match(re)[1], 10) - parseInt(b.match(re)[1], 10); + }); + return versions; +} + +function availableBuildToolsVersions(sdkDir) { + var re = /^([0-9]+)\.([0-9]+)\.([0-9]+)$/; + var buildTools = File.directoryEntries(FileInfo.joinPaths(sdkDir, "build-tools"), + File.Dirs | File.NoDotAndDotDot); + var versions = []; + for (var i = 0; i < buildTools.length; ++i) { + var match = buildTools[i].match(re); + if (match !== null) { + versions.push(buildTools[i]); + } + } + + // Sort by version number + versions.sort(function (a, b) { + a = a.match(re); + if (a) + a = {major: a[1], minor: a[2], patch: a[3]}; + b = b.match(re); + if (b) + b = {major: b[1], minor: b[2], patch: a[3]}; + + if (a.major === b.major) { + if (a.minor === b.minor) + return a.patch - b.patch; + return a.minor - b.minor; + } + return a.major - b.major; + }); + + return versions; +} diff --git a/share/qbs/modules/archiver/archiver.qbs b/share/qbs/modules/archiver/archiver.qbs new file mode 100644 index 00000000..b63355c3 --- /dev/null +++ b/share/qbs/modules/archiver/archiver.qbs @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.Environment +import qbs.File +import qbs.FileInfo +import qbs.Probes + +Module { + // jar is a suitable fallback for creating zip files as they are the same format + // This will most likely end up being used on Windows + Depends { name: "java"; required: false } + + Probes.BinaryProbe { + id: tarProbe + names: ["tar"] + } + + Probes.BinaryProbe { + id: zipProbe + names: ["zip"] + } + + Probes.BinaryProbe { + id: sevenZipProbe + names: ["7z"] + platformPaths: { + var paths = base; + if (qbs.hostOS.contains("windows")) { + var env32 = Environment.getEnv("PROGRAMFILES(X86)"); + var env64 = Environment.getEnv("PROGRAMFILES"); + if (env64 === env32 && env64.endsWith(" (x86)")) + env64 = env64.slice(0, -(" (x86)".length)); // QTBUG-3845 + paths.push(FileInfo.joinPaths(env64, "7-Zip")); + paths.push(FileInfo.joinPaths(env32, "7-Zip")); + } + return paths; + } + } + + property string type + property string archiveBaseName: product.targetName + property string workingDirectory + property stringList flags: [] + property path outputDirectory: product.destinationDirectory + property string archiveExtension: { + if (type === "7zip") + return "7z"; + if (type == "tar") { + var extension = "tar"; + if (compressionType !== "none") + extension += "." + compressionType; + return extension; + } + if (type === "zip") + return "zip"; + return undefined; + } + property string command: { + if (type === "7zip") + return sevenZipProbe.filePath; + if (type === "tar") { + if (tarProbe.found) + return tarProbe.filePath; + if (sevenZipProbe.found) + return sevenZipProbe.filePath; + } + if (type === "zip") { + // Prefer zip (probably Info-Zip) and fall back to 7z or jar when it's not available + // (as is the likely case on Windows) + if (zipProbe.found) + return zipProbe.filePath; + if (sevenZipProbe.found) + return sevenZipProbe.filePath; + if (java.present) + return java.jarFilePath; + } + return undefined; + } + + property string compressionLevel + property string compressionType: { + if (type === "tar") + return "gz"; + return undefined; + } + + PropertyOptions { + name: "type" + description: "The type of archive to create." + allowedValues: ["7zip", "tar", "zip"] + } + + PropertyOptions { + name: "compressionLevel" + description: "How much effort to put into compression.\n" + + "Higher numbers mean smaller archive files at the cost of taking more time.\n" + + "This property is only used for the '7zip' and 'zip' types.\n" + + "'7zip' only supports 0 and odd numbers." + allowedValues: [undefined, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"] + } + + PropertyOptions { + name: "compressionType" + description: "The compression format to use.\n" + + "For tar archives, the respective tool needs to be present.\n" + + "This property is only used for the 'tar' and 'zip' types." + allowedValues: ["none", "gz", "bz2", "Z", "xz", "deflate", "store"] + } + + Rule { + inputs: ["archiver.input-list"] + + Artifact { + filePath: FileInfo.joinPaths(product.moduleProperty("archiver", "outputDirectory"), + product.moduleProperty("archiver", "archiveBaseName") + '.' + + product.moduleProperty("archiver", "archiveExtension")); + fileTags: ["archiver.archive"] + } + + prepare: { + var binary = product.moduleProperty("archiver", "command"); + var binaryName = FileInfo.baseName(binary); + var args = []; + var commands = []; + var type = product.moduleProperty("archiver", "type"); + var compression = product.moduleProperty("archiver", "compressionType"); + var compressionLevel = product.moduleProperty("archiver", "compressionLevel"); + if (binaryName === "7z") { + var rmCommand = new JavaScriptCommand(); + rmCommand.silent = true; + rmCommand.sourceCode = function() { + if (File.exists(output.filePath)) + File.remove(output.filePath); + }; + commands.push(rmCommand); + args.push("a", "-y", "-mmt=on"); + switch (type) { + case "7zip": + args.push("-t7z"); + break; + case "zip": + args.push("-tzip"); + break; + case "tar": + if (compression === "gz") + args.push("-tgzip"); + else if (compression === "bz2") + args.push("-tbzip2"); + else + args.push("-ttar"); + break; + default: + throw "7zip: unrecognized archive type: '" + type + "'"; + } + + if (compressionLevel) + args.push("-mx" + compressionLevel); + args = args.concat(product.moduleProperty("archiver", "flags")); + args.push(output.filePath); + args.push("@" + input.filePath); + } else if (binaryName === "tar" && type === "tar") { + args.push("-c"); + if (compression === "gz") + args.push("-z"); + else if (compression === "bz2") + args.push("-j"); + else if (compression === "Z") + args.push("-Z"); + else if (compression === "xz") + args.push("-J"); + args.push("-f", output.filePath, "-T", input.filePath); + args = args.concat(product.moduleProperty("archiver", "flags")); + } else if (binaryName === "jar" && type === "zip") { + if (compression === "none" || compressionLevel === "0") + args.push("-0"); + + args.push("-cfM", output.filePath, "@" + input.filePath); + args = args.concat(product.moduleProperty("archiver", "flags")); + } else if (binaryName === "zip" && type === "zip") { + // The "zip" program included with most Linux and Unix distributions + // (including macOS) is Info-ZIP's Zip, so this should be fairly portable. + if (compression === "none") { + args.push("-0"); + } else { + compression = compression === "bz2" ? "bzip2" : compression; + if (["store", "deflate", "bzip2"].contains(compression)) + args.push("-Z", compression); + + if (compressionLevel) + args.push("-" + compressionLevel); + } + + args.push("-r", output.filePath, ".", "-i@" + input.filePath); + args = args.concat(product.moduleProperty("archiver", "flags")); + } else if (["tar", "zip", "jar"].contains(binaryName)) { + throw binaryName + ": unrecognized archive type: '" + type + "'"; + } else { + throw "unrecognized archive tool: '" + binaryName + "'"; + } + + var archiverCommand = new Command(binary, args); + archiverCommand.description = "Creating archive file " + output.fileName; + archiverCommand.highlight = "linker"; + archiverCommand.workingDirectory + = product.moduleProperty("archiver", "workingDirectory"); + commands.push(archiverCommand); + return commands; + } + } +} diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs new file mode 100644 index 00000000..7c227897 --- /dev/null +++ b/share/qbs/modules/bundle/BundleModule.qbs @@ -0,0 +1,746 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.BundleTools +import qbs.DarwinTools +import qbs.Environment +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.PropertyList +import qbs.TextFile +import qbs.Utilities +import "bundle.js" as Bundle + +Module { + Depends { name: "xcode"; required: false; } + + Probe { + id: bundleSettingsProbe + condition: qbs.targetOS.contains("darwin") + + property string xcodeDeveloperPath: xcode.developerPath + + // Note that we include several settings pointing to properties which reference the output + // of this probe (WRAPPER_NAME, WRAPPER_EXTENSION, etc.). This is to ensure that derived + // properties take into account the value of these settings if the user customized them. + property var additionalSettings: ({ + "DEVELOPMENT_LANGUAGE": "English", + "EXECUTABLE_VARIANT_SUFFIX": "", // e.g. _debug, _profile + "FRAMEWORK_VERSION": frameworkVersion, + "GENERATE_PKGINFO_FILE": generatePackageInfo !== undefined + ? (generatePackageInfo ? "YES" : "NO") + : undefined, + "PRODUCT_NAME": product.targetName, + "LOCAL_APPS_DIR": Environment.getEnv("HOME") + "/Applications", + "LOCAL_LIBRARY_DIR": Environment.getEnv("HOME") + "/Library", + "TARGET_BUILD_DIR": product.buildDirectory, + "WRAPPER_NAME": bundleName, + "WRAPPER_EXTENSION": extension + }) + + // Outputs + property var xcodeSettings: ({}) + + configure: { + var specsPath = path; + var specsSeparator = "-"; + if (xcodeDeveloperPath && _useXcodeBuildSpecs) { + specsPath = xcodeDeveloperPath + + "/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications"; + specsSeparator = " "; + } + + var reader = new Bundle.XcodeBuildSpecsReader(specsPath, + specsSeparator, + additionalSettings, + !qbs.targetOS.contains("macos")); + var settings = reader.expandedSettings(_productTypeIdentifier); + if (settings) { + xcodeSettings = settings; + found = true; + } else { + xcodeSettings = {}; + found = false; + } + } + } + + additionalProductTypes: ["bundle"] + + property bool isBundle: !product.consoleApplication && qbs.targetOS.contains("darwin") && + product.type.containsAny(["application", "dynamiclibrary", "loadablemodule"]) + + readonly property bool isShallow: bundleSettingsProbe.xcodeSettings["SHALLOW_BUNDLE"] === "YES" + + property string identifierPrefix: "org.example" + property string identifier: [identifierPrefix, Utilities.rfc1034Identifier(product.targetName)].join(".") + + property string extension: bundleSettingsProbe.xcodeSettings["WRAPPER_EXTENSION"] + + property string packageType: { + if (product.type.contains("inapppurchase")) + return undefined; + if (product.type.contains("xpcservice")) + return "XPC!"; + if (product.type.contains("application")) + return "APPL"; + if (product.type.containsAny(["dynamiclibrary", "staticlibrary"])) + return "FMWK"; + if (product.type.contains("kernelmodule")) + return "KEXT"; + return "BNDL"; + } + + property string signature: "????" // legacy creator code in Mac OS Classic (CFBundleSignature), can be ignored + + property string bundleName: bundleSettingsProbe.xcodeSettings["WRAPPER_NAME"] + + property string frameworkVersion: { + var n = parseInt(product.version, 10); + return isNaN(n) ? bundleSettingsProbe.xcodeSettings["FRAMEWORK_VERSION"] : String(n); + } + + property bool generatePackageInfo: { + // Make sure to return undefined as default to indicate "not set" + var genPkgInfo = bundleSettingsProbe.xcodeSettings["GENERATE_PKGINFO_FILE"]; + if (genPkgInfo) + return genPkgInfo === "YES"; + } + + property pathList publicHeaders + property pathList privateHeaders + property pathList resources + + property var infoPlist + property bool processInfoPlist: true + property bool embedInfoPlist: product.type.contains("application") && !isBundle + property string infoPlistFormat: qbs.targetOS.contains("macos") ? "same-as-input" : "binary1" + + property string localizedResourcesFolderSuffix: ".lproj" + + property string lsregisterName: "lsregister" + property string lsregisterPath: FileInfo.joinPaths( + "/System/Library/Frameworks/CoreServices.framework" + + "/Versions/A/Frameworks/LaunchServices.framework" + + "/Versions/A/Support", lsregisterName); + + // all paths are relative to the directory containing the bundle + readonly property string infoPlistPath: bundleSettingsProbe.xcodeSettings["INFOPLIST_PATH"] + readonly property string infoStringsPath: bundleSettingsProbe.xcodeSettings["INFOSTRINGS_PATH"] + readonly property string pbdevelopmentPlistPath: bundleSettingsProbe.xcodeSettings["PBDEVELOPMENTPLIST_PATH"] + readonly property string pkgInfoPath: bundleSettingsProbe.xcodeSettings["PKGINFO_PATH"] + readonly property string versionPlistPath: bundleSettingsProbe.xcodeSettings["VERSIONPLIST_PATH"] + + readonly property string executablePath: bundleSettingsProbe.xcodeSettings["EXECUTABLE_PATH"] + + readonly property string contentsFolderPath: bundleSettingsProbe.xcodeSettings["CONTENTS_FOLDER_PATH"] + readonly property string documentationFolderPath: bundleSettingsProbe.xcodeSettings["DOCUMENTATION_FOLDER_PATH"] + readonly property string executableFolderPath: bundleSettingsProbe.xcodeSettings["EXECUTABLE_FOLDER_PATH"] + readonly property string executablesFolderPath: bundleSettingsProbe.xcodeSettings["EXECUTABLES_FOLDER_PATH"] + readonly property string frameworksFolderPath: bundleSettingsProbe.xcodeSettings["FRAMEWORKS_FOLDER_PATH"] + readonly property string javaFolderPath: bundleSettingsProbe.xcodeSettings["JAVA_FOLDER_PATH"] + readonly property string localizedResourcesFolderPath: bundleSettingsProbe.xcodeSettings["LOCALIZED_RESOURCES_FOLDER_PATH"] + readonly property string pluginsFolderPath: bundleSettingsProbe.xcodeSettings["PLUGINS_FOLDER_PATH"] + readonly property string privateHeadersFolderPath: bundleSettingsProbe.xcodeSettings["PRIVATE_HEADERS_FOLDER_PATH"] + readonly property string publicHeadersFolderPath: bundleSettingsProbe.xcodeSettings["PUBLIC_HEADERS_FOLDER_PATH"] + readonly property string scriptsFolderPath: bundleSettingsProbe.xcodeSettings["SCRIPTS_FOLDER_PATH"] + readonly property string sharedFrameworksFolderPath: bundleSettingsProbe.xcodeSettings["SHARED_FRAMEWORKS_FOLDER_PATH"] + readonly property string sharedSupportFolderPath: bundleSettingsProbe.xcodeSettings["SHARED_SUPPORT_FOLDER_PATH"] + readonly property string unlocalizedResourcesFolderPath: bundleSettingsProbe.xcodeSettings["UNLOCALIZED_RESOURCES_FOLDER_PATH"] + readonly property string versionsFolderPath: bundleSettingsProbe.xcodeSettings["VERSIONS_FOLDER_PATH"] + + // private properties + property string _productTypeIdentifier: Bundle.productTypeIdentifier(product.type) + + property bool _useXcodeBuildSpecs: true // false to use ONLY the qbs build specs + + readonly property var extraEnv: ({ + "PRODUCT_BUNDLE_IDENTIFIER": identifier + }) + + readonly property var qmakeEnv: { + return { + "BUNDLEIDENTIFIER": identifier, + "EXECUTABLE": product.targetName, + "FULL_VERSION": product.version || "1.0", // CFBundleVersion + "ICON": product.targetName, // ### QBS-73 + "LIBRARY": product.targetName, + "SHORT_VERSION": product.version || "1.0", // CFBundleShortVersionString + "TYPEINFO": signature // CFBundleSignature + }; + } + + readonly property var defaultInfoPlist: { + return { + CFBundleDevelopmentRegion: "en", // default localization + CFBundleDisplayName: product.targetName, // localizable + CFBundleExecutable: product.targetName, + CFBundleIdentifier: identifier, + CFBundleInfoDictionaryVersion: "6.0", + CFBundleName: product.targetName, // short display name of the bundle, localizable + CFBundlePackageType: packageType, + CFBundleShortVersionString: product.version || "1.0", // "release" version number, localizable + CFBundleSignature: signature, // legacy creator code in Mac OS Classic, can be ignored + CFBundleVersion: product.version || "1.0.0" // build version number, must be 3 octets + }; + } + + validate: { + if (!qbs.targetOS.contains("darwin")) + return; + if (!bundleSettingsProbe.found) { + var error = "Bundle product type " + _productTypeIdentifier + " is not supported."; + if ((_productTypeIdentifier || "").startsWith("com.apple.product-type.")) + error += " You may need to upgrade Xcode."; + throw error; + } + + var validator = new ModUtils.PropertyValidator("bundle"); + validator.setRequiredProperty("bundleName", bundleName); + validator.setRequiredProperty("infoPlistPath", infoPlistPath); + validator.setRequiredProperty("pbdevelopmentPlistPath", pbdevelopmentPlistPath); + validator.setRequiredProperty("pkgInfoPath", pkgInfoPath); + validator.setRequiredProperty("versionPlistPath", versionPlistPath); + validator.setRequiredProperty("executablePath", executablePath); + validator.setRequiredProperty("contentsFolderPath", contentsFolderPath); + validator.setRequiredProperty("documentationFolderPath", documentationFolderPath); + validator.setRequiredProperty("executableFolderPath", executableFolderPath); + validator.setRequiredProperty("executablesFolderPath", executablesFolderPath); + validator.setRequiredProperty("frameworksFolderPath", frameworksFolderPath); + validator.setRequiredProperty("javaFolderPath", javaFolderPath); + validator.setRequiredProperty("localizedResourcesFolderPath", localizedResourcesFolderPath); + validator.setRequiredProperty("pluginsFolderPath", pluginsFolderPath); + validator.setRequiredProperty("privateHeadersFolderPath", privateHeadersFolderPath); + validator.setRequiredProperty("publicHeadersFolderPath", publicHeadersFolderPath); + validator.setRequiredProperty("scriptsFolderPath", scriptsFolderPath); + validator.setRequiredProperty("sharedFrameworksFolderPath", sharedFrameworksFolderPath); + validator.setRequiredProperty("sharedSupportFolderPath", sharedSupportFolderPath); + validator.setRequiredProperty("unlocalizedResourcesFolderPath", unlocalizedResourcesFolderPath); + + if (packageType === "FMWK") { + validator.setRequiredProperty("frameworkVersion", frameworkVersion); + validator.setRequiredProperty("versionsFolderPath", versionsFolderPath); + } + + // extension and infoStringsPath might not be set + return validator.validate(); + } + + FileTagger { + fileTags: ["infoplist"] + patterns: ["Info.plist", "*-Info.plist"] + } + + Rule { + condition: qbs.targetOS.contains("darwin") + multiplex: true + inputs: ["qbs", "infoplist", "partial_infoplist"] + + outputFileTags: ["aggregate_infoplist"] + outputArtifacts: { + var artifacts = []; + var embed = ModUtils.moduleProperty(product, "embedInfoPlist"); + if (ModUtils.moduleProperty(product, "isBundle") || embed) { + artifacts.push({ + filePath: FileInfo.joinPaths( + product.destinationDirectory, embed + ? product.name + "-Info.plist" + : ModUtils.moduleProperty(product, "infoPlistPath")), + fileTags: ["aggregate_infoplist"] + }); + } + return artifacts; + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating Info.plist for " + product.name; + cmd.highlight = "codegen"; + cmd.infoPlistFiles = inputs.infoplist; + cmd.partialInfoPlistFiles = inputs.partial_infoplist; + cmd.infoPlist = ModUtils.moduleProperty(product, "infoPlist") || {}; + cmd.processInfoPlist = ModUtils.moduleProperty(product, "processInfoPlist"); + cmd.infoPlistFormat = ModUtils.moduleProperty(product, "infoPlistFormat"); + cmd.extraEnv = ModUtils.moduleProperty(product, "extraEnv"); + cmd.qmakeEnv = ModUtils.moduleProperty(product, "qmakeEnv"); + + cmd.buildEnv = product.moduleProperty("cpp", "buildEnv"); + cmd.defines = product.moduleProperty("cpp", "defines"); + cmd.platformDefines = product.moduleProperty("cpp", "platformDefines"); + cmd.compilerDefines = product.moduleProperty("cpp", "compilerDefines"); + cmd.allDefines = [].concat(cmd.defines || []).concat(cmd.platformDefines || []).concat(cmd.compilerDefines || []); + + cmd.developerPath = product.moduleProperty("xcode", "developerPath"); + cmd.platformInfoPlist = product.moduleProperty("xcode", "platformInfoPlist"); + cmd.sdkSettingsPlist = product.moduleProperty("xcode", "sdkSettingsPlist"); + cmd.toolchainInfoPlist = product.moduleProperty("xcode", "toolchainInfoPlist"); + + cmd.osBuildVersion = product.moduleProperty("qbs", "hostOSBuildVersion"); + + cmd.sourceCode = function() { + var plist, process, key, i; + + // Contains the combination of default, file, and in-source keys and values + // Start out with the contents of this file as the "base", if given + var aggregatePlist = {}; + for (i in infoPlistFiles) { + aggregatePlist = BundleTools.infoPlistContents(infoPlistFiles[i].filePath); + infoPlistFormat = (infoPlistFormat === "same-as-input") + ? BundleTools.infoPlistFormat(infoPlistFiles[i].filePath) + : "xml1"; + break; + } + + // Add local key-value pairs (overrides equivalent keys specified in the file if + // one was given) + for (key in infoPlist) { + if (infoPlist.hasOwnProperty(key)) + aggregatePlist[key] = infoPlist[key]; + } + + // Do some postprocessing if desired + if (processInfoPlist) { + // Add default values to the aggregate plist if the corresponding keys + // for those values are not already present + var defaultValues = ModUtils.moduleProperty(product, "defaultInfoPlist"); + for (key in defaultValues) { + if (defaultValues.hasOwnProperty(key) && !(key in aggregatePlist)) + aggregatePlist[key] = defaultValues[key]; + } + + // Add keys from platform's Info.plist if not already present + var platformInfo = {}; + var sdkSettings = {}; + var toolchainInfo = {}; + if (developerPath) { + plist = new PropertyList(); + try { + plist.readFromFile(platformInfoPlist); + platformInfo = plist.toObject(); + } finally { + plist.clear(); + } + + var additionalProps = platformInfo["AdditionalInfo"]; + for (key in additionalProps) { + // override infoPlist? + if (additionalProps.hasOwnProperty(key) && !(key in aggregatePlist)) + aggregatePlist[key] = defaultValues[key]; + } + props = platformInfo['OverrideProperties']; + for (key in props) { + aggregatePlist[key] = props[key]; + } + + plist = new PropertyList(); + try { + plist.readFromFile(sdkSettingsPlist); + sdkSettings = plist.toObject(); + } finally { + plist.clear(); + } + + plist = new PropertyList(); + try { + plist.readFromFile(toolchainInfoPlist); + toolchainInfo = plist.toObject(); + } finally { + plist.clear(); + } + } + + aggregatePlist["BuildMachineOSBuild"] = osBuildVersion; + + // setup env + env = { + "SDK_NAME": sdkSettings["CanonicalName"], + "XCODE_VERSION_ACTUAL": toolchainInfo["DTXcode"], + "SDK_PRODUCT_BUILD_VERSION": toolchainInfo["DTPlatformBuild"], + "GCC_VERSION": platformInfo["DTCompiler"], + "XCODE_PRODUCT_BUILD_VERSION": platformInfo["DTPlatformBuild"], + "PLATFORM_PRODUCT_BUILD_VERSION": platformInfo["ProductBuildVersion"], + } + env["MAC_OS_X_PRODUCT_BUILD_VERSION"] = osBuildVersion; + + for (key in extraEnv) + env[key] = extraEnv[key]; + + for (key in buildEnv) + env[key] = buildEnv[key]; + + for (key in qmakeEnv) + env[key] = qmakeEnv[key]; + + for (i = 0; i < allDefines.length; ++i) { + var parts = allDefines[i].split('='); + env[parts[0]] = parts[1]; + } + + DarwinTools.expandPlistEnvironmentVariables(aggregatePlist, env, true); + + // Add keys from partial Info.plists from asset catalogs, XIBs, and storyboards + for (i in partialInfoPlistFiles) { + var partialInfoPlist = BundleTools.infoPlistContents(partialInfoPlistFiles[i].filePath) || {}; + for (key in partialInfoPlist) { + if (partialInfoPlist.hasOwnProperty(key)) + aggregatePlist[key] = partialInfoPlist[key]; + } + } + } + + // Anything with an undefined or otherwise empty value should be removed + // Only JSON-formatted plists can have null values, other formats error out + // This also follows Xcode behavior + DarwinTools.cleanPropertyList(aggregatePlist); + + if (infoPlistFormat === "same-as-input") + infoPlistFormat = "xml1"; + + var validFormats = [ "xml1", "binary1", "json" ]; + if (!validFormats.contains(infoPlistFormat)) + throw("Invalid Info.plist format " + infoPlistFormat + ". " + + "Must be in [xml1, binary1, json]."); + + // Write the plist contents in the format appropriate for the current platform + plist = new PropertyList(); + try { + plist.readFromObject(aggregatePlist); + plist.writeToFile(outputs.aggregate_infoplist[0].filePath, infoPlistFormat); + } finally { + plist.clear(); + } + } + return cmd; + } + } + + Rule { + condition: qbs.targetOS.contains("darwin") + multiplex: true + inputs: ["aggregate_infoplist"] + + outputFileTags: ["pkginfo"] + outputArtifacts: { + var artifacts = []; + if (ModUtils.moduleProperty(product, "isBundle") && ModUtils.moduleProperty(product, "generatePackageInfo")) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "pkgInfoPath")), + fileTags: ["pkginfo"] + }); + } + return artifacts; + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating PkgInfo for " + product.name; + cmd.highlight = "codegen"; + cmd.sourceCode = function() { + var infoPlist = BundleTools.infoPlistContents(inputs.aggregate_infoplist[0].filePath); + + var pkgType = infoPlist['CFBundlePackageType']; + if (!pkgType) + throw("CFBundlePackageType not found in Info.plist; this should not happen"); + + var pkgSign = infoPlist['CFBundleSignature']; + if (!pkgSign) + throw("CFBundleSignature not found in Info.plist; this should not happen"); + + var pkginfo = new TextFile(outputs.pkginfo[0].filePath, TextFile.WriteOnly); + pkginfo.write(pkgType + pkgSign); + pkginfo.close(); + } + return cmd; + } + } + + Rule { + condition: qbs.targetOS.contains("darwin") + multiplex: true + inputs: ["aggregate_infoplist", "pkginfo", "hpp", + "icns", "xcent", + "compiled_ibdoc", "compiled_assetcatalog", + "xcode.provisioningprofile.main"] + + outputFileTags: ["bundle", + "bundle.symlink.headers", "bundle.symlink.private-headers", + "bundle.symlink.resources", "bundle.symlink.executable", + "bundle.symlink.version", "bundle.hpp", "bundle.resource", + "bundle.provisioningprofile"] + outputArtifacts: { + var i, artifacts = []; + if (ModUtils.moduleProperty(product, "isBundle")) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName")), + fileTags: ["bundle"] + }); + + for (i in inputs["xcode.provisioningprofile.main"]) { + var ext = inputs["xcode.provisioningprofile.main"][i].fileName.split('.')[1]; + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, + ModUtils.moduleProperty(product, + "contentsFolderPath"), + "embedded." + ext), + fileTags: ["bundle.provisioningprofile"] + }); + } + + var packageType = ModUtils.moduleProperty(product, "packageType"); + var isShallow = ModUtils.moduleProperty(product, "isShallow"); + if (packageType === "FMWK" && !isShallow) { + var publicHeaders = ModUtils.moduleProperty(product, "publicHeaders"); + if (publicHeaders && publicHeaders.length) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "Headers"), + fileTags: ["bundle.symlink.headers"] + }); + } + + var privateHeaders = ModUtils.moduleProperty(product, "privateHeaders"); + if (privateHeaders && privateHeaders.length) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "PrivateHeaders"), + fileTags: ["bundle.symlink.private-headers"] + }); + } + + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "Resources"), + fileTags: ["bundle.symlink.resources"] + }); + + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), product.targetName), + fileTags: ["bundle.symlink.executable"] + }); + + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "versionsFolderPath"), "Current"), + fileTags: ["bundle.symlink.version"] + }); + } + + var headerTypes = ["public", "private"]; + for (var h in headerTypes) { + var sources = ModUtils.moduleProperty(product, headerTypes[h] + "Headers"); + var destination = FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, headerTypes[h] + "HeadersFolderPath")); + for (i in sources) { + artifacts.push({ + filePath: FileInfo.joinPaths(destination, FileInfo.fileName(sources[i])), + fileTags: ["bundle.hpp"] + }); + } + } + + sources = ModUtils.moduleProperty(product, "resources"); + for (i in sources) { + destination = BundleTools.destinationDirectoryForResource(product, {baseDir: FileInfo.path(sources[i]), fileName: FileInfo.fileName(sources[i])}); + artifacts.push({ + filePath: FileInfo.joinPaths(destination, FileInfo.fileName(sources[i])), + fileTags: ["bundle.resource"] + }); + } + } + return artifacts; + } + + prepare: { + var i, cmd, commands = []; + var packageType = ModUtils.moduleProperty(product, "packageType"); + + var bundleType = "bundle"; + if (packageType === "APPL") + bundleType = "application"; + if (packageType === "FMWK") + bundleType = "framework"; + + var bundles = outputs.bundle; + for (i in bundles) { + cmd = new Command("mkdir", ["-p", bundles[i].filePath]); + cmd.description = "creating " + bundleType + " " + product.targetName; + commands.push(cmd); + + cmd = new Command("touch", ["-c", bundles[i].filePath]); + cmd.silent = true; + commands.push(cmd); + } + + // Product is unbundled + if (commands.length === 0) { + cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function () { }; + commands.push(cmd); + } + + var symlinks = outputs["bundle.symlink.version"]; + for (i in symlinks) { + cmd = new Command("ln", ["-sfn", ModUtils.moduleProperty(product, "frameworkVersion"), + symlinks[i].filePath]); + cmd.silent = true; + commands.push(cmd); + } + + var publicHeaders = outputs["bundle.symlink.headers"]; + for (i in publicHeaders) { + cmd = new Command("ln", ["-sfn", "Versions/Current/Headers", + publicHeaders[i].filePath]); + cmd.silent = true; + commands.push(cmd); + } + + var privateHeaders = outputs["bundle.symlink.private-headers"]; + for (i in privateHeaders) { + cmd = new Command("ln", ["-sfn", "Versions/Current/PrivateHeaders", + privateHeaders[i].filePath]); + cmd.silent = true; + commands.push(cmd); + } + + var resources = outputs["bundle.symlink.resources"]; + for (i in resources) { + cmd = new Command("ln", ["-sfn", "Versions/Current/Resources", + resources[i].filePath]); + cmd.silent = true; + commands.push(cmd); + } + + var executables = outputs["bundle.symlink.executable"]; + for (i in executables) { + cmd = new Command("ln", ["-sf", FileInfo.joinPaths("Versions", "Current", product.targetName), + executables[i].filePath]); + cmd.silent = true; + commands.push(cmd); + } + + var provisioningProfiles = outputs["bundle.provisioningprofile"]; + for (i in provisioningProfiles) { + cmd = new JavaScriptCommand(); + cmd.description = "copying provisioning profile"; + cmd.highlight = "filegen"; + cmd.source = inputs["xcode.provisioningprofile.main"][i].filePath; + cmd.destination = provisioningProfiles[i].filePath; + cmd.sourceCode = function() { + File.copy(source, destination); + }; + commands.push(cmd); + } + + cmd = new JavaScriptCommand(); + cmd.description = "copying public headers"; + cmd.highlight = "filegen"; + cmd.sources = ModUtils.moduleProperty(product, "publicHeaders"); + cmd.destination = FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "publicHeadersFolderPath")); + cmd.sourceCode = function() { + var i; + for (var i in sources) { + File.copy(sources[i], FileInfo.joinPaths(destination, FileInfo.fileName(sources[i]))); + } + }; + if (cmd.sources && cmd.sources.length) + commands.push(cmd); + + cmd = new JavaScriptCommand(); + cmd.description = "copying private headers"; + cmd.highlight = "filegen"; + cmd.sources = ModUtils.moduleProperty(product, "privateHeaders"); + cmd.destination = FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "privateHeadersFolderPath")); + cmd.sourceCode = function() { + var i; + for (var i in sources) { + File.copy(sources[i], FileInfo.joinPaths(destination, FileInfo.fileName(sources[i]))); + } + }; + if (cmd.sources && cmd.sources.length) + commands.push(cmd); + + cmd = new JavaScriptCommand(); + cmd.description = "copying resources"; + cmd.highlight = "filegen"; + cmd.sources = ModUtils.moduleProperty(product, "resources"); + cmd.sourceCode = function() { + var i; + for (var i in sources) { + var destination = BundleTools.destinationDirectoryForResource(product, {baseDir: FileInfo.path(sources[i]), fileName: FileInfo.fileName(sources[i])}); + File.copy(sources[i], FileInfo.joinPaths(destination, FileInfo.fileName(sources[i]))); + } + }; + if (cmd.sources && cmd.sources.length) + commands.push(cmd); + + if (product.moduleProperty("qbs", "hostOS").contains("darwin")) { + for (i in bundles) { + var actualSigningIdentity = product.moduleProperty("xcode", "actualSigningIdentity"); + var codesignDisplayName = product.moduleProperty("xcode", "actualSigningIdentityDisplayName"); + if (actualSigningIdentity) { + // If this is a framework, we need to sign its versioned directory + var subpath = ""; + var frameworkVersion = ModUtils.moduleProperty(product, "frameworkVersion"); + if (frameworkVersion) { + subpath = ModUtils.moduleProperty(product, "contentsFolderPath"); + subpath = subpath.substring(subpath.indexOf(ModUtils.moduleProperty("qbs", "pathSeparator"))); + } + + var args = product.moduleProperty("xcode", "codesignFlags") || []; + args.push("--force"); + args.push("--sign", actualSigningIdentity); + args = args.concat(DarwinTools._codeSignTimestampFlags(product)); + + for (var j in inputs.xcent) { + args.push("--entitlements", inputs.xcent[j].filePath); + break; // there should only be one + } + args.push(bundles[i].filePath + subpath); + + cmd = new Command(product.moduleProperty("xcode", "codesignPath"), args); + cmd.description = "codesign " + + ModUtils.moduleProperty(product, "bundleName") + + " using " + codesignDisplayName + + " (" + actualSigningIdentity + ")"; + commands.push(cmd); + } + + if (product.type.contains("application") + && product.moduleProperty("qbs", "targetOS").contains("macos")) { + cmd = new Command(ModUtils.moduleProperty(product, "lsregisterPath"), + ["-f", bundles[i].filePath]); + cmd.description = "register " + ModUtils.moduleProperty(product, "bundleName"); + commands.push(cmd); + } + } + } + + return commands; + } + } +} diff --git a/share/qbs/modules/bundle/MacOSX-Package-Types.xcspec b/share/qbs/modules/bundle/MacOSX-Package-Types.xcspec new file mode 100644 index 00000000..b36353fc --- /dev/null +++ b/share/qbs/modules/bundle/MacOSX-Package-Types.xcspec @@ -0,0 +1,462 @@ +[ + { + "DefaultBuildSettings" : { + "EXECUTABLE_PATH" : "$(EXECUTABLE_NAME)", + "EXECUTABLE_PREFIX" : "", + "EXECUTABLE_SUFFIX" : "", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)" + }, + "Identifier" : "com.apple.package-type.mach-o-executable", + "Type" : "PackageType", + "Name" : "Mach-O Executable", + "Description" : "Mach-O executable", + "ProductReference" : { + "FileType" : "compiled.mach-o.executable", + "Name" : "$(EXECUTABLE_NAME)", + "IsLaunchable" : "YES" + } + }, + { + "DefaultBuildSettings" : { + "EXECUTABLE_PATH" : "$(EXECUTABLE_NAME)", + "EXECUTABLE_PREFIX" : "", + "EXECUTABLE_SUFFIX" : "", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)" + }, + "Identifier" : "com.apple.package-type.mach-o-objfile", + "Type" : "PackageType", + "Name" : "Mach-O Object File", + "Description" : "Mach-O Object File", + "ProductReference" : { + "FileType" : "compiled.mach-o.objfile", + "Name" : "$(EXECUTABLE_NAME)", + "IsLaunchable" : "NO" + } + }, + { + "DefaultBuildSettings" : { + "EXECUTABLE_PATH" : "$(EXECUTABLE_NAME)", + "EXECUTABLE_PREFIX" : "", + "EXECUTABLE_SUFFIX" : "", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)" + }, + "Identifier" : "com.apple.package-type.mach-o-dylib", + "Type" : "PackageType", + "Name" : "Mach-O Dynamic Library", + "Description" : "Mach-O dynamic library", + "ProductReference" : { + "FileType" : "compiled.mach-o.dylib", + "Name" : "$(EXECUTABLE_NAME)", + "IsLaunchable" : "NO" + } + }, + { + "DefaultBuildSettings" : { + "EXECUTABLE_PATH" : "$(EXECUTABLE_NAME)", + "EXECUTABLE_PREFIX" : "lib", + "EXECUTABLE_SUFFIX" : ".a", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)" + }, + "Identifier" : "com.apple.package-type.static-library", + "Type" : "PackageType", + "Name" : "Mach-O Static Library", + "Description" : "Mach-O static library", + "ProductReference" : { + "FileType" : "archive.ar", + "Name" : "$(EXECUTABLE_NAME)", + "IsLaunchable" : "NO" + } + }, + { + "DefaultBuildSettings" : { + "EXECUTABLE_PATH" : "$(EXECUTABLE_NAME)", + "EXECUTABLE_PREFIX" : "", + "EXECUTABLE_SUFFIX" : ".dylib", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)" + }, + "Identifier" : "com.apple.package-type.mach-o-bundle", + "Type" : "PackageType", + "Name" : "Mach-O Loadable", + "Description" : "Mach-O loadable", + "ProductReference" : { + "FileType" : "compiled.mach-o.bundle", + "Name" : "$(EXECUTABLE_NAME)", + "IsLaunchable" : "NO" + } + }, + { + "DefaultBuildSettings" : { + "PUBLIC_HEADERS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/Headers", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)", + "EXECUTABLE_PREFIX" : "", + "PLUGINS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/PlugIns", + "DOCUMENTATION_FOLDER_PATH" : "$(LOCALIZED_RESOURCES_FOLDER_PATH)\/Documentation", + "EXECUTABLES_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/Executables", + "INFOSTRINGS_PATH" : "$(LOCALIZED_RESOURCES_FOLDER_PATH)\/InfoPlist.strings", + "INFOPLIST_PATH" : "$(CONTENTS_FOLDER_PATH)\/Info.plist", + "EXECUTABLE_SUFFIX" : "", + "VERSIONPLIST_PATH" : "$(CONTENTS_FOLDER_PATH)\/version.plist", + "SHARED_SUPPORT_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/SharedSupport", + "EXECUTABLE_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/MacOS", + "PBDEVELOPMENTPLIST_PATH" : "$(CONTENTS_FOLDER_PATH)\/pbdevelopment.plist", + "FRAMEWORKS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/Frameworks", + "LOCALIZED_RESOURCES_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)\/$(DEVELOPMENT_LANGUAGE).lproj", + "SCRIPTS_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)\/Scripts", + "WRAPPER_PREFIX" : "", + "PRIVATE_HEADERS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/PrivateHeaders", + "CONTENTS_FOLDER_PATH" : "$(WRAPPER_NAME)\/Contents", + "WRAPPER_NAME" : "$(WRAPPER_PREFIX)$(PRODUCT_NAME)$(WRAPPER_SUFFIX)", + "PKGINFO_PATH" : "$(CONTENTS_FOLDER_PATH)\/PkgInfo", + "EXECUTABLE_PATH" : "$(EXECUTABLE_FOLDER_PATH)\/$(EXECUTABLE_NAME)", + "UNLOCALIZED_RESOURCES_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/Resources", + "JAVA_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)\/Java", + "SHARED_FRAMEWORKS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/SharedFrameworks", + "WRAPPER_SUFFIX" : ".bundle" + }, + "Identifier" : "com.apple.package-type.wrapper", + "Type" : "PackageType", + "Name" : "Wrapper", + "Description" : "Wrapper", + "ProductReference" : { + "FileType" : "wrapper.cfbundle", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + } + }, + { + "ProductReference" : { + "FileType" : "wrapper.cfbundle", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "CONTENTS_FOLDER_PATH" : "$(WRAPPER_NAME)", + "UNLOCALIZED_RESOURCES_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "SHALLOW_BUNDLE" : "YES", + "EXECUTABLE_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper", + "Name" : "Wrapper (Shallow)", + "Identifier" : "com.apple.package-type.wrapper.shallow", + "Description" : "Shallow Wrapper" + }, + { + "ProductReference" : { + "FileType" : "wrapper.application", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "YES" + }, + "DefaultBuildSettings" : { + "GENERATE_PKGINFO_FILE" : "YES" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper", + "Name" : "Application Wrapper", + "Identifier" : "com.apple.package-type.wrapper.application", + "Description" : "Application Wrapper" + }, + { + "ProductReference" : { + "FileType" : "wrapper.application", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "YES" + }, + "DefaultBuildSettings" : { + "UNLOCALIZED_RESOURCES_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "SHALLOW_BUNDLE" : "YES", + "GENERATE_PKGINFO_FILE" : "YES" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper.shallow", + "Name" : "Application Wrapper (Shallow)", + "Identifier" : "com.apple.package-type.wrapper.application.shallow", + "Description" : "Shallow Application Wrapper" + }, + { + "ProductReference" : { + "FileType" : "wrapper.cfbundle", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "PRIVATE_HEADERS_FOLDER_PATH" : "$(KEXT_FRAMEWORK)\/Contents\/PrivateHeaders\/$(KEXT_FAMILY_NAME)", + "PUBLIC_HEADERS_FOLDER_PATH" : "$(KEXT_FRAMEWORK)\/Contents\/Headers\/$(KEXT_FAMILY_NAME)" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper", + "Name" : "Kernel Extension Wrapper", + "Identifier" : "com.apple.package-type.wrapper.kernel-extension", + "Description" : "Kernel Extension Wrapper" + }, + { + "ProductReference" : { + "FileType" : "wrapper.cfbundle", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "PRIVATE_HEADERS_FOLDER_PATH" : "$(KEXT_FRAMEWORK)\/Contents\/PrivateHeaders\/$(KEXT_FAMILY_NAME)", + "PUBLIC_HEADERS_FOLDER_PATH" : "$(KEXT_FRAMEWORK)\/Contents\/Headers\/$(KEXT_FAMILY_NAME)", + "SHALLOW_BUNDLE" : "YES" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper.shallow", + "Name" : "Kernel Extension Wrapper (Shallow)", + "Identifier" : "com.apple.package-type.wrapper.kernel-extension.shallow", + "Description" : "Shallow Kernel Extension Wrapper" + }, + { + "DefaultBuildSettings" : { + "PUBLIC_HEADERS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/Headers", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)", + "EXECUTABLE_PREFIX" : "", + "PLUGINS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/PlugIns", + "DOCUMENTATION_FOLDER_PATH" : "$(LOCALIZED_RESOURCES_FOLDER_PATH)\/Documentation", + "EXECUTABLES_FOLDER_PATH" : "$(LOCALIZED_RESOURCES_FOLDER_PATH)", + "INFOPLIST_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)\/Info.plist", + "EXECUTABLE_SUFFIX" : "", + "INFOPLISTSTRINGS_PATH" : "$(LOCALIZED_RESOURCES_FOLDER_PATH)\/InfoPlist.strings", + "VERSIONPLIST_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)\/version.plist", + "SHARED_SUPPORT_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)", + "EXECUTABLE_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "PBDEVELOPMENTPLIST_PATH" : "$(CONTENTS_FOLDER_PATH)\/pbdevelopment.plist", + "VERSIONS_FOLDER_PATH" : "$(WRAPPER_NAME)\/Versions", + "FRAMEWORKS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/Frameworks", + "CODESIGNING_FOLDER_PATH" : "$(TARGET_BUILD_DIR)\/$(CONTENTS_FOLDER_PATH)", + "LOCALIZED_RESOURCES_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)\/$(DEVELOPMENT_LANGUAGE).lproj", + "SCRIPTS_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)\/Scripts", + "WRAPPER_PREFIX" : "", + "PRIVATE_HEADERS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/PrivateHeaders", + "CURRENT_VERSION" : "Current", + "PKGINFO_PATH" : "$(WRAPPER_NAME)\/PkgInfo", + "WRAPPER_NAME" : "$(WRAPPER_PREFIX)$(PRODUCT_NAME)$(WRAPPER_SUFFIX)", + "CONTENTS_FOLDER_PATH" : "$(VERSIONS_FOLDER_PATH)\/$(FRAMEWORK_VERSION)", + "EXECUTABLE_PATH" : "$(EXECUTABLE_FOLDER_PATH)\/$(EXECUTABLE_NAME)", + "UNLOCALIZED_RESOURCES_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/Resources", + "JAVA_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)\/Java", + "SHARED_FRAMEWORKS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)\/SharedFrameworks", + "WRAPPER_SUFFIX" : ".framework" + }, + "Identifier" : "com.apple.package-type.wrapper.framework", + "Type" : "PackageType", + "Name" : "Framework Wrapper", + "Description" : "Framework wrapper", + "ProductReference" : { + "FileType" : "wrapper.framework", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + } + }, + { + "ProductReference" : { + "FileType" : "wrapper.framework.static", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "EXECUTABLE_SUFFIX" : "", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)", + "EXECUTABLE_PREFIX" : "" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper.framework", + "Name" : "Mach-O Static Framework", + "Identifier" : "com.apple.package-type.wrapper.framework.static", + "Description" : "Mach-O static framework" + }, + { + "ProductReference" : { + "FileType" : "wrapper.framework", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "UNLOCALIZED_RESOURCES_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "CONTENTS_FOLDER_PATH" : "$(WRAPPER_NAME)", + "SHALLOW_BUNDLE" : "YES", + "VERSIONS_FOLDER_PATH" : "$(WRAPPER_NAME)" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper.framework", + "Name" : "Shallow Framework Wrapper", + "Identifier" : "com.apple.package-type.wrapper.framework.shallow", + "Description" : "Shallow framework wrapper" + }, + { + "ProductReference" : { + "FileType" : "wrapper.cfbundle", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "WRAPPER_SUFFIX" : "xctest" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper", + "Name" : "Unit Test Bundle", + "Identifier" : "com.apple.package-type.bundle.unit-test", + "Description" : "Unit Test Bundle" + }, + { + "ProductReference" : { + "FileType" : "wrapper.cfbundle", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "WRAPPER_SUFFIX" : "octest" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper", + "Name" : "OCUnit Test Bundle", + "Identifier" : "com.apple.package-type.bundle.ocunit-test", + "Description" : "OCUnit Test Bundle" + }, + { + "ProductReference" : { + "FileType" : "folder", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "EXECUTABLE_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "JAVA_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)", + "INFOSTRINGS_PATH" : "$(LOCALIZED_RESOURCES_FOLDER_PATH)\/ContentInfo.strings", + "INFOPLIST_PATH" : "$(WRAPPER_NAME)\/ContentInfo.plist", + "WRAPPER_SUFFIX" : "", + "UNLOCALIZED_RESOURCES_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "DOCUMENTATION_FOLDER_PATH" : "$(LOCALIZED_RESOURCES_FOLDER_PATH)", + "EXECUTABLES_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "LOCALIZED_RESOURCES_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)\/$(DEVELOPMENT_LANGUAGE).lproj", + "PLUGINS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "PUBLIC_HEADERS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "SHARED_SUPPORT_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "SHARED_FRAMEWORKS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "PRIVATE_HEADERS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)", + "SCRIPTS_FOLDER_PATH" : "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)", + "FRAMEWORKS_FOLDER_PATH" : "$(CONTENTS_FOLDER_PATH)" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper", + "Name" : "In-App Purchase Content", + "Identifier" : "com.apple.package-type.in-app-purchase-content", + "Description" : "In-App Purchase Content" + }, + { + "ProductReference" : { + "FileType" : "wrapper.xpc-service", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "WRAPPER_SUFFIX" : ".xpc" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper", + "Name" : "XPC Service", + "Identifier" : "com.apple.package-type.xpc-service", + "Description" : "XPC Service" + }, + { + "ProductReference" : { + "FileType" : "wrapper.app-extension", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "WRAPPER_SUFFIX" : ".pluginkit" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.xpc-service", + "Name" : "PlugInKit PlugIn", + "Identifier" : "com.apple.package-type.pluginkit-plugin", + "Description" : "PlugInKit PlugIn" + }, + { + "ProductReference" : { + "FileType" : "wrapper.app-extension", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + "WRAPPER_SUFFIX" : ".appex" + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.pluginkit-plugin", + "Name" : "App Extension", + "Identifier" : "com.apple.package-type.app-extension", + "Description" : "App Extension" + }, + { + "ProductReference" : { + "FileType" : "wrapper.spotlight-importer", + "Name" : "$(WRAPPER_NAME)", + "IsLaunchable" : "NO" + }, + "DefaultBuildSettings" : { + + }, + "Type" : "PackageType", + "BasedOn" : "com.apple.package-type.wrapper", + "Name" : "Spotlight Importer", + "Identifier" : "com.apple.package-type.spotlight-importer", + "Description" : "Spotlight Importer" + }, + { + "DefaultBuildSettings" : { + "EXECUTABLE_PATH" : "$(EXECUTABLE_NAME)", + "JAVA_MAKE_ZIPFILE" : "NO", + "JAVA_ARCHIVE_CLASSES" : "YES", + "EXECUTABLE_PREFIX" : "", + "EXECUTABLE_SUFFIX" : ".jar", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_SUFFIX)" + }, + "Identifier" : "com.apple.package-type.jarfile", + "Type" : "PackageType", + "Name" : "Jar File", + "Description" : "Jar file", + "ProductReference" : { + "FileType" : "archive.jar", + "Name" : "$(EXECUTABLE_NAME)", + "IsLaunchable" : "NO" + } + }, + { + "DefaultBuildSettings" : { + "EXECUTABLE_PATH" : "$(EXECUTABLE_NAME)", + "JAVA_MAKE_ZIPFILE" : "YES", + "JAVA_ARCHIVE_CLASSES" : "YES", + "EXECUTABLE_PREFIX" : "", + "EXECUTABLE_SUFFIX" : ".zip", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_SUFFIX)" + }, + "Identifier" : "com.apple.package-type.zipfile", + "Type" : "PackageType", + "Name" : "Zip File", + "Description" : "Zip file", + "ProductReference" : { + "FileType" : "archive.zip", + "Name" : "$(EXECUTABLE_NAME)", + "IsLaunchable" : "NO" + } + }, + { + "DefaultBuildSettings" : { + "EXECUTABLE_PATH" : "$(EXECUTABLE_NAME)", + "JAVA_ARCHIVE_CLASSES" : "NO", + "EXECUTABLE_PREFIX" : "", + "EXECUTABLE_SUFFIX" : "", + "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_SUFFIX)" + }, + "Identifier" : "com.apple.package-type.javaclassfolder", + "Type" : "PackageType", + "Name" : "Class Folder", + "Description" : "Class folder", + "ProductReference" : { + "FileType" : "wrapper.java-classfolder", + "Name" : "$(EXECUTABLE_NAME)", + "IsLaunchable" : "NO" + } + } +] diff --git a/share/qbs/modules/bundle/MacOSX-Product-Types.xcspec b/share/qbs/modules/bundle/MacOSX-Product-Types.xcspec new file mode 100644 index 00000000..51c778f0 --- /dev/null +++ b/share/qbs/modules/bundle/MacOSX-Product-Types.xcspec @@ -0,0 +1,585 @@ +[ + { + "IconNamePrefix" : "TargetExecutable", + "DefaultBuildProperties" : { + "REZ_EXECUTABLE" : "YES", + "FULL_PRODUCT_NAME" : "$(EXECUTABLE_NAME)", + "LIBRARY_FLAG_NOSPACE" : "YES", + "INSTALL_PATH" : "\/usr\/local\/bin", + "GCC_INLINES_ARE_PRIVATE_EXTERN" : "YES", + "FRAMEWORK_FLAG_PREFIX" : "-framework", + "GCC_DYNAMIC_NO_PIC" : "NO", + "GCC_SYMBOLS_PRIVATE_EXTERN" : "YES", + "CODE_SIGNING_ALLOWED" : "YES", + "STRIP_STYLE" : "all", + "EXECUTABLE_PREFIX" : "", + "EXECUTABLE_SUFFIX" : "", + "LIBRARY_FLAG_PREFIX" : "-l" + }, + "PackageTypes" : [ + "com.apple.package-type.mach-o-executable" + ], + "Type" : "ProductType", + "DefaultTargetName" : "Command-line Tool", + "Name" : "Command-line Tool", + "Identifier" : "com.apple.product-type.tool", + "Description" : "Standalone command-line tool", + "Class" : "PBXToolProductType" + }, + { + "IconNamePrefix" : "TargetExecutable", + "IsJava" : "YES", + "PackageTypes" : [ + "com.apple.package-type.jarfile", + "com.apple.package-type.zipfile", + "com.apple.package-type.javaclassfolder" + ], + "Type" : "ProductType", + "DefaultTargetName" : "Java Command-line Tool", + "Name" : "Java Command-line Tool", + "Identifier" : "com.apple.product-type.tool.java", + "Description" : "Java Command-line tool", + "DefaultBuildProperties" : { + "REZ_EXECUTABLE" : "YES", + "INSTALL_PATH" : "\/usr\/local\/bin", + "FULL_PRODUCT_NAME" : "$(EXECUTABLE_NAME)" + } + }, + { + "IconNamePrefix" : "TargetPlugin", + "DefaultBuildProperties" : { + "DEAD_CODE_STRIPPING" : "NO", + "REZ_EXECUTABLE" : "YES", + "LINK_WITH_STANDARD_LIBRARIES" : "NO", + "FULL_PRODUCT_NAME" : "$(EXECUTABLE_NAME)", + "LIBRARY_FLAG_NOSPACE" : "YES", + "FRAMEWORK_FLAG_PREFIX" : "-framework", + "INSTALL_PATH" : "$(HOME)\/Objects", + "SKIP_INSTALL" : "YES", + "GCC_INLINES_ARE_PRIVATE_EXTERN" : "YES", + "KEEP_PRIVATE_EXTERNS" : "YES", + "EXECUTABLE_EXTENSION" : "o", + "PUBLIC_HEADERS_FOLDER_PATH" : "\/usr\/local\/include", + "MACH_O_TYPE" : "mh_object", + "EXECUTABLE_SUFFIX" : ".$(EXECUTABLE_EXTENSION)", + "LIBRARY_FLAG_PREFIX" : "-l", + "PRIVATE_HEADERS_FOLDER_PATH" : "\/usr\/local\/include", + "STRIP_STYLE" : "debugging" + }, + "PackageTypes" : [ + "com.apple.package-type.mach-o-objfile" + ], + "Type" : "ProductType", + "DefaultTargetName" : "Object File", + "Name" : "Object File", + "Identifier" : "com.apple.product-type.objfile", + "Description" : "Object File", + "Class" : "XCStandaloneExecutableProductType" + }, + { + "IconNamePrefix" : "TargetLibrary", + "DefaultBuildProperties" : { + "LIBRARY_FLAG_PREFIX" : "-l", + "STRIP_STYLE" : "debugging", + "REZ_EXECUTABLE" : "YES", + "FULL_PRODUCT_NAME" : "$(EXECUTABLE_NAME)", + "LD_DYLIB_INSTALL_NAME" : "$(DYLIB_INSTALL_NAME_BASE:standardizepath)\/$(EXECUTABLE_PATH)", + "DYLIB_COMPATIBILITY_VERSION" : "1", + "INSTALL_PATH" : "\/usr\/local\/lib", + "FRAMEWORK_FLAG_PREFIX" : "-framework", + "LIBRARY_FLAG_NOSPACE" : "YES", + "GCC_INLINES_ARE_PRIVATE_EXTERN" : "YES", + "CODE_SIGNING_ALLOWED" : "YES", + "CODE_SIGNING_REQUIRED" : "NO", + "EXECUTABLE_EXTENSION" : "dylib", + "PUBLIC_HEADERS_FOLDER_PATH" : "\/usr\/local\/include", + "DYLIB_INSTALL_NAME_BASE" : "$(INSTALL_PATH)", + "EXECUTABLE_SUFFIX" : ".$(EXECUTABLE_EXTENSION)", + "PRIVATE_HEADERS_FOLDER_PATH" : "\/usr\/local\/include", + "MACH_O_TYPE" : "mh_dylib", + "DYLIB_CURRENT_VERSION" : "1" + }, + "PackageTypes" : [ + "com.apple.package-type.mach-o-dylib" + ], + "Type" : "ProductType", + "DefaultTargetName" : "Dynamic Library", + "Name" : "Dynamic Library", + "Identifier" : "com.apple.product-type.library.dynamic", + "Description" : "Dynamic library", + "Class" : "PBXDynamicLibraryProductType" + }, + { + "IconNamePrefix" : "TargetLibrary", + "DefaultBuildProperties" : { + "STRIP_STYLE" : "debugging", + "CLANG_ENABLE_MODULE_DEBUGGING" : "NO", + "REZ_EXECUTABLE" : "YES", + "FULL_PRODUCT_NAME" : "$(EXECUTABLE_NAME)", + "LIBRARY_FLAG_NOSPACE" : "YES", + "FRAMEWORK_FLAG_PREFIX" : "-framework", + "INSTALL_PATH" : "\/usr\/local\/lib", + "SEPARATE_STRIP" : "YES", + "EXECUTABLE_EXTENSION" : "a", + "EXECUTABLE_PREFIX" : "lib", + "PUBLIC_HEADERS_FOLDER_PATH" : "\/usr\/local\/include", + "EXECUTABLE_SUFFIX" : ".$(EXECUTABLE_EXTENSION)", + "LIBRARY_FLAG_PREFIX" : "-l", + "PRIVATE_HEADERS_FOLDER_PATH" : "\/usr\/local\/include", + "MACH_O_TYPE" : "staticlib" + }, + "AlwaysPerformSeparateStrip" : "YES", + "PackageTypes" : [ + "com.apple.package-type.static-library" + ], + "Type" : "ProductType", + "DefaultTargetName" : "Static Library", + "Name" : "Static Library", + "Identifier" : "com.apple.product-type.library.static", + "Description" : "Static library", + "Class" : "PBXStaticLibraryProductType" + }, + { + "IconNamePrefix" : "TargetPlugin", + "IsJava" : "YES", + "PackageTypes" : [ + "com.apple.package-type.jarfile", + "com.apple.package-type.zipfile", + "com.apple.package-type.javaclassfolder" + ], + "Type" : "ProductType", + "DefaultTargetName" : "Java Library", + "Name" : "Java Library", + "Identifier" : "com.apple.product-type.library.java.archive", + "Description" : "Java library packaged as a Jar file, Zip file, or class folder", + "DefaultBuildProperties" : { + "INSTALL_PATH" : "\/usr\/local\/lib", + "FULL_PRODUCT_NAME" : "$(PRODUCT_NAME)" + } + }, + { + "HasInfoPlistStrings" : "YES", + "Description" : "Generic bundle", + "HasInfoPlist" : "YES", + "Name" : "Bundle", + "Class" : "PBXBundleProductType", + "DefaultTargetName" : "Bundle", + "DefaultBuildProperties" : { + "LIBRARY_FLAG_NOSPACE" : "YES", + "WRAPPER_NAME" : "$(WRAPPER_PREFIX)$(PRODUCT_NAME)$(WRAPPER_SUFFIX)", + "FRAMEWORK_FLAG_PREFIX" : "-framework", + "GCC_INLINES_ARE_PRIVATE_EXTERN" : "YES", + "WRAPPER_SUFFIX" : ".$(WRAPPER_EXTENSION)", + "FULL_PRODUCT_NAME" : "$(WRAPPER_NAME)", + "WRAPPER_EXTENSION" : "bundle", + "CODE_SIGNING_ALLOWED" : "YES", + "WRAPPER_PREFIX" : "", + "STRIP_STYLE" : "non-global", + "MACH_O_TYPE" : "mh_bundle", + "LIBRARY_FLAG_PREFIX" : "-l" + }, + "PackageTypes" : [ + "com.apple.package-type.wrapper", + "com.apple.package-type.wrapper.shallow" + ], + "IsWrapper" : "YES", + "Type" : "ProductType", + "Identifier" : "com.apple.product-type.bundle", + "IconNamePrefix" : "TargetPlugin" + }, + { + "PackageTypes" : [ + "com.apple.package-type.wrapper.shallow" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.bundle", + "Name" : "Bundle (Shallow)", + "Identifier" : "com.apple.product-type.bundle.shallow", + "Description" : "Bundle (Shallow)", + "Class" : "PBXBundleProductType" + }, + { + "Description" : "Application", + "Class" : "PBXApplicationProductType", + "Name" : "Application", + "RunpathSearchPathForEmbeddedFrameworks" : "@executable_path\/..\/Frameworks", + "ValidateEmbeddedBinaries" : "YES", + "ProvisioningProfileSupported" : "YES", + "DefaultTargetName" : "Application", + "DefaultBuildProperties" : { + "INSTALL_PATH" : "$(LOCAL_APPS_DIR)", + "WRAPPER_EXTENSION" : "app", + "GCC_DYNAMIC_NO_PIC" : "NO", + "STRIP_STYLE" : "all", + "CODE_SIGNING_ALLOWED" : "YES", + "GCC_INLINES_ARE_PRIVATE_EXTERN" : "YES", + "WRAPPER_SUFFIX" : ".$(WRAPPER_EXTENSION)", + "GCC_SYMBOLS_PRIVATE_EXTERN" : "YES", + "MACH_O_TYPE" : "mh_execute" + }, + "BasedOn" : "com.apple.product-type.bundle", + "ProvisioningProfileRequired" : "NO", + "PackageTypes" : [ + "com.apple.package-type.wrapper.application" + ], + "Type" : "ProductType", + "CanEmbedCompilerSanitizerLibraries" : "YES", + "Identifier" : "com.apple.product-type.application", + "IconNamePrefix" : "TargetApp" + }, + { + "PackageTypes" : [ + "com.apple.package-type.wrapper.application.shallow" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.application", + "Name" : "Application (Shallow Bundle)", + "Identifier" : "com.apple.product-type.application.shallow", + "Description" : "Application (Shallow Bundle)", + "Class" : "PBXApplicationProductType" + }, + { + "DefaultBuildProperties" : { + "PKGINFO_PATH" : "", + "INFOPLIST_PATH" : "" + }, + "IsJava" : "YES", + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.application", + "Name" : "Java Application", + "Identifier" : "com.apple.product-type.application.java", + "Description" : "Java Application", + "DefaultTargetName" : "Java Application" + }, + { + "IconNamePrefix" : "TargetFramework", + "DefaultTargetName" : "Framework", + "DefaultBuildProperties" : { + "CODE_SIGNING_REQUIRES_TEAM" : "YES", + "LD_DYLIB_INSTALL_NAME" : "$(DYLIB_INSTALL_NAME_BASE:standardizepath)\/$(EXECUTABLE_PATH)", + "CODE_SIGNING_REQUIRED" : "NO", + "CODE_SIGNING_ALLOWED" : "YES", + "INSTALL_PATH" : "$(LOCAL_LIBRARY_DIR)\/Frameworks", + "WRAPPER_SUFFIX" : ".$(WRAPPER_EXTENSION)", + "WRAPPER_EXTENSION" : "framework", + "FRAMEWORK_VERSION" : "A", + "ENTITLEMENTS_REQUIRED" : "NO", + "STRIP_STYLE" : "debugging", + "DYLIB_INSTALL_NAME_BASE" : "$(INSTALL_PATH)", + "MACH_O_TYPE" : "mh_dylib" + }, + "PackageTypes" : [ + "com.apple.package-type.wrapper.framework" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.bundle", + "Name" : "Framework", + "Identifier" : "com.apple.product-type.framework", + "Description" : "Framework", + "Class" : "PBXFrameworkProductType" + }, + { + "PackageTypes" : [ + "com.apple.package-type.wrapper.framework.shallow" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.framework", + "Name" : "Framework (Shallow Bundle)", + "Identifier" : "com.apple.product-type.framework.shallow", + "Description" : "Framework (Shallow Bundle)", + "Class" : "PBXFrameworkProductType" + }, + { + "IconNamePrefix" : "TargetFramework", + "DefaultTargetName" : "Static Framework", + "DefaultBuildProperties" : { + "SEPARATE_STRIP" : "YES", + "WRAPPER_EXTENSION" : "framework", + "DYLIB_INSTALL_NAME_BASE" : "", + "CODE_SIGNING_ALLOWED" : "NO", + "FRAMEWORK_VERSION" : "A", + "LD_DYLIB_INSTALL_NAME" : "", + "GCC_INLINES_ARE_PRIVATE_EXTERN" : "NO", + "INSTALL_PATH" : "$(LOCAL_LIBRARY_DIR)\/Frameworks", + "WRAPPER_SUFFIX" : ".$(WRAPPER_EXTENSION)", + "MACH_O_TYPE" : "staticlib" + }, + "AlwaysPerformSeparateStrip" : "YES", + "PackageTypes" : [ + "com.apple.package-type.wrapper.framework.static" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.framework", + "Name" : "Static Framework", + "Identifier" : "com.apple.product-type.framework.static", + "Description" : "Static Framework", + "Class" : "XCStaticFrameworkProductType" + }, + { + "DefaultTargetName" : "Kernel Extension", + "DefaultBuildProperties" : { + "GCC_ENABLE_BUILTIN_FUNCTIONS" : "NO", + "MODULE_START" : "0", + "PRIVATE_HEADERS_FOLDER_PATH" : "$(KEXT_FRAMEWORK)\/Contents\/PrivateHeaders\/$(KEXT_FAMILY_NAME)", + "WRAPPER_SUFFIX" : ".$(WRAPPER_EXTENSION)", + "MACH_O_TYPE" : "mh_execute", + "GCC_ENABLE_KERNEL_DEVELOPMENT" : "YES", + "MODULE_STOP" : "0", + "GCC_ENABLE_FLOATING_POINT_LIBRARY_CALLS" : "YES", + "GCC_PRODUCT_TYPE_PREPROCESSOR_DEFINITIONS" : "$(inherited) KERNEL KERNEL_PRIVATE DRIVER_PRIVATE APPLE NeXT", + "GCC_DISABLE_STATIC_FUNCTION_INLINING" : "YES", + "ENABLE_APPLE_KEXT_CODE_GENERATION" : "YES", + "CODE_SIGNING_ALLOWED" : "YES", + "GCC_FORCE_CPU_SUBTYPE_ALL" : "YES", + "WRAPPER_EXTENSION" : "kext", + "KERNEL_EXTENSION_HEADER_SEARCH_PATHS" : "$(KERNEL_FRAMEWORK)\/PrivateHeaders $(KERNEL_FRAMEWORK_HEADERS)", + "GCC_INLINES_ARE_PRIVATE_EXTERN" : "NO", + "KEXT_FRAMEWORK_NAME" : "Kernel", + "GCC_NO_COMMON_BLOCKS" : "YES", + "GCC_ENABLE_PASCAL_STRINGS" : "NO", + "PUBLIC_HEADERS_FOLDER_PATH" : "$(KEXT_FRAMEWORK)\/Contents\/Headers\/$(KEXT_FAMILY_NAME)", + "GCC_ENABLE_FUNCTION_INLINING" : "YES", + "KERNEL_FRAMEWORK_HEADERS" : "$(KERNEL_FRAMEWORK)\/Headers", + "KEXT_FAMILY_NAME" : "family", + "KEXT_FRAMEWORK" : "$(SYSTEM_LIBRARY_DIR)\/Frameworks\/$(KEXT_FRAMEWORK_NAME).framework", + "GCC_ENABLE_CPP_EXCEPTIONS" : "NO", + "GCC_ENABLE_CPP_RTTI" : "NO", + "MODULE_NAME" : "com.company.driver.modulename", + "GCC_USE_STANDARD_INCLUDE_SEARCHING" : "NO", + "KERNEL_FRAMEWORK" : "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)\/Frameworks\/Kernel.framework", + "MODULE_VERSION" : "1.0", + "INSTALL_PATH" : "$(DEFAULT_KEXT_INSTALL_PATH)", + "PRODUCT_TYPE_HEADER_SEARCH_PATHS" : "$(inherited) $(KERNEL_EXTENSION_HEADER_SEARCH_PATHS)", + "GCC_CHECK_RETURN_VALUE_OF_OPERATOR_NEW" : "YES", + "STRIP_STYLE" : "debugging" + }, + "PackageTypes" : [ + "com.apple.package-type.wrapper.kernel-extension", + "com.apple.package-type.wrapper.kernel-extension.shallow" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.bundle", + "Name" : "Kernel Extension", + "Identifier" : "com.apple.product-type.kernel-extension", + "Description" : "Kernel extension", + "Class" : "XCKernelExtensionProductType" + }, + { + "DefaultBuildProperties" : { + + }, + "PackageTypes" : [ + "com.apple.package-type.wrapper.kernel-extension.shallow" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.kernel-extension", + "Name" : "Kernel Extension (Shallow)", + "Identifier" : "com.apple.product-type.kernel-extension.shallow", + "Description" : "Kernel extension (shallow)", + "Class" : "XCKernelExtensionProductType" + }, + { + "DefaultTargetName" : "IOKit Kernel Extension", + "DefaultBuildProperties" : { + "CODE_SIGNING_ALLOWED" : "YES" + }, + "PackageTypes" : [ + "com.apple.package-type.wrapper.kernel-extension", + "com.apple.package-type.wrapper.kernel-extension.shallow" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.kernel-extension", + "Name" : "IOKit Kernel Extension", + "Identifier" : "com.apple.product-type.kernel-extension.iokit", + "Description" : "IOKit Kernel extension", + "Class" : "XCKernelExtensionProductType" + }, + { + "DefaultTargetName" : "IOKit Kernel Extension (Shallow)", + "DefaultBuildProperties" : { + + }, + "PackageTypes" : [ + "com.apple.package-type.wrapper.kernel-extension.shallow" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.kernel-extension", + "Name" : "IOKit Kernel Extension (Shallow)", + "Identifier" : "com.apple.product-type.kernel-extension.iokit.shallow", + "Description" : "IOKit Kernel extension (Shallow)", + "Class" : "XCKernelExtensionProductType" + }, + { + "DefaultBuildProperties" : { + "TEST_FRAMEWORK_SEARCH_PATHS" : [ + "$(inherited)", + "$(PLATFORM_DIR)\/Developer\/Library\/Frameworks" + ], + "PRODUCT_SPECIFIC_LDFLAGS" : "-framework XCTest", + "WRAPPER_EXTENSION" : "xctest", + "PRODUCT_TYPE_FRAMEWORK_SEARCH_PATHS" : "$(TEST_FRAMEWORK_SEARCH_PATHS)" + }, + "PackageTypes" : [ + "com.apple.package-type.bundle.unit-test" + ], + "CanEmbedCompilerSanitizerLibraries" : "YES", + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.bundle", + "Name" : "Unit Test Bundle", + "Identifier" : "com.apple.product-type.bundle.unit-test", + "Description" : "Unit Test Bundle", + "Class" : "PBXXCTestBundleProductType" + }, + { + "DefaultBuildProperties" : { + "TEST_FRAMEWORK_SEARCH_PATHS" : [ + "$(inherited)", + "$(PLATFORM_DIR)\/Developer\/Library\/Frameworks" + ], + "PRODUCT_SPECIFIC_LDFLAGS" : "-framework XCTest", + "WRAPPER_EXTENSION" : "xctest", + "USES_XCTRUNNER" : "YES", + "PRODUCT_TYPE_FRAMEWORK_SEARCH_PATHS" : "$(TEST_FRAMEWORK_SEARCH_PATHS)" + }, + "PackageTypes" : [ + "com.apple.package-type.bundle.unit-test" + ], + "ProvisioningProfileSupported" : "YES", + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.bundle.unit-test", + "Name" : "UI Testing Bundle", + "Identifier" : "com.apple.product-type.bundle.ui-testing", + "Description" : "UI Testing Bundle", + "Class" : "PBXXCTestBundleProductType" + }, + { + "DefaultBuildProperties" : { + "WRAPPER_EXTENSION" : "octest" + }, + "PackageTypes" : [ + "com.apple.package-type.bundle.ocunit-test" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.bundle", + "Name" : "OCUnit Test Bundle", + "Identifier" : "com.apple.product-type.bundle.ocunit-test", + "Description" : "OCUnit Test Bundle", + "Class" : "PBXBundleProductType" + }, + { + "HasInfoPlistStrings" : "NO", + "PackageTypes" : [ + "com.apple.package-type.in-app-purchase-content" + ], + "HasInfoPlist" : "YES", + "IsWrapper" : "YES", + "Type" : "ProductType", + "DefaultBuildProperties" : { + "FULL_PRODUCT_NAME" : "$(WRAPPER_NAME)" + }, + "Name" : "In-App Purchase Content", + "Identifier" : "com.apple.product-type.in-app-purchase-content", + "Description" : "In-App Purchase Content", + "Class" : "PBXBundleProductType" + }, + { + "IconNamePrefix" : "XPCService", + "DefaultTargetName" : "XPC Service", + "CanEmbedCompilerSanitizerLibraries" : "YES", + "DefaultBuildProperties" : { + "MACH_O_TYPE" : "mh_execute", + "WRAPPER_EXTENSION" : "xpc" + }, + "PackageTypes" : [ + "com.apple.package-type.xpc-service" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.bundle", + "Name" : "XPC Service", + "Identifier" : "com.apple.product-type.xpc-service", + "Description" : "XPC Service", + "Class" : "PBXBundleProductType" + }, + { + "IconNamePrefix" : "XPCService", + "DefaultTargetName" : "PlugInKit PlugIn", + "DefaultBuildProperties" : { + "WRAPPER_EXTENSION" : "pluginkit", + "PRODUCT_SPECIFIC_LDFLAGS" : "$(SDKROOT)\/System\/Library\/PrivateFrameworks\/PlugInKit.framework\/PlugInKit" + }, + "PackageTypes" : [ + "com.apple.package-type.pluginkit-plugin" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.xpc-service", + "Name" : "PlugInKit PlugIn", + "Identifier" : "com.apple.product-type.pluginkit-plugin", + "Description" : "PlugInKit PlugIn", + "Class" : "PBXBundleProductType" + }, + { + "Description" : "App Extension", + "Class" : "PBXBundleProductType", + "Name" : "App Extension", + "ProvisioningProfileSupported" : "YES", + "DefaultTargetName" : "App Extension", + "DefaultBuildProperties" : { + "APPLICATION_EXTENSION_API_ONLY" : "YES", + "PRODUCT_SPECIFIC_LDFLAGS" : "-e _NSExtensionMain", + "WRAPPER_EXTENSION" : "appex", + "CODE_SIGNING_ALLOWED" : "YES" + }, + "BasedOn" : "com.apple.product-type.pluginkit-plugin", + "ProvisioningProfileRequired" : "NO", + "PackageTypes" : [ + "com.apple.package-type.app-extension" + ], + "Type" : "ProductType", + "Identifier" : "com.apple.product-type.app-extension", + "IconNamePrefix" : "AppExtension" + }, + { + "Description" : "Xcode Extension", + "Class" : "PBXBundleProductType", + "Name" : "Xcode Extension", + "ProvisioningProfileSupported" : "YES", + "DefaultTargetName" : "Xcode Extension", + "DefaultBuildProperties" : { + "APPLICATION_EXTENSION_API_ONLY" : "YES", + "PRODUCT_SPECIFIC_LDFLAGS" : "-e _XCExtensionMain -lXcodeExtension -weak_framework XcodeKit", + "PRODUCT_TYPE_LIBRARY_SEARCH_PATHS" : [ + "$(inherited)", + "$(DEVELOPER_USR_DIR)\/lib" + ], + "CODE_SIGNING_ALLOWED" : "YES", + "WRAPPER_EXTENSION" : "appex", + "PRODUCT_TYPE_FRAMEWORK_SEARCH_PATHS" : [ + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)" + ] + }, + "BasedOn" : "com.apple.product-type.app-extension", + "ProvisioningProfileRequired" : "NO", + "PackageTypes" : [ + "com.apple.package-type.app-extension" + ], + "Type" : "ProductType", + "Identifier" : "com.apple.product-type.xcode-extension", + "IconNamePrefix" : "XcodeExtension" + }, + { + "DefaultTargetName" : "Spotlight", + "DefaultBuildProperties" : { + "CODE_SIGNING_ALLOWED" : "YES" + }, + "PackageTypes" : [ + "com.apple.package-type.spotlight-importer" + ], + "Type" : "ProductType", + "BasedOn" : "com.apple.product-type.bundle", + "Name" : "Spotlight Importer", + "Identifier" : "com.apple.product-type.spotlight-importer", + "Description" : "Spotlight Importer", + "Class" : "PBXBundleProductType" + } +] diff --git a/share/qbs/modules/bundle/bundle.js b/share/qbs/modules/bundle/bundle.js new file mode 100644 index 00000000..71210b83 --- /dev/null +++ b/share/qbs/modules/bundle/bundle.js @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var DarwinTools = loadExtension("qbs.DarwinTools"); +var Process = loadExtension("qbs.Process"); + +// HACK: Workaround until the PropertyList extension is supported cross-platform +var PropertyList2 = (function () { + function PropertyList2() { + } + PropertyList2.prototype.readFromFile = function (filePath) { + var str; + var process = new Process(); + try { + if (process.exec("plutil", ["-convert", "json", "-o", "-", filePath], false) === 0) { + str = process.readStdOut(); + } else { + var tf = new TextFile(filePath); + try { + str = tf.readAll(); + } finally { + tf.close(); + } + } + } finally { + process.close(); + } + + if (str) + this.obj = JSON.parse(str); + }; + PropertyList2.prototype.toObject = function () { + return this.obj; + }; + PropertyList2.prototype.clear = function () { + }; + return PropertyList2; +}()); + +// Order is significant due to productTypeIdentifier() search path +var _productTypeIdentifiers = { + "inapppurchase": "com.apple.product-type.in-app-purchase-content", + "applicationextension": "com.apple.product-type.app-extension", + "xpcservice": "com.apple.product-type.xpc-service", + "application": "com.apple.product-type.application", + "dynamiclibrary": "com.apple.product-type.framework", + "loadablemodule": "com.apple.product-type.bundle", + "staticlibrary": "com.apple.product-type.framework.static", + "kernelmodule": "com.apple.product-type.kernel-extension" +}; + +function productTypeIdentifier(productType) { + for (var k in _productTypeIdentifiers) { + if (productType.contains(k)) + return _productTypeIdentifiers[k]; + } + return "com.apple.package-type.wrapper"; +} + +var XcodeBuildSpecsReader = (function () { + function XcodeBuildSpecsReader(specsPath, separator, additionalSettings, useShallowBundles) { + this._additionalSettings = additionalSettings; + this._useShallowBundles = useShallowBundles; + var i; + var plist = new PropertyList2(); + var plist2 = new PropertyList2(); + try { + plist.readFromFile(specsPath + ["/MacOSX", "Package", "Types.xcspec"].join(separator)); + plist2.readFromFile(specsPath + ["/MacOSX", "Product", "Types.xcspec"].join(separator)); + this._packageTypes = plist.toObject(); + this._productTypes = plist2.toObject(); + this._types = {}; + for (i = 0; i < this._packageTypes.length; ++i) + this._types[this._packageTypes[i]["Identifier"]] = this._packageTypes[i]; + for (i = 0; i < this._productTypes.length; ++i) + this._types[this._productTypes[i]["Identifier"]] = this._productTypes[i]; + } finally { + plist.clear(); + plist2.clear(); + } + } + XcodeBuildSpecsReader.prototype.settings = function (typeIdentifier, recursive, skipPackageTypes) { + // Silently use shallow bundles when preferred since it seems to be some sort of automatic + // shadowing mechanism. For example, this matches Xcode behavior where static frameworks + // are shallow even though no such product specification exists, and also seems to match + // other behavior i.e. where productType in pbxproj files is never explicitly shallow. + if (this._useShallowBundles && this._types[typeIdentifier + ".shallow"] && !skipPackageTypes) + typeIdentifier += ".shallow"; + + var typesObject = this._types[typeIdentifier]; + if (typesObject) { + var buildProperties = {}; + + if (recursive) { + // Get all the settings for the product's package type + if (!skipPackageTypes && typesObject["PackageTypes"]) { + for (var k = 0; k < typesObject["PackageTypes"].length; ++k) { + var props = this.settings(typesObject["PackageTypes"][k], recursive, true); + for (var y in props) { + if (props.hasOwnProperty(y)) + buildProperties[y] = props[y]; + } + break; + } + } + + // Get all the settings for the product's inherited product type + if (typesObject["BasedOn"]) { + // We'll only do the auto shallow substitution for wrapper package types... + // this ensures that in-app purchase content bundles are non-shallow on both + // macOS and iOS, for example (which matches Xcode behavior) + var isWrapper = false; + if (typesObject["ProductReference"]) { + var fileType = typesObject["ProductReference"]["FileType"]; + if (fileType) + isWrapper = fileType.startsWith("wrapper."); + } + + // Prevent recursion loop if this spec's base plus .shallow would be the same + // as the current spec's identifier + var baseIdentifier = typesObject["BasedOn"]; + if (this._useShallowBundles && isWrapper + && this._types[baseIdentifier + ".shallow"] + && typeIdentifier !== baseIdentifier + ".shallow") + baseIdentifier += ".shallow"; + + props = this.settings(baseIdentifier, recursive, true); + for (y in props) { + if (props.hasOwnProperty(y)) + buildProperties[y] = props[y]; + } + } + } + + + if (typesObject["Type"] === "PackageType") { + props = typesObject["DefaultBuildSettings"]; + for (y in props) { + if (props.hasOwnProperty(y)) + buildProperties[y] = props[y]; + } + } + + if (typesObject["Type"] === "ProductType") { + props = typesObject["DefaultBuildProperties"]; + for (y in props) { + if (props.hasOwnProperty(y)) + buildProperties[y] = props[y]; + } + } + + return buildProperties; + } + }; + XcodeBuildSpecsReader.prototype.setting = function (typeIdentifier, settingName) { + var obj = this.settings(typeIdentifier, false); + if (obj) { + return obj[settingName]; + } + }; + XcodeBuildSpecsReader.prototype.expandedSettings = function (typeIdentifier) { + var obj = this.settings(typeIdentifier, true); + if (obj) { + for (var k in obj) + obj[k] = this.expandedSetting(typeIdentifier, k); + return obj; + } + }; + XcodeBuildSpecsReader.prototype.expandedSetting = function (typeIdentifier, settingName) { + var obj = this.settings(typeIdentifier, true); + if (obj) { + for (var x in this._additionalSettings) { + var additionalSetting = this._additionalSettings[x]; + if (additionalSetting !== undefined) + obj[x] = additionalSetting; + } + var setting = obj[settingName]; + var original; + while (original !== setting) { + original = setting; + setting = DarwinTools.expandPlistEnvironmentVariables({ key: setting }, obj, true)["key"]; + } + return setting; + } + }; + return XcodeBuildSpecsReader; +}()); diff --git a/share/qbs/modules/bundle/update-specs.sh b/share/qbs/modules/bundle/update-specs.sh new file mode 100755 index 00000000..08e862d0 --- /dev/null +++ b/share/qbs/modules/bundle/update-specs.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +############################################################################# +## +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qbs. +## +## $QT_BEGIN_LICENSE:LGPL$ +## 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 The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/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 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +# Update build specs from Xcode - this script should be run when new Xcode releases are made. +specs_dir="$(xcrun --sdk macosx --show-sdk-platform-path)/Developer/Library/Xcode/Specifications" +spec_files=("MacOSX Package Types.xcspec" "MacOSX Product Types.xcspec") +for spec_file in "${spec_files[@]}" ; do + printf "%s\n" "$(plutil -convert json -r -o - "$specs_dir/$spec_file")" > "${spec_file// /-}" +done +xcode_version="$(/usr/libexec/PlistBuddy -c 'Print CFBundleShortVersionString' \ + "$(xcode-select --print-path)/../Info.plist")" +echo "Updated build specs from Xcode $xcode_version" diff --git a/share/qbs/modules/cli/CLIModule.qbs b/share/qbs/modules/cli/CLIModule.qbs new file mode 100644 index 00000000..b9ff259f --- /dev/null +++ b/share/qbs/modules/cli/CLIModule.qbs @@ -0,0 +1,194 @@ +// base for Common Language Infrastructure modules +import qbs +import qbs.FileInfo +import qbs.ModUtils +import "cli.js" as CLI + +Module { + Depends { name: "bundle" } + + bundle.isBundle: false + + condition: false + + property string warningLevel: 'all' // 'none', 'all' + property bool treatWarningsAsErrors: false + property string architecture: "anycpu" // for the CLI this is a much better default than qbs.architecture + property string optimization: qbs.optimization + property bool debugInformation: qbs.debugInformation + property stringList defines + property stringList platformDefines: qbs.enableDebugCode ? [] : ["NDEBUG"] + property stringList compilerDefines + PropertyOptions { + name: "compilerDefines" + description: "preprocessor macros that are defined when using this particular compiler" + } + + property pathList libraryPaths + property string csharpCompilerName + property string csharpCompilerPath: FileInfo.joinPaths(toolchainInstallPath, csharpCompilerName) + property string vbCompilerName + property string vbCompilerPath: FileInfo.joinPaths(toolchainInstallPath, vbCompilerName) + property string fsharpCompilerName + property string fsharpCompilerPath: FileInfo.joinPaths(toolchainInstallPath, fsharpCompilerName) + property string resgenName: "resgen" + property string resgenPath: FileInfo.joinPaths(toolchainInstallPath, resgenName) + property string dynamicLibrarySuffix: ".dll" + property string executableSuffix: ".exe" + property string netmoduleSuffix: ".netmodule" + property string debugInfoSuffix + property stringList dynamicLibraries // list of names, will be linked with /reference:name + property stringList netmodules // list of netmodule files, will be linked with /addmodule:name + + property bool generateManifestFile: true + + property string toolchainInstallPath + + property stringList compilerFlags + PropertyOptions { + name: "compilerFlags" + description: "additional compiler flags" + } + + // Platform properties. Those are intended to be set by the toolchain setup + // and are prepended to the corresponding user properties. + property stringList platformCompilerFlags + + FileTagger { + patterns: ["*.cs", "*.CS"] + fileTags: ["cli.csharp"] + } + + FileTagger { + patterns: ["*.vb", "*.VB"] + fileTags: ["cli.vb"] + } + + FileTagger { + patterns: ["*.fs", "*.FS"] + fileTags: ["cli.fsharp"] + } + + FileTagger { + patterns: ["*.fsi", "*.FSI"] + fileTags: ["cli.fsharp_signature"] + } + + FileTagger { + patterns: ["*.resx", "*.RESX"] + fileTags: ["cli.resx"] + } + + validate: { + var validator = new ModUtils.PropertyValidator("cli"); + validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath); + validator.validate(); + } + + setupBuildEnvironment: { + var v = new ModUtils.EnvironmentVariable("PATH", qbs.pathListSeparator, + qbs.hostOS.contains("windows")); + v.prepend(toolchainInstallPath); + v.set(); + } + + Rule { + id: cliApplication + multiplex: true + inputs: ["cli.csharp", "cli.vb", "cli.fsharp"] + inputsFromDependencies: ["cli.netmodule", "dynamiclibrary", "cli.resources"] + + Artifact { + fileTags: ["application"] + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + + product.moduleProperty(product.moduleName, + "executableSuffix")) + } + + Artifact { + fileTags: ["debuginfo"] + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + + product.moduleProperty(product.moduleName, + "debugInfoSuffix")) + } + + prepare: { + return CLI.prepareCompiler(product, inputs, outputs.application[0]); + } + } + + Rule { + id: cliDynamicLibrary + multiplex: true + inputs: ["cli.csharp", "cli.vb", "cli.fsharp"] + inputsFromDependencies: ["cli.netmodule", "dynamiclibrary", "cli.resources"] + + Artifact { + fileTags: ["dynamiclibrary"] + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + + product.moduleProperty(product.moduleName, + "dynamicLibrarySuffix")) + } + + Artifact { + fileTags: ["debuginfo"] + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + + product.moduleProperty(product.moduleName, + "debugInfoSuffix")) + } + + prepare: { + return CLI.prepareCompiler(product, inputs, outputs.dynamiclibrary[0]); + } + } + + Rule { + id: netmodule + multiplex: true + inputs: ["cli.csharp", "cli.vb", "cli.fsharp"] + inputsFromDependencies: ["cli.netmodule", "dynamiclibrary", "cli.resources"] + + Artifact { + fileTags: ["cli.netmodule"] + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + + product.moduleProperty(product.moduleName, + "netmoduleSuffix")) + } + + Artifact { + fileTags: ["debuginfo"] + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + + product.moduleProperty(product.moduleName, + "debugInfoSuffix")) + } + + prepare: { + return CLI.prepareCompiler(product, inputs, outputs["cli.netmodule"][0]); + } + } + + Rule { + inputs: ["cli.resx"] + + Artifact { + fileTags: ["cli.resources"] + filePath: FileInfo.joinPaths(product.destinationDirectory, + input.completeBaseName + ".resources") + } + + prepare: { + var args = [ input.filePath, output.filePath ]; + var cmd = new Command(ModUtils.moduleProperty(product, "resgenPath"), args); + cmd.description = "building " + input.fileName; + cmd.highlight = "compiler"; + cmd.workingDirectory = FileInfo.path(output.filePath); + return cmd; + } + } +} diff --git a/share/qbs/modules/cli/cli.js b/share/qbs/modules/cli/cli.js new file mode 100644 index 00000000..0cebf681 --- /dev/null +++ b/share/qbs/modules/cli/cli.js @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = loadExtension("qbs.FileInfo"); + +function prepareCompiler(product, inputs, output) { + var i; + var args = ["/nologo"]; + + var platformDefines = product.moduleProperty("cli", "platformDefines"); + var compilerDefines = product.moduleProperty("cli", "compilerDefines"); + var defines = product.moduleProperty("cli", "defines"); + var platformCompilerFlags = product.moduleProperty("cli", "platformCompilerFlags"); + var compilerFlags = product.moduleProperty("cli", "compilerFlags") + var libraryPaths = product.moduleProperty("cli", "libraryPaths"); + var dynamicLibraries = product.moduleProperty("cli", "dynamicLibraries"); + var netmodules = product.moduleProperty("cli", "netmodules"); + var warnAsError = product.moduleProperty("cli", "treatWarningsAsErrors"); + var warningLevel = product.moduleProperty("cli", "warningLevel"); + var debugInformation = product.moduleProperty("cli", "debugInformation"); + var optimization = product.moduleProperty("cli", "optimization"); + var architecture = product.moduleProperty("cli", "architecture"); + + var csharpCompilerPath = product.moduleProperty("cli", "csharpCompilerPath"); + var vbCompilerPath = product.moduleProperty("cli", "vbCompilerPath"); + var fsharpCompilerPath = product.moduleProperty("cli", "fsharpCompilerPath"); + + var compilers = { + "cli.csharp": csharpCompilerPath, + "cli.vb": vbCompilerPath, + "cli.fsharp": fsharpCompilerPath + }; + + var pathFunction = product.moduleProperty("qbs", "hostOS").contains("windows") + ? FileInfo.toWindowsSeparators + : function (path) { return path; }; + + var outputDescription = "assembly"; + if (output.fileTags.contains("application")) { + args.push("/target:" + (product.consoleApplication === false ? "winexe" : "exe")); + } else if (output.fileTags.contains("dynamiclibrary")) { + args.push("/target:library"); + } else if (output.fileTags.contains("cli.netmodule")) { + args.push("/target:module"); + outputDescription = "netmodule"; + } + + // Make sure our input files are all the same language + var keys = Object.keys(inputs); + var language; + for (i in keys) { + if (Object.keys(compilers).contains(keys[i])) { + if (language) + throw("You cannot compile source files in more than one CLI language into a single " + outputDescription + "."); + language = keys[i]; + } + } + + if (!compilers[language]) { + throw("No CLI compiler available to compile " + language + " files."); + } + + // docs state "/platform is not available in the development environment in Visual C# Express" + // does this mean from the IDE or the compiler itself shipped with Express does not support it? + if (architecture !== undefined) { + if (architecture === "x86" || + architecture === "anycpu" || + architecture === "anycpu32bitpreferred") // requires .NET 4.5 + ; + else if (architecture === "x86_64") + architecture = "x64"; + else if (architecture === "ia64") + architecture = "Itanium"; + else if (architecture === "arm") + architecture = "ARM"; + else + throw("Invalid CLI architecture: " + architecture); + + args.push("/platform:" + architecture); + } + + if (debugInformation !== undefined) + args.push("/debug" + (debugInformation ? "+" : "-")); + + if (optimization !== undefined) + args.push("/optimize" + (optimization !== "none" ? "+" : "-")); + + if (warnAsError !== undefined) + args.push("/warnaserror" + (warnAsError ? "+" : "-")); + + if (warningLevel !== undefined) { + if (language === "cli.vb") { + if (warningLevel === "none" || warningLevel === 0) + args.push("/quiet"); + } else { + if (warningLevel === "all") + warningLevel = 4; + else if (warningLevel === "none") + warningLevel = 0; + else + warningLevel = parseInt(warningLevel, 10); + args.push("/warn:" + warningLevel); + } + } + + // Preprocessor defines + var allDefines = (platformDefines || []).concat(compilerDefines || []).concat(defines || []); + if (allDefines.length > 0) + args.push("/define:" + allDefines.join(";")); + + // Library search paths + for (i in libraryPaths) + args.push("/lib:" + libraryPaths.join(",")); + + // Dependent libraries + for (i in dynamicLibraries) { + args.push("/reference:" + + dynamicLibraries[i] + + product.moduleProperty("cli", "dynamicLibrarySuffix")); + } + + for (i in inputs.dynamiclibrary) + args.push("/reference:" + inputs.dynamiclibrary[i].filePath); + + // Dependent netmodules + for (i in netmodules) { + args.push("/addmodule:" + netmodules.map(function (f) { + return f + product.moduleProperty("cli", "netmoduleSuffix"); + }).join(";")); + } + + if (inputs["cli.netmodule"]) { + args.push("/addmodule:" + inputs["cli.netmodule"].map(function (f) { + return f.filePath; + }).join(";")); + } + + // Dependent resources + for (i in inputs["cli.resources"]) + args.push("/resource:" + pathFunction(inputs["cli.resources"][i].filePath)); + + // Additional compiler flags + args = args.concat((platformCompilerFlags || []).concat(compilerFlags || [])); + + args.push("/out:" + pathFunction(output.filePath)); + + for (i in inputs[keys[0]]) + args.push(pathFunction(inputs[keys[0]][i].filePath)); + + var cmd = new Command(compilers[language], args); + cmd.description = "linking " + output.fileName; + cmd.highlight = "linker"; + cmd.workingDirectory = FileInfo.path(output.filePath); + return cmd; +} diff --git a/share/qbs/modules/cli/mono.qbs b/share/qbs/modules/cli/mono.qbs new file mode 100644 index 00000000..c679c4bf --- /dev/null +++ b/share/qbs/modules/cli/mono.qbs @@ -0,0 +1,27 @@ +import qbs +import qbs.File +import qbs.Probes + +CLIModule { + condition: qbs.toolchain && qbs.toolchain.contains("mono") + + debugInfoSuffix: ".mdb" + csharpCompilerName: "mcs" + vbCompilerName: "vbnc" + fsharpCompilerName: "fsharpc" + + Probes.PathProbe { + id: monoProbe + names: ["mono"] + platformPaths: { + var paths = []; + if (qbs.hostOS.contains("macos")) + paths.push("/Library/Frameworks/Mono.framework/Commands"); + if (qbs.hostOS.contains("unix")) + paths.push("/usr/bin"); + return paths; + } + } + + toolchainInstallPath: monoProbe.path +} diff --git a/share/qbs/modules/cli/windows-dotnet.qbs b/share/qbs/modules/cli/windows-dotnet.qbs new file mode 100644 index 00000000..0ec457bd --- /dev/null +++ b/share/qbs/modules/cli/windows-dotnet.qbs @@ -0,0 +1,29 @@ +import qbs +import qbs.Utilities + +CLIModule { + condition: qbs.toolchain && qbs.toolchain.contains("dotnet") + + debugInfoSuffix: ".pdb" + csharpCompilerName: "csc" + vbCompilerName: "vbc" + fsharpCompilerName: "fsc" + + toolchainInstallPath: Utilities.getNativeSetting(registryKey, "InstallPath") + + // private properties + property string registryKey: { + // TODO: Use a probe after QBS-833 is resolved + // Querying the registry on-demand should be fast enough for now + // https://msdn.microsoft.com/en-us/library/hh925568.aspx + var keys = [ + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP", + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\NET Framework Setup\\NDP" + ]; + for (var i in keys) { + var key = keys[i] + "\\v4\\Full"; + if (Utilities.getNativeSetting(key, "InstallPath")) + return key; + } + } +} diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs new file mode 100644 index 00000000..eab5400d --- /dev/null +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +// base for Cpp modules +import qbs.ModUtils +import qbs.Utilities +import qbs.WindowsUtils + +Module { + condition: false + version: compilerVersion + property string compilerVersion: + [compilerVersionMajor, compilerVersionMinor, compilerVersionPatch].join(".") + property int compilerVersionMajor + property int compilerVersionMinor + property int compilerVersionPatch + property string warningLevel : 'all' // 'none', 'all' + property bool treatWarningsAsErrors : false + property string architecture: qbs.architecture + property string machineType // undocumented + property string imageFormat // undocumented + property string optimization: qbs.optimization + property bool debugInformation: qbs.debugInformation + property bool enableReproducibleBuilds: false + property bool separateDebugInformation: false + property pathList prefixHeaders + property bool useCPrecompiledHeader: false + property bool useCxxPrecompiledHeader: false + property bool useObjcPrecompiledHeader: false + property bool useObjcxxPrecompiledHeader: false + + property stringList defines + property stringList platformDefines: qbs.enableDebugCode ? [] : ["NDEBUG"] + property stringList compilerDefines + PropertyOptions { + name: "compilerDefines" + description: "preprocessor macros that are defined when using this particular compiler" + } + + property string windowsApiCharacterSet + + property string minimumWindowsVersion + PropertyOptions { + name: "minimumWindowsVersion" + description: "A version number in the format [major].[minor] indicating the earliest \ + version of Windows that the product should run on. Defines WINVER, \ + _WIN32_WINNT, and _WIN32_WINDOWS, and applies a version number to the \ + linker flags /SUBSYSTEM and /OSVERSION for MSVC or \ + --major-subsystem-version, --minor-subsystem-version, \ + --major-os-version and --minor-os-version for MinGW. \ + If undefined, compiler defaults will be used." + } + + property string minimumOsxVersion + + property string minimumMacosVersion: minimumOsxVersion + PropertyOptions { + name: "minimumMacosVersion" + description: "a version number in the format [major].[minor] indicating the earliest \ + version of macOS that the product should run on. passes -mmacosx-version-min= \ + to the compiler. if undefined, compiler defaults will be used." + } + + property string minimumIosVersion + PropertyOptions { + name: "minimumIosVersion" + description: "a version number in the format [major].[minor] indicating the earliest \ + version of iOS that the product should run on. passes -miphoneos-version-min= \ + to the compiler. if undefined, compiler defaults will be used." + } + + property string minimumWatchosVersion + PropertyOptions { + name: "minimumWatchosVersion" + description: "a version number in the format [major].[minor] indicating the earliest \ + version of watchOS that the product should run on. if undefined, compiler \ + defaults will be used." + } + + property string minimumTvosVersion + PropertyOptions { + name: "minimumTvosVersion" + description: "a version number in the format [major].[minor] indicating the earliest \ + version of tvOS that the product should run on. if undefined, compiler \ + defaults will be used." + } + + property string minimumAndroidVersion + PropertyOptions { + name: "minimumAndroidVersion" + description: "a version number in the format [major].[minor] indicating the earliest \ + version of Android that the product should run on. this value is converted into an SDK \ + version which is then written to AndroidManifest.xml." + } + + property string maximumAndroidVersion + PropertyOptions { + name: "maximumAndroidVersion" + description: "a version number in the format [major].[minor] indicating the latest \ + version of Android that the product should run on. this value is converted into an SDK \ + version which is then written to AndroidManifest.xml. if undefined, no upper limit will \ + be set." + } + + property pathList includePaths + property pathList systemIncludePaths + property pathList compilerIncludePaths + property pathList libraryPaths + property pathList compilerLibraryPaths + property pathList frameworkPaths + property pathList systemFrameworkPaths + property pathList compilerFrameworkPaths + property stringList systemRunPaths: [] + + property string assemblerName + property string assemblerPath: assemblerName + property string compilerName + property string compilerPath: compilerName + property var compilerPathByLanguage + property stringList compilerWrapper + property string linkerName + property string linkerPath: linkerName + property stringList linkerWrapper + property string staticLibraryPrefix + property string dynamicLibraryPrefix + property string loadableModulePrefix + property string executablePrefix + property string staticLibrarySuffix + property string dynamicLibrarySuffix + property string loadableModuleSuffix + property string executableSuffix + property string debugInfoSuffix + property string debugInfoBundleSuffix + property bool createSymlinks: true + property stringList dynamicLibraries // list of names, will be linked with -lname + property stringList staticLibraries // list of static library files + property stringList frameworks // list of frameworks, will be linked with '-framework ' + property stringList weakFrameworks // list of weakly-linked frameworks, will be linked with '-weak_framework ' + property stringList rpaths + property string sonamePrefix + property bool useRPaths: true + + property stringList assemblerFlags + PropertyOptions { + name: "assemblerFlags" + description: "additional flags for the assembler" + } + + property stringList cppFlags + PropertyOptions { + name: "cppFlags" + description: "additional flags for the C preprocessor" + } + + property stringList cFlags + PropertyOptions { + name: "cFlags" + description: "additional flags for the C compiler" + } + + property stringList cxxFlags + PropertyOptions { + name: "cxxFlags" + description: "additional flags for the C++ compiler" + } + + property stringList objcFlags + PropertyOptions { + name: "objcFlags" + description: "additional flags for the Objective-C compiler" + } + + property stringList objcxxFlags + PropertyOptions { + name: "objcxxFlags" + description: "additional flags for the Objective-C++ compiler" + } + property stringList commonCompilerFlags + PropertyOptions { + name: "commonCompilerFlags" + description: "flags added to all compilation independently of the language" + } + + property stringList linkerFlags + PropertyOptions { + name: "linkerFlags" + description: "additional linker flags" + } + + property stringList driverFlags + PropertyOptions { + name: "driverFlags" + description: "additional compiler driver flags" + } + + property bool positionIndependentCode + PropertyOptions { + name: "positionIndependentCode" + description: "generate position independent code" + } + + property string entryPoint + PropertyOptions { + name: "entryPoint" + description: "entry point symbol for an executable or dynamic library" + } + + property string runtimeLibrary + PropertyOptions { + name: "runtimeLibrary" + description: "determine which runtime library to use" + allowedValues: ['static', 'dynamic'] + } + + property string visibility: 'default' + PropertyOptions { + name: "visibility" + description: "export symbols visibility level" + allowedValues: ['default', 'hidden', 'hiddenInlines', 'minimal'] + } + + property string cLanguageVersion + PropertyOptions { + name: "cLanguageVersion" + allowedValues: ["c89", "c99", "c11"] + description: "The version of the C standard with which the code must comply." + } + + property string cxxLanguageVersion + PropertyOptions { + name: "cxxLanguageVersion" + allowedValues: ["c++98", "c++11", "c++14"] + description: "The version of the C++ standard with which the code must comply." + } + + property string cxxStandardLibrary + PropertyOptions { + name: "cxxStandardLibrary" + allowedValues: ["libstdc++", "libc++"] + description: "version of the C++ standard library to use" + } + + property bool enableExceptions: true + PropertyOptions { + name: "enableExceptions" + description: "enable/disable exception handling (enabled by default)" + } + + property string exceptionHandlingModel: "default" + PropertyOptions { + name: "exceptionHandlingModel" + description: "the kind of exception handling to be used by the compiler" + } + + property bool enableRtti + + // Platform properties. Those are intended to be set by the toolchain setup + // and are prepended to the corresponding user properties. + property stringList platformAssemblerFlags + property stringList platformCommonCompilerFlags + property stringList platformCFlags + property stringList platformCxxFlags + property stringList platformObjcFlags + property stringList platformObjcxxFlags + property stringList platformLinkerFlags + property stringList platformDriverFlags + + // Apple platforms properties + property bool automaticReferenceCounting + PropertyOptions { + name: "automaticReferenceCounting" + description: "whether to enable Automatic Reference Counting (ARC) for Objective-C" + } + + property bool requireAppExtensionSafeApi + PropertyOptions { + name: "requireAppExtensionSafeApi" + description: "whether to require app-extension-safe APIs only" + } + + property bool allowUnresolvedSymbols: false + + FileTagger { + patterns: ["*.c"] + fileTags: ["c"] + } + + FileTagger { + patterns: ["*.C", "*.cpp", "*.cxx", "*.c++", "*.cc"] + fileTags: ["cpp"] + } + + FileTagger { + patterns: ["*.m"] + fileTags: ["objc"] + } + + FileTagger { + patterns: ["*.mm"] + fileTags: ["objcpp"] + } + + FileTagger { + patterns: ["*.h", "*.H", "*.hpp", "*.hxx", "*.h++"] + fileTags: ["hpp"] + } + + validate: { + var validator = new ModUtils.PropertyValidator("cpp"); + validator.setRequiredProperty("architecture", architecture, + "you might want to re-run 'qbs-setup-toolchains'"); + validator.addCustomValidator("architecture", architecture, function (value) { + return !architecture || architecture === Utilities.canonicalArchitecture(architecture); + }, "'" + architecture + "' is invalid. You must use the canonical name '" + + Utilities.canonicalArchitecture(architecture) + "'"); + validator.setRequiredProperty("compilerVersion", compilerVersion); + validator.setRequiredProperty("compilerVersionMajor", compilerVersionMajor); + validator.setRequiredProperty("compilerVersionMinor", compilerVersionMinor); + validator.setRequiredProperty("compilerVersionPatch", compilerVersionPatch); + validator.addVersionValidator("compilerVersion", compilerVersion, 3, 3); + validator.addRangeValidator("compilerVersionMajor", compilerVersionMajor, 1); + validator.addRangeValidator("compilerVersionMinor", compilerVersionMinor, 0); + validator.addRangeValidator("compilerVersionPatch", compilerVersionPatch, 0); + if (minimumWindowsVersion) { + validator.addVersionValidator("minimumWindowsVersion", minimumWindowsVersion, 2, 2); + validator.addCustomValidator("minimumWindowsVersion", minimumWindowsVersion, function (v) { + return !v || v === WindowsUtils.canonicalizeVersion(v); + }, "'" + minimumWindowsVersion + "' is invalid. Did you mean '" + + WindowsUtils.canonicalizeVersion(minimumWindowsVersion) + "'?"); + } + validator.validate(); + + if (minimumWindowsVersion && !WindowsUtils.isValidWindowsVersion(minimumWindowsVersion)) { + console.warn("Unknown Windows version '" + minimumWindowsVersion + + "'; expected one of: " + + WindowsUtils.knownWindowsVersions().map(function (a) { + return '"' + a + '"'; }).join(", ") + + ". See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832.aspx"); + } + } + + setupRunEnvironment: { + var env = qbs.commonRunEnvironment; + for (var i in env) { + var v = new ModUtils.EnvironmentVariable(i, qbs.pathListSeparator, + qbs.hostOS.contains("windows")); + v.value = env[i]; + v.set(); + } + } +} diff --git a/share/qbs/modules/cpp/DarwinGCC.qbs b/share/qbs/modules/cpp/DarwinGCC.qbs new file mode 100644 index 00000000..89a89cb4 --- /dev/null +++ b/share/qbs/modules/cpp/DarwinGCC.qbs @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.DarwinTools +import qbs.FileInfo +import qbs.ModUtils +import qbs.PropertyList +import qbs.TextFile + +UnixGCC { + condition: false + + Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.contains("xcode") } + + targetVendor: "apple" + targetSystem: "darwin" + targetAbi: "macho" + imageFormat: "macho" + + compilerDefines: ["__GNUC__=4", "__APPLE__"] + cxxStandardLibrary: qbs.toolchain.contains("clang") && cxxLanguageVersion !== "c++98" + ? "libc++" : base + loadableModulePrefix: "" + loadableModuleSuffix: ".bundle" + dynamicLibrarySuffix: ".dylib" + separateDebugInformation: true + debugInfoBundleSuffix: ".dSYM" + debugInfoSuffix: ".dwarf" + + toolchainInstallPath: xcode.present + ? FileInfo.joinPaths(xcode.toolchainPath, "usr", "bin") : base + sysroot: xcode.present ? xcode.sdkPath : base + + setupBuildEnvironment: { + for (var key in buildEnv) { + v = new ModUtils.EnvironmentVariable(key); + v.value = buildEnv[key]; + v.set(); + } + } + + property var defaultInfoPlist: { + var dict = {}; + + if (qbs.targetOS.contains("macos")) { + dict["NSPrincipalClass"] = "NSApplication"; // needed for Retina display support + + if (minimumMacosVersion) + dict["LSMinimumSystemVersion"] = minimumMacosVersion; + } + + if (qbs.targetOS.containsAny(["ios", "tvos"])) { + dict["LSRequiresIPhoneOS"] = true; + + if (xcode.platformType === "device") { + if (qbs.targetOS.contains("ios")) + dict["UIRequiredDeviceCapabilities"] = ["armv7"]; + + if (qbs.targetOS.contains("tvos")) + dict["UIRequiredDeviceCapabilities"] = ["arm64"]; + } + } + + if (xcode.present) { + var targetDevices = DarwinTools.targetedDeviceFamily(xcode.targetDevices); + if (qbs.targetOS.contains("ios")) + dict["UIDeviceFamily"] = targetDevices; + + if (qbs.targetOS.containsAny(["ios", "watchos"])) { + var orientations = [ + "UIInterfaceOrientationPortrait", + "UIInterfaceOrientationPortraitUpsideDown", + "UIInterfaceOrientationLandscapeLeft", + "UIInterfaceOrientationLandscapeRight" + ]; + + if (targetDevices.contains("ipad")) + dict["UISupportedInterfaceOrientations~ipad"] = orientations; + + if (targetDevices.contains("watch")) + dict["UISupportedInterfaceOrientations"] = orientations.slice(0, 2); + + if (targetDevices.contains("iphone")) { + orientations.splice(1, 1); + dict["UISupportedInterfaceOrientations"] = orientations; + } + } + } + + return dict; + } + + // private properties + readonly property var buildEnv: { + var env = { + "ARCHS_STANDARD": targetArch, // TODO: this will be affected by multi-arch support + "EXECUTABLE_NAME": product.targetName, + "LANG": "en_US.US-ASCII", + "PRODUCT_NAME": product.name + } + + // Set the corresponding environment variable even if the minimum OS version is undefined, + // because this indicates the default deployment target for that OS + if (qbs.targetOS.contains("ios")) + env["IPHONEOS_DEPLOYMENT_TARGET"] = minimumIosVersion || ""; + if (qbs.targetOS.contains("macos")) + env["MACOSX_DEPLOYMENT_TARGET"] = minimumMacosVersion || ""; + if (qbs.targetOS.contains("watchos")) + env["WATCHOS_DEPLOYMENT_TARGET"] = minimumWatchosVersion || ""; + if (qbs.targetOS.contains("tvos")) + env["TVOS_DEPLOYMENT_TARGET"] = minimumTvosVersion || ""; + + if (xcode.present) + env["TARGETED_DEVICE_FAMILY"] = DarwinTools.targetedDeviceFamily(xcode.targetDevices); + return env; + } + + property string minimumDarwinVersion + property string minimumDarwinVersionCompilerFlag + property string minimumDarwinVersionLinkerFlag + + Rule { + condition: qbs.targetOS.contains("darwin") + inputs: ["qbs"] + + Artifact { + filePath: product.name + "-cpp-Info.plist" + fileTags: ["partial_infoplist"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.inputData = ModUtils.moduleProperty(product, "defaultInfoPlist"); + cmd.outputFilePath = output.filePath; + cmd.sourceCode = function() { + var plist = new PropertyList(); + try { + plist.readFromObject(inputData); + plist.writeToFile(outputFilePath, "xml1"); + } finally { + plist.clear(); + } + }; + return [cmd]; + } + } +} diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs new file mode 100644 index 00000000..edf1a3fb --- /dev/null +++ b/share/qbs/modules/cpp/GenericGCC.qbs @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.PathTools +import qbs.Probes +import qbs.Process +import qbs.Utilities +import qbs.UnixUtils +import qbs.WindowsUtils +import 'gcc.js' as Gcc + +CppModule { + condition: false + + Probes.GccProbe { + id: gccProbe + compilerFilePath: compilerPath + preferredArchitecture: targetArch + preferredMachineType: machineType + } + + qbs.architecture: gccProbe.found ? gccProbe.architecture : original + + compilerVersionMajor: gccProbe.versionMajor + compilerVersionMinor: gccProbe.versionMinor + compilerVersionPatch: gccProbe.versionPatch + + compilerIncludePaths: gccProbe.includePaths + compilerFrameworkPaths: gccProbe.frameworkPaths + compilerLibraryPaths: gccProbe.libraryPaths + + property string target: [targetArch, targetVendor, targetSystem, targetAbi].join("-") + property string targetArch: qbs.architecture === "x86" ? "i386" : qbs.architecture + property string targetVendor: "unknown" + property string targetSystem: "unknown" + property string targetAbi: "unknown" + + property string toolchainPrefix + property path toolchainInstallPath + assemblerName: 'as' + compilerName: cxxCompilerName + linkerName: 'ld' + property string archiverName: 'ar' + property string nmName: 'nm' + property string objcopyName: "objcopy" + property string stripName: "strip" + property string dsymutilName: "dsymutil" + property path sysroot: qbs.sysroot + + property string linkerMode: "automatic" + PropertyOptions { + name: "linkerMode" + allowedValues: ["automatic", "manual"] + description: "Controls whether to automatically use an appropriate compiler frontend " + + "in place of the system linker when linking binaries. The default is \"automatic\", " + + "which chooses either the C++ compiler, C compiler, or system linker specified by " + + "the linkerName/linkerPath properties, depending on the type of object files " + + "present on the linker command line. \"manual\" allows you to explicitly specify " + + "the linker using the linkerName/linkerPath properties, and allows linker flags " + + "passed to the linkerFlags and platformLinkerFlags properties to be escaped " + + "manually (using -Wl or -Xlinker) instead of automatically based on the selected " + + "linker." + } + + property string exportedSymbolsCheckMode: "ignore-undefined" + PropertyOptions { + name: "exportedSymbolsCheckMode" + allowedValues: ["strict", "ignore-undefined"] + description: "Controls when we consider an updated dynamic library as changed with " + + "regards to other binaries depending on it. The default is \"ignore-undefined\", " + + "which means we do not care about undefined symbols being added or removed. " + + "If you do care about that, e.g. because you link dependent products with an option " + + "such as \"--no-undefined\", then you should set this property to \"strict\"." + } + + property string toolchainPathPrefix: { + var path = '' + if (toolchainInstallPath) { + path += toolchainInstallPath + if (path.substr(-1) !== '/') + path += '/' + } + if (toolchainPrefix) + path += toolchainPrefix + return path + } + + property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : "" + property string cCompilerName: (qbs.toolchain.contains("clang") ? "clang" : "gcc") + + compilerExtension + property string cxxCompilerName: (qbs.toolchain.contains("clang") ? "clang++" : "g++") + + compilerExtension + + compilerPathByLanguage: ({ + "c": toolchainPathPrefix + cCompilerName, + "cpp": toolchainPathPrefix + cxxCompilerName, + "objc": toolchainPathPrefix + cCompilerName, + "objcpp": toolchainPathPrefix + cxxCompilerName, + "asm_cpp": toolchainPathPrefix + cCompilerName + }) + + assemblerPath: toolchainPathPrefix + assemblerName + compilerPath: toolchainPathPrefix + compilerName + linkerPath: toolchainPathPrefix + linkerName + property string archiverPath: toolchainPathPrefix + archiverName + property string nmPath: toolchainPathPrefix + nmName + property string objcopyPath: toolchainPathPrefix + objcopyName + property string stripPath: toolchainPathPrefix + stripName + property string dsymutilPath: toolchainPathPrefix + dsymutilName + property stringList dsymutilFlags + + readonly property bool shouldCreateSymlinks: { + return createSymlinks && internalVersion && ["macho", "elf"].contains(cpp.imageFormat); + } + + readonly property string internalVersion: { + if (product.version === undefined) + return undefined; + + if (!Gcc.isNumericProductVersion(product.version)) { + // Dynamic library version numbers like "A" or "B" are common on Apple platforms, so + // don't restrict the product version to a componentized version number here. + if (cpp.imageFormat === "macho") + return product.version; + + throw("product.version must be a string in the format x[.y[.z[.w]] " + + "where each component is an integer"); + } + + var maxVersionParts = 3; + var versionParts = product.version.split('.').slice(0, maxVersionParts); + + // pad if necessary + for (var i = versionParts.length; i < maxVersionParts; ++i) + versionParts.push("0"); + + return versionParts.join('.'); + } + property string soVersion: { + var v = internalVersion; + if (!Gcc.isNumericProductVersion(v)) + return ""; + return v.split('.')[0]; + } + + exceptionHandlingModel: { + if (qbs.toolchain.contains("mingw")) { + // https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html claims + // __USING_SJLJ_EXCEPTIONS__ is defined as 1 when using SJLJ exceptions, but there don't + // seem to be defines for the other models, so use the presence of the DLLs for now. + var prefix = toolchainInstallPath; + if (!qbs.hostOS.contains("windows")) + prefix = FileInfo.joinPaths(toolchainInstallPath, "..", "lib", "gcc", + toolchainPrefix, + [compilerVersionMajor, compilerVersionMinor].join(".")); + var models = ["seh", "sjlj", "dw2"]; + for (var i = 0; i < models.length; ++i) { + if (File.exists(FileInfo.joinPaths(prefix, "libgcc_s_" + models[i] + "-1.dll"))) { + return models[i]; + } + } + } + return base; + } + + validate: { + var validator = new ModUtils.PropertyValidator("cpp"); + validator.setRequiredProperty("architecture", architecture, + "you might want to re-run 'qbs-setup-toolchains'"); + if (gccProbe.architecture) { + validator.addCustomValidator("architecture", architecture, function (value) { + return Utilities.canonicalArchitecture(architecture) === Utilities.canonicalArchitecture(gccProbe.architecture); + }, "'" + architecture + "' differs from the architecture produced by this compiler (" + + gccProbe.architecture +")"); + } else { + // This is a warning and not an error on the rare chance some new architecture comes + // about which qbs does not know about the macros of. But it *might* still work. + if (architecture) + console.warn("Unknown architecture '" + architecture + "' " + + "may not be supported by this compiler."); + } + + var validateFlagsFunction = function (value) { + if (value) { + for (var i = 0; i < value.length; ++i) { + if (["-target", "-triple", "-arch"].contains(value[i]) + || value[i].startsWith("-march=")) + return false; + } + } + return true; + } + + var msg = "'-target', '-triple', '-arch' and '-march' cannot appear in flags; set qbs.architecture instead"; + validator.addCustomValidator("assemblerFlags", assemblerFlags, validateFlagsFunction, msg); + validator.addCustomValidator("cppFlags", cppFlags, validateFlagsFunction, msg); + validator.addCustomValidator("cFlags", cFlags, validateFlagsFunction, msg); + validator.addCustomValidator("cxxFlags", cxxFlags, validateFlagsFunction, msg); + validator.addCustomValidator("objcFlags", objcFlags, validateFlagsFunction, msg); + validator.addCustomValidator("objcxxFlags", objcxxFlags, validateFlagsFunction, msg); + validator.addCustomValidator("commonCompilerFlags", commonCompilerFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformAssemblerFlags", platformAssemblerFlags, validateFlagsFunction, msg); + //validator.addCustomValidator("platformCppFlags", platformCppFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformCFlags", platformCFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformCxxFlags", platformCxxFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformObjcFlags", platformObjcFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformObjcxxFlags", platformObjcxxFlags, validateFlagsFunction, msg); + validator.addCustomValidator("platformCommonCompilerFlags", platformCommonCompilerFlags, validateFlagsFunction, msg); + + validator.setRequiredProperty("compilerVersion", compilerVersion); + validator.setRequiredProperty("compilerVersionMajor", compilerVersionMajor); + validator.setRequiredProperty("compilerVersionMinor", compilerVersionMinor); + validator.setRequiredProperty("compilerVersionPatch", compilerVersionPatch); + validator.addVersionValidator("compilerVersion", compilerVersion, 3, 3); + validator.addRangeValidator("compilerVersionMajor", compilerVersionMajor, 1); + validator.addRangeValidator("compilerVersionMinor", compilerVersionMinor, 0); + validator.addRangeValidator("compilerVersionPatch", compilerVersionPatch, 0); + + validator.setRequiredProperty("compilerIncludePaths", compilerIncludePaths); + validator.setRequiredProperty("compilerFrameworkPaths", compilerFrameworkPaths); + validator.setRequiredProperty("compilerLibraryPaths", compilerLibraryPaths); + + validator.validate(); + } + + Rule { + id: dynamicLibraryLinker + multiplex: true + inputs: { + var tags = ["obj", "linkerscript", "versionscript"]; + if (product.type.contains("dynamiclibrary") && + product.moduleProperty("qbs", "targetOS").contains("darwin") && + product.moduleProperty("bundle", "embedInfoPlist")) + tags.push("aggregate_infoplist"); + return tags; + } + inputsFromDependencies: ["dynamiclibrary_copy", "staticlibrary"] + + outputFileTags: ["dynamiclibrary", "dynamiclibrary_symlink", "dynamiclibrary_copy", "debuginfo"] + outputArtifacts: { + var lib = { + filePath: product.destinationDirectory + "/" + + PathTools.dynamicLibraryFilePath(product), + fileTags: ["dynamiclibrary"] + }; + var libCopy = { + // Copy of libfoo for smart re-linking. + filePath: product.destinationDirectory + "/.socopy/" + + PathTools.dynamicLibraryFilePath(product), + fileTags: ["dynamiclibrary_copy"], + alwaysUpdated: false, + }; + var artifacts = [lib, libCopy]; + + if (ModUtils.moduleProperty(product, "shouldCreateSymlinks") && !product.moduleProperty("bundle", "isBundle")) { + var maxVersionParts = Gcc.isNumericProductVersion(product.version) ? 3 : 1; + for (var i = 0; i < maxVersionParts; ++i) { + var symlink = { + filePath: product.destinationDirectory + "/" + + PathTools.dynamicLibraryFileName(product, undefined, i), + fileTags: ["dynamiclibrary_symlink"] + }; + if (i > 0 && artifacts[i-1].filePath == symlink.filePath) + break; // Version number has less than three components. + artifacts.push(symlink); + } + } + return artifacts.concat(Gcc.debugInfoArtifacts(product)); + } + + prepare: { + return Gcc.prepareLinker.apply(this, arguments); + } + } + + Rule { + id: staticLibraryLinker + multiplex: true + inputs: ["obj", "linkerscript"] + inputsFromDependencies: ["dynamiclibrary", "staticlibrary"] + + outputFileTags: ["staticlibrary", "c_staticlibrary", "cpp_staticlibrary"] + outputArtifacts: { + var tags = ["staticlibrary"]; + for (var i = 0; i < inputs["obj"].length; ++i) { + var ft = inputs["obj"][i].fileTags; + if (ft.contains("c_obj")) + tags.push("c_staticlibrary"); + if (ft.contains("cpp_obj")) + tags.push("cpp_staticlibrary"); + } + + var staticLibraries = function() { + var result = []; + for (var i in inputs.staticlibrary) { + var lib = inputs.staticlibrary[i] + result = Gcc.concatLibs(result, [lib.filePath].concat( + ModUtils.moduleProperty(lib, 'staticLibraries'))); + } + result = Gcc.concatLibs(result, + ModUtils.moduleProperty(product, 'staticLibraries')); + return result + }(); + + var dynamicLibraries = function() { + var result = []; + for (var i in inputs.dynamiclibrary) + result.push(inputs.dynamiclibrary[i].filePath); + result = result.concat(ModUtils.moduleProperty(product, 'dynamicLibraries')); + return result + }(); + + return [{ + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.staticLibraryFilePath(product)), + fileTags: tags, + cpp: { "staticLibraries": staticLibraries, "dynamicLibraries": dynamicLibraries } + }]; + } + + prepare: { + var args = ['rcs', output.filePath]; + for (var i in inputs.obj) + args.push(inputs.obj[i].filePath); + var cmd = new Command(ModUtils.moduleProperty(product, "archiverPath"), args); + cmd.description = 'creating ' + output.fileName; + cmd.highlight = 'linker' + cmd.responseFileUsagePrefix = '@'; + return cmd; + } + } + + Rule { + id: loadableModuleLinker + multiplex: true + inputs: { + var tags = ["obj", "linkerscript"]; + if (product.type.contains("loadablemodule") && + product.moduleProperty("qbs", "targetOS").contains("darwin") && + product.moduleProperty("bundle", "embedInfoPlist")) + tags.push("aggregate_infoplist"); + return tags; + } + inputsFromDependencies: ["dynamiclibrary_copy", "staticlibrary"] + + outputFileTags: ["loadablemodule", "debuginfo"] + outputArtifacts: { + var app = { + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.loadableModuleFilePath(product)), + fileTags: ["loadablemodule"] + } + return [app].concat(Gcc.debugInfoArtifacts(product)); + } + + prepare: { + return Gcc.prepareLinker.apply(this, arguments); + } + } + + Rule { + id: applicationLinker + multiplex: true + inputs: { + var tags = ["obj", "linkerscript"]; + if (product.type.contains("application") && + product.moduleProperty("qbs", "targetOS").contains("darwin") && + product.moduleProperty("bundle", "embedInfoPlist")) + tags.push("aggregate_infoplist"); + return tags; + } + inputsFromDependencies: ["dynamiclibrary_copy", "staticlibrary"] + + outputFileTags: ["application", "debuginfo"] + outputArtifacts: { + var app = { + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.applicationFilePath(product)), + fileTags: ["application"] + } + return [app].concat(Gcc.debugInfoArtifacts(product)); + } + + prepare: { + return Gcc.prepareLinker.apply(this, arguments); + } + } + + Rule { + id: compiler + inputs: ["cpp", "c", "objcpp", "objc", "asm_cpp"] + auxiliaryInputs: ["hpp"] + explicitlyDependsOn: ["c_pch", "cpp_pch", "objc_pch", "objcpp_pch"] + + outputFileTags: ["obj", "c_obj", "cpp_obj"] + outputArtifacts: { + var tags = ["obj"]; + if (inputs.c || inputs.objc) + tags.push("c_obj"); + if (inputs.cpp || inputs.objcpp) + tags.push("cpp_obj"); + return [{ + fileTags: tags, + filePath: FileInfo.joinPaths(".obj", + Utilities.getHash(input.baseDir), + input.fileName + ".o") + }]; + } + + prepare: { + return Gcc.prepareCompiler.apply(this, arguments); + } + } + + Rule { + id: assembler + inputs: ["asm"] + + Artifact { + fileTags: ["obj"] + filePath: FileInfo.joinPaths(".obj", Utilities.getHash(input.baseDir), input.fileName + ".o") + } + + prepare: { + return Gcc.prepareAssembler.apply(this, arguments); + } + } + + Rule { + condition: useCPrecompiledHeader + inputs: ["c_pch_src"] + auxiliaryInputs: ["hpp"] + Artifact { + filePath: product.name + "_c.gch" + fileTags: ["c_pch"] + } + prepare: { + return Gcc.prepareCompiler.apply(this, arguments); + } + } + + Rule { + condition: useCxxPrecompiledHeader + inputs: ["cpp_pch_src"] + auxiliaryInputs: ["hpp"] + Artifact { + filePath: product.name + "_cpp.gch" + fileTags: ["cpp_pch"] + } + prepare: { + return Gcc.prepareCompiler.apply(this, arguments); + } + } + + Rule { + condition: useObjcPrecompiledHeader + inputs: ["objc_pch_src"] + auxiliaryInputs: ["hpp"] + Artifact { + filePath: product.name + "_objc.gch" + fileTags: ["objc_pch"] + } + prepare: { + return Gcc.prepareCompiler.apply(this, arguments); + } + } + + Rule { + condition: useObjcxxPrecompiledHeader + inputs: ["objcpp_pch_src"] + auxiliaryInputs: ["hpp"] + Artifact { + filePath: product.name + "_objcpp.gch" + fileTags: ["objcpp_pch"] + } + prepare: { + return Gcc.prepareCompiler.apply(this, arguments); + } + } + + FileTagger { + patterns: "*.s" + fileTags: ["asm"] + } + + FileTagger { + patterns: "*.S" + fileTags: ["asm_cpp"] + } + + FileTagger { + patterns: "*.sx" + fileTags: ["asm_cpp"] + } +} diff --git a/share/qbs/modules/cpp/LinuxGCC.qbs b/share/qbs/modules/cpp/LinuxGCC.qbs new file mode 100644 index 00000000..a2544556 --- /dev/null +++ b/share/qbs/modules/cpp/LinuxGCC.qbs @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.Process + +UnixGCC { + condition: qbs.targetOS.contains('linux') && !qbs.targetOS.contains("android") && + qbs.toolchain && qbs.toolchain.contains('gcc') + + targetVendor: "pc" + targetSystem: "linux" + targetAbi: "gnu" + + Probe { + id: runPathsProbe + condition: qbs.targetOS.length === qbs.hostOS.length + && qbs.targetOS.every(function(v, i) { return v === qbs.hostOS[i]; }) + property stringList systemRunPaths: [] + configure: { + var paths = []; + var ldconfig = new Process(); + try { + var success = ldconfig.exec("ldconfig", ["-vNX"]); + if (success === -1) + return; + var line; + do { + line = ldconfig.readLine(); + if (line.charAt(0) === '/') + paths.push(line.slice(0, line.length - 1)); + } while (line && line.length > 0) + found = true; + systemRunPaths = paths; + } finally { + ldconfig.close(); + } + } + } + + systemRunPaths: runPathsProbe.found ? runPathsProbe.systemRunPaths : base +} diff --git a/share/qbs/modules/cpp/UnixGCC.qbs b/share/qbs/modules/cpp/UnixGCC.qbs new file mode 100644 index 00000000..8e11a401 --- /dev/null +++ b/share/qbs/modules/cpp/UnixGCC.qbs @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 + +GenericGCC { + condition: false + staticLibraryPrefix: "lib" + dynamicLibraryPrefix: "lib" + loadableModulePrefix: "lib" + executablePrefix: "" + staticLibrarySuffix: ".a" + dynamicLibrarySuffix: ".so" + loadableModuleSuffix: "" + executableSuffix: "" + debugInfoSuffix: ".debug" + imageFormat: "elf" + systemRunPaths: ["/lib", "/usr/lib"] +} + diff --git a/share/qbs/modules/cpp/android-gcc.qbs b/share/qbs/modules/cpp/android-gcc.qbs new file mode 100644 index 00000000..34ef322b --- /dev/null +++ b/share/qbs/modules/cpp/android-gcc.qbs @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.TextFile +import "../../modules/Android/ndk/utils.js" as NdkUtils + +LinuxGCC { + Depends { name: "Android.ndk" } + + condition: qbs.targetOS.contains("android") && + qbs.toolchain && qbs.toolchain.contains("gcc") + rpaths: ['$ORIGIN'] + + property string toolchainDir: { + if (qbs.toolchain && qbs.toolchain.contains("clang")) + return "llvm-" + Android.ndk.toolchainVersionNumber; + if (["x86", "x86_64"].contains(Android.ndk.abi)) + return Android.ndk.abi + "-" + Android.ndk.toolchainVersionNumber; + return toolchainPrefix + Android.ndk.toolchainVersionNumber; + } + + property string cxxStlBaseDir: FileInfo.joinPaths(Android.ndk.ndkDir, "sources", "cxx-stl") + property string gabiBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "gabi++") + property string stlPortBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "stlport") + property string gnuStlBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "gnu-libstdc++", + Android.ndk.toolchainVersionNumber) + property string llvmStlBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "llvm-libc++") + property string stlBaseDir: { + if (Android.ndk.appStl.startsWith("gabi++_")) + return gabiBaseDir; + else if (Android.ndk.appStl.startsWith("stlport_")) + return stlPortBaseDir; + else if (Android.ndk.appStl.startsWith("gnustl_")) + return gnuStlBaseDir; + else if (Android.ndk.appStl.startsWith("c++_")) + return llvmStlBaseDir; + return undefined; + } + + property string stlLibsDir: { + if (stlBaseDir) { + var infix = Android.ndk.abi; + if (Android.ndk.armMode === "thumb") + infix = FileInfo.joinPaths(infix, "thumb"); + return FileInfo.joinPaths(stlBaseDir, "libs", infix); + } + return undefined; + } + + property string sharedStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_shared")) + ? FileInfo.joinPaths(stlLibsDir, dynamicLibraryPrefix + Android.ndk.appStl + dynamicLibrarySuffix) + : undefined + property string staticStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_static")) + ? FileInfo.joinPaths(stlLibsDir, staticLibraryPrefix + Android.ndk.appStl + staticLibrarySuffix) + : undefined + + toolchainInstallPath: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", + toolchainDir, "prebuilt", + Android.ndk.hostArch, "bin") + + toolchainPrefix: { + if (qbs.toolchain && qbs.toolchain.contains("clang")) + return undefined; + return [targetAbi === "androideabi" ? "arm" : targetArch, + targetSystem, targetAbi].join("-") + "-"; + } + + machineType: { + if (Android.ndk.abi === "armeabi") + return "armv5te"; + if (Android.ndk.abi === "armeabi-v7a") + return "armv7-a"; + } + + qbs.optimization: targetAbi === "androideabi" ? "small" : base + + enableExceptions: Android.ndk.appStl !== "system" + enableRtti: Android.ndk.appStl !== "system" + + commonCompilerFlags: NdkUtils.commonCompilerFlags(qbs.buildVariant, Android.ndk.abi, + Android.ndk.armMode) + + linkerFlags: NdkUtils.commonLinkerFlags(Android.ndk.abi) + + platformDriverFlags: ["-no-canonical-prefixes"] + + libraryPaths: { + var prefix = FileInfo.joinPaths(sysroot, "usr"); + var paths = []; + if (Android.ndk.abi === "mips64" || Android.ndk.abi === "x86_64") // no lib64 for arm64-v8a + paths.push(FileInfo.joinPaths(prefix, "lib64")); + paths.push(FileInfo.joinPaths(prefix, "lib")); + return paths; + } + + dynamicLibraries: { + var libs = ["c", "m"]; + if (sharedStlFilePath) + libs.push(sharedStlFilePath); + return libs; + } + staticLibraries: { + var libs = ["gcc"]; + if (staticStlFilePath) + libs.push(staticStlFilePath); + return libs; + } + systemIncludePaths: { + var includes = []; + if (Android.ndk.appStl === "system") { + includes.push(FileInfo.joinPaths(cxxStlBaseDir, "system", "include")); + } else if (Android.ndk.appStl.startsWith("gabi++")) { + includes.push(FileInfo.joinPaths(gabiBaseDir, "include")); + } else if (Android.ndk.appStl.startsWith("stlport")) { + includes.push(FileInfo.joinPaths(stlPortBaseDir, "stlport")); + } else if (Android.ndk.appStl.startsWith("gnustl")) { + includes.push(FileInfo.joinPaths(gnuStlBaseDir, "include")); + includes.push(FileInfo.joinPaths(gnuStlBaseDir, "libs", Android.ndk.abi, "include")); + includes.push(FileInfo.joinPaths(gnuStlBaseDir, "include", "backward")); + } else if (Android.ndk.appStl.startsWith("c++_")) { + includes.push(FileInfo.joinPaths(llvmStlBaseDir, "libcxx", "include")); + includes.push(FileInfo.joinPaths(llvmStlBaseDir + "abi", "libcxxabi", "include")); + } + return includes; + } + defines: ["ANDROID"] + sysroot: FileInfo.joinPaths(Android.ndk.ndkDir, "platforms", Android.ndk.platform, + "arch-" + NdkUtils.abiNameToDirName(Android.ndk.abi)) + + targetArch: { + switch (qbs.architecture) { + case "arm64": + return "aarch64"; + case "armv5": + case "armv5te": + return "armv5te"; + case "armv7a": + case "x86_64": + return qbs.architecture; + case "x86": + return "i686"; + case "mips": + case "mipsel": + return "mipsel"; + case "mips64": + case "mips64el": + return "mips64el"; + } + } + + targetVendor: "none" + targetSystem: "linux" + targetAbi: "android" + (["armeabi", "armeabi-v7a"].contains(Android.ndk.abi) ? "eabi" : "") + + Rule { + inputs: ["dynamiclibrary"] + outputFileTags: ["android.nativelibrary", "android.gdbserver-info", "android.stl-info"] + outputArtifacts: { + var artifacts = [{ + filePath: FileInfo.joinPaths("stripped-libs", + inputs["dynamiclibrary"][0].fileName), + fileTags: ["android.nativelibrary"] + }]; + if (product.moduleProperty("qbs", "buildVariant") === "debug") { + artifacts.push({ + filePath: "android.gdbserver-info.txt", + fileTags: ["android.gdbserver-info"] + }); + } + var stlFilePath = product.moduleProperty("cpp", "sharedStlFilePath"); + if (stlFilePath) + artifacts.push({filePath: "android.stl-info.txt", fileTags: ["android.stl-info"]}); + return artifacts; + } + + prepare: { + var stlFilePath = product.moduleProperty("cpp", "sharedStlFilePath"); + var copyCmd = new JavaScriptCommand(); + copyCmd.silent = true; + copyCmd.stlFilePath = stlFilePath; + copyCmd.sourceCode = function() { + File.copy(inputs["dynamiclibrary"][0].filePath, + outputs["android.nativelibrary"][0].filePath); + var arch = product.moduleProperty("Android.ndk", "abi"); + var destDir = FileInfo.joinPaths("lib", arch); + if (product.moduleProperty("qbs", "buildVariant") === "debug") { + arch = NdkUtils.abiNameToDirName(arch); + var srcPath = FileInfo.joinPaths( + product.moduleProperty("Android.ndk", "ndkDir"), + "prebuilt/android-" + arch, "gdbserver/gdbserver"); + var targetPath = FileInfo.joinPaths(destDir, + product.moduleProperty("Android.ndk", "gdbserverFileName")); + var infoFile = new TextFile(outputs["android.gdbserver-info"][0].filePath, + TextFile.WriteOnly); + infoFile.writeLine(srcPath); + infoFile.writeLine(targetPath); + infoFile.close(); + } + if (stlFilePath) { + var srcPath = stlFilePath; + var targetPath = FileInfo.joinPaths(destDir, FileInfo.fileName(srcPath)); + var infoFile = new TextFile(outputs["android.stl-info"][0].filePath, + TextFile.WriteOnly); + infoFile.writeLine(srcPath); + infoFile.writeLine(targetPath); + infoFile.close(); + } + } + var stripArgs = ["--strip-unneeded", outputs["android.nativelibrary"][0].filePath]; + if (stlFilePath) + stripArgs.push(stlFilePath); + var stripCmd = new Command(product.moduleProperty("cpp", "stripPath"), stripArgs); + stripCmd.description = "Stripping unneeded symbols from " + + outputs["android.nativelibrary"][0].fileName; + return [copyCmd, stripCmd]; + } + } + + validate: { + var baseValidator = new ModUtils.PropertyValidator("qbs"); + baseValidator.addCustomValidator("architecture", targetArch, function (value) { + return value !== undefined; + }, "unknown Android architecture '" + qbs.architecture + "'."); + + var validator = new ModUtils.PropertyValidator("cpp"); + validator.setRequiredProperty("targetArch", targetArch); + + return baseValidator.validate() && validator.validate(); + } +} diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js new file mode 100644 index 00000000..fed290b1 --- /dev/null +++ b/share/qbs/modules/cpp/gcc.js @@ -0,0 +1,1016 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var File = loadExtension("qbs.File"); +var FileInfo = loadExtension("qbs.FileInfo"); +var DarwinTools = loadExtension("qbs.DarwinTools"); +var ModUtils = loadExtension("qbs.ModUtils"); +var PathTools = loadExtension("qbs.PathTools"); +var Process = loadExtension("qbs.Process"); +var UnixUtils = loadExtension("qbs.UnixUtils"); +var Utilities = loadExtension("qbs.Utilities"); +var WindowsUtils = loadExtension("qbs.WindowsUtils"); + +function effectiveLinkerPath(product, inputs) { + if (product.moduleProperty("cpp", "linkerMode") === "automatic") { + var compilers = ModUtils.moduleProperty(product, "compilerPathByLanguage"); + if (compilers) { + if (inputs.cpp_obj || inputs.cpp_staticlibrary) { + console.log("Found C++ or Objective-C++ objects, choosing C++ linker for " + + product.name); + return compilers["cpp"]; + } + + if (inputs.c_obj || inputs.c_staticlibrary) { + console.log("Found C or Objective-C objects, choosing C linker for " + + product.name); + return compilers["c"]; + } + } + + console.log("Found no C-language objects, choosing system linker for " + + product.name); + } + + return ModUtils.moduleProperty(product, "linkerPath"); +} + +function useCompilerDriverLinker(product, inputs) { + var linker = effectiveLinkerPath(product, inputs); + var compilers = product.moduleProperty("cpp", "compilerPathByLanguage"); + if (compilers) { + return linker === compilers["cpp"] + || linker === compilers["c"]; + } + return linker === product.moduleProperty("cpp", "compilerPath"); +} + +function escapeLinkerFlags(product, inputs, linkerFlags, allowEscape) { + if (allowEscape === undefined) + allowEscape = true; + + if (!linkerFlags || linkerFlags.length === 0) + return []; + + if (useCompilerDriverLinker(product, inputs) && allowEscape) { + var sep = ","; + var useXlinker = linkerFlags.some(function (f) { return f.contains(sep); }); + if (useXlinker) { + // One or more linker arguments contain the separator character itself + // Use -Xlinker to handle these + var xlinkerFlags = []; + linkerFlags.map(function (linkerFlag) { + xlinkerFlags.push("-Xlinker", linkerFlag); + }); + return xlinkerFlags; + } + + // If no linker arguments contain the separator character we can just use -Wl, + // which is more compact and easier to read in logs + return [["-Wl"].concat(linkerFlags).join(sep)]; + } + + return linkerFlags; +} + +function linkerFlags(project, product, inputs, output) { + var libraryPaths = ModUtils.moduleProperty(product, 'libraryPaths'); + var dynamicLibraries = ModUtils.moduleProperty(product, "dynamicLibraries"); + var staticLibraries = ModUtils.modulePropertiesFromArtifacts(product, inputs.staticlibrary, 'cpp', 'staticLibraries'); + var frameworks = ModUtils.moduleProperty(product, 'frameworks'); + var weakFrameworks = ModUtils.moduleProperty(product, 'weakFrameworks'); + var rpaths = (product.moduleProperty("cpp", "useRPaths") !== false) + ? ModUtils.moduleProperty(product, 'rpaths') : undefined; + var systemRunPaths = product.moduleProperty("cpp", "systemRunPaths") || []; + var isDarwin = product.moduleProperty("qbs", "targetOS").contains("darwin"); + var i, args = additionalCompilerAndLinkerFlags(product); + + if (output.fileTags.contains("dynamiclibrary")) { + args.push(isDarwin ? "-dynamiclib" : "-shared"); + + if (isDarwin) { + var internalVersion = product.moduleProperty("cpp", "internalVersion"); + if (internalVersion && isNumericProductVersion(internalVersion)) + args.push("-current_version", internalVersion); + + args = args.concat(escapeLinkerFlags(product, inputs, [ + "-install_name", + UnixUtils.soname(product, output.fileName)])); + } else { + args = args.concat(escapeLinkerFlags(product, inputs, [ + "-soname=" + + UnixUtils.soname(product, output.fileName)])); + } + } + + if (output.fileTags.contains("loadablemodule")) + args.push(isDarwin ? "-bundle" : "-shared"); + + if (output.fileTags.containsAny(["dynamiclibrary", "loadablemodule"])) { + if (isDarwin) + args = args.concat(escapeLinkerFlags(product, inputs, + ["-headerpad_max_install_names"])); + else + args = args.concat(escapeLinkerFlags(product, inputs, + ["--as-needed"])); + } + + var minimumDarwinVersion = ModUtils.moduleProperty(product, "minimumDarwinVersion"); + if (minimumDarwinVersion) { + var flag = ModUtils.moduleProperty(product, "minimumDarwinVersionLinkerFlag"); + if (flag) + args = args.concat(escapeLinkerFlags(product, inputs, [flag, minimumDarwinVersion])); + } + + var sysroot = ModUtils.moduleProperty(product, "sysroot"); + if (sysroot) { + if (isDarwin) + args = args.concat(escapeLinkerFlags(product, inputs, ["-syslibroot", sysroot])); + else + args.push("--sysroot=" + sysroot); // do not escape, compiler-as-linker also needs it + } + + var unresolvedSymbolsAction = isDarwin ? "error" : "ignore-in-shared-libs"; + if (ModUtils.moduleProperty(product, "allowUnresolvedSymbols")) + unresolvedSymbolsAction = isDarwin ? "suppress" : "ignore-all"; + args = args.concat(escapeLinkerFlags(product, inputs, isDarwin + ? ["-undefined", unresolvedSymbolsAction] + : ["--unresolved-symbols=" + unresolvedSymbolsAction])); + + for (i in rpaths) { + if (systemRunPaths.indexOf(rpaths[i]) === -1) + args = args.concat(escapeLinkerFlags(product, inputs, ["-rpath", rpaths[i]])); + } + + if (product.moduleProperty("cpp", "entryPoint")) + args = args.concat(escapeLinkerFlags(product, inputs, + ["-e", product.moduleProperty("cpp", "entryPoint")])); + + if (product.moduleProperty("qbs", "toolchain").contains("mingw")) { + if (product.consoleApplication !== undefined) + args = args.concat(escapeLinkerFlags(product, inputs, [ + "-subsystem", + product.consoleApplication + ? "console" + : "windows"])); + + var minimumWindowsVersion = ModUtils.moduleProperty(product, "minimumWindowsVersion"); + if (minimumWindowsVersion) { + var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'subsystem'); + if (subsystemVersion) { + var major = subsystemVersion.split('.')[0]; + var minor = subsystemVersion.split('.')[1]; + + // http://sourceware.org/binutils/docs/ld/Options.html + args = args.concat(escapeLinkerFlags(product, inputs, + ["--major-subsystem-version", major])); + args = args.concat(escapeLinkerFlags(product, inputs, + ["--minor-subsystem-version", minor])); + args = args.concat(escapeLinkerFlags(product, inputs, + ["--major-os-version", major])); + args = args.concat(escapeLinkerFlags(product, inputs, + ["--minor-os-version", minor])); + } + } + } + + if (inputs.aggregate_infoplist) + args.push("-sectcreate", "__TEXT", "__info_plist", inputs.aggregate_infoplist[0].filePath); + + var isLinkingCppObjects = !!(inputs.cpp_obj || inputs.cpp_staticlibrary); + var stdlib = isLinkingCppObjects + ? product.moduleProperty("cpp", "cxxStandardLibrary") + : undefined; + if (stdlib && product.moduleProperty("qbs", "toolchain").contains("clang")) + args.push("-stdlib=" + stdlib); + + // Flags for library search paths + if (libraryPaths) + args = args.concat([].uniqueConcat(libraryPaths).map(function(path) { return '-L' + path })); + + var linkerScripts = inputs.linkerscript + ? inputs.linkerscript.map(function(a) { return a.filePath; }) : []; + args = args.concat(escapeLinkerFlags(product, inputs, [].uniqueConcat(linkerScripts) + .map(function(path) { return '-T' + path }))); + + var versionScripts = inputs.versionscript + ? inputs.versionscript.map(function(a) { return a.filePath; }) : []; + args = args.concat(escapeLinkerFlags(product, inputs, [].uniqueConcat(versionScripts) + .map(function(path) { return '--version-script=' + path }))); + + if (isDarwin && ModUtils.moduleProperty(product, "warningLevel") === "none") + args.push('-w'); + + var allowEscape = !ModUtils.checkCompatibilityMode(project, "1.6", + "Enabling linker flags compatibility mode. cpp.linkerFlags and " + + "cpp.platformLinkerFlags escaping is handled automatically beginning in Qbs 1.6. " + + "When upgrading to Qbs 1.6, you should only pass raw linker flags to these " + + "properties; do not escape them using -Wl or -Xlinker. This allows Qbs to " + + "automatically supply the correct linker flags regardless of whether the " + + "linker chosen is the compiler driver or system linker (see the documentation for " + + "cpp.linkerMode for more information)."); + + args = args.concat(configFlags(product, useCompilerDriverLinker(product, inputs))); + args = args.concat(escapeLinkerFlags( + product, inputs, + ModUtils.moduleProperty(product, 'platformLinkerFlags'), allowEscape)); + args = args.concat(escapeLinkerFlags( + product, inputs, + ModUtils.moduleProperty(product, 'linkerFlags'), allowEscape)); + + args.push("-o", output.filePath); + + if (inputs.obj) + args = args.concat(inputs.obj.map(function (obj) { return obj.filePath })); + + // Add filenames of internal library dependencies to the lists + var staticLibsFromInputs = inputs.staticlibrary + ? inputs.staticlibrary.map(function(a) { return a.filePath; }) : []; + staticLibraries = concatLibsFromArtifacts(staticLibraries, inputs.staticlibrary); + var dynamicLibsFromInputs = inputs.dynamiclibrary_copy + ? inputs.dynamiclibrary_copy.map(function(a) { return a.filePath; }) : []; + dynamicLibraries = concatLibsFromArtifacts(dynamicLibraries, inputs.dynamiclibrary_copy); + + for (i in frameworks) { + frameworkExecutablePath = PathTools.frameworkExecutablePath(frameworks[i]); + if (File.exists(frameworkExecutablePath)) + args.push(frameworkExecutablePath); + else + args = args.concat(['-framework', frameworks[i]]); + } + + for (i in weakFrameworks) { + frameworkExecutablePath = PathTools.frameworkExecutablePath(weakFrameworks[i]); + if (File.exists(frameworkExecutablePath)) + args = args.concat(['-weak_library', frameworkExecutablePath]); + else + args = args.concat(['-weak_framework', weakFrameworks[i]]); + } + + for (i in staticLibraries) { + if (staticLibsFromInputs.contains(staticLibraries[i]) || File.exists(staticLibraries[i])) { + args.push(staticLibraries[i]); + } else { + args.push('-l' + staticLibraries[i]); + } + } + + for (i in dynamicLibraries) { + if (dynamicLibsFromInputs.contains(dynamicLibraries[i]) + || File.exists(dynamicLibraries[i])) { + args.push(dynamicLibraries[i]); + } else { + args.push('-l' + dynamicLibraries[i]); + } + } + + return args; +} + +// for compiler AND linker +function configFlags(config, isDriver) { + if (isDriver === undefined) + isDriver = true; + + var args = []; + + if (isDriver) { + args = args.concat(ModUtils.moduleProperty(config, 'platformDriverFlags')); + args = args.concat(ModUtils.moduleProperty(config, 'driverFlags')); + } + + if (haveTargetOption(config) && isDriver) { + args.push("-target", config.moduleProperty("cpp", "target")); + } else { + var arch = config.moduleProperty("cpp", "targetArch"); + if (config.moduleProperty("qbs", "targetOS").contains("darwin")) + args.push("-arch", arch); + + if (isDriver) { + if (arch === 'x86_64') + args.push('-m64'); + else if (arch === 'i386') + args.push('-m32'); + + var march = config.moduleProperty("cpp", "machineType"); + if (march) + args.push("-march=" + march); + + var minimumDarwinVersion = ModUtils.moduleProperty(config, "minimumDarwinVersion"); + if (minimumDarwinVersion) { + var flag = ModUtils.moduleProperty(config, "minimumDarwinVersionCompilerFlag"); + if (flag) + args.push(flag + "=" + minimumDarwinVersion); + } + } + } + + var frameworkPaths = ModUtils.moduleProperty(config, 'frameworkPaths'); + if (frameworkPaths) + args = args.concat(frameworkPaths.map(function(path) { return '-F' + path })); + + var systemFrameworkPaths = ModUtils.moduleProperty(config, 'systemFrameworkPaths'); + if (systemFrameworkPaths) + args = args.concat(systemFrameworkPaths.map(function(path) { return '-iframework' + path })); + + return args; +} + +function languageTagFromFileExtension(toolchain, fileName) { + var i = fileName.lastIndexOf('.'); + if (i === -1) + return; + var m = { + "c" : "c", + "C" : "cpp", + "cpp" : "cpp", + "cxx" : "cpp", + "c++" : "cpp", + "cc" : "cpp", + "m" : "objc", + "mm" : "objcpp", + "s" : "asm", + "S" : "asm_cpp" + }; + if (!toolchain.contains("clang")) + m["sx"] = "asm_cpp"; // clang does NOT recognize .sx + return m[fileName.substring(i + 1)]; +} + +function effectiveCompilerInfo(toolchain, input, output) { + var compilerPath, language; + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(output.fileTags)); + + // Whether we're compiling a precompiled header or normal source file + var pchOutput = output.fileTags.contains(tag + "_pch"); + + var compilerPathByLanguage = ModUtils.moduleProperty(input, "compilerPathByLanguage"); + if (compilerPathByLanguage) + compilerPath = compilerPathByLanguage[tag]; + if (!compilerPath || tag !== languageTagFromFileExtension(toolchain, input.fileName)) + language = languageName(tag) + (pchOutput ? '-header' : ''); + if (!compilerPath) + // fall back to main compiler + compilerPath = ModUtils.moduleProperty(input, "compilerPath"); + return { + path: compilerPath, + language: language, + tag: tag + }; +} + +// ### what we actually need here is something like product.usedFileTags +// that contains all fileTags that have been used when applying the rules. +function compilerFlags(product, input, output) { + var includePaths = ModUtils.moduleProperty(input, 'includePaths'); + var systemIncludePaths = ModUtils.moduleProperty(input, 'systemIncludePaths'); + + var platformDefines = ModUtils.moduleProperty(input, 'platformDefines'); + var defines = ModUtils.moduleProperty(input, 'defines'); + + var EffectiveTypeEnum = { UNKNOWN: 0, LIB: 1, APP: 2 }; + var effectiveType = EffectiveTypeEnum.UNKNOWN; + var libTypes = {staticlibrary : 1, dynamiclibrary : 1}; + var appTypes = {application : 1}; + var i; + for (i = product.type.length; --i >= 0;) { + if (libTypes.hasOwnProperty(product.type[i]) !== -1) { + effectiveType = EffectiveTypeEnum.LIB; + break; + } else if (appTypes.hasOwnProperty(product.type[i]) !== -1) { + effectiveType = EffectiveTypeEnum.APP; + break; + } + } + + // Determine which C-language we're compiling + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(output.fileTags)); + if (!["c", "cpp", "objc", "objcpp", "asm_cpp"].contains(tag)) + throw ("unsupported source language: " + tag); + + var compilerInfo = effectiveCompilerInfo(product.moduleProperty("qbs", "toolchain"), + input, output); + + var args = additionalCompilerAndLinkerFlags(product); + + var sysroot = ModUtils.moduleProperty(product, "sysroot"); + if (sysroot) { + if (product.moduleProperty("qbs", "targetOS").contains("darwin")) + args.push("-isysroot", sysroot); + else + args.push("--sysroot=" + sysroot); + } + + if (ModUtils.moduleProperty(input, "debugInformation")) + args.push('-g'); + var opt = ModUtils.moduleProperty(input, "optimization") + if (opt === 'fast') + args.push('-O2'); + if (opt === 'small') + args.push('-Os'); + if (opt === 'none') + args.push('-O0'); + + var warnings = ModUtils.moduleProperty(input, "warningLevel") + if (warnings === 'none') + args.push('-w'); + if (warnings === 'all') { + args.push('-Wall'); + args.push('-Wextra'); + } + if (ModUtils.moduleProperty(input, "treatWarningsAsErrors")) + args.push('-Werror'); + + args = args.concat(configFlags(input)); + args.push('-pipe'); + + if (ModUtils.moduleProperty(input, "enableReproducibleBuilds")) { + var toolchain = product.moduleProperty("qbs", "toolchain"); + if (!toolchain.contains("clang")) { + var hashString = FileInfo.relativePath(project.sourceDirectory, input.filePath); + var hash = Utilities.getHash(hashString); + args.push("-frandom-seed=0x" + hash.substring(0, 8)); + } + + var major = product.moduleProperty("cpp", "compilerVersionMajor"); + var minor = product.moduleProperty("cpp", "compilerVersionMinor"); + if ((toolchain.contains("clang") && (major > 3 || (major === 3 && minor >= 5))) || + (toolchain.contains("gcc") && (major > 4 || (major === 4 && minor >= 9)))) { + args.push("-Wdate-time"); + } + } + + var useArc = ModUtils.moduleProperty(input, "automaticReferenceCounting"); + if (useArc !== undefined && (tag === "objc" || tag === "objcpp")) { + args.push(useArc ? "-fobjc-arc" : "-fno-objc-arc"); + } + + var enableExceptions = ModUtils.moduleProperty(input, "enableExceptions"); + if (enableExceptions !== undefined) { + if (tag === "cpp" || tag === "objcpp") + args.push(enableExceptions ? "-fexceptions" : "-fno-exceptions"); + + if (tag === "objc" || tag === "objcpp") { + args.push(enableExceptions ? "-fobjc-exceptions" : "-fno-objc-exceptions"); + if (useArc !== undefined) + args.push(useArc ? "-fobjc-arc-exceptions" : "-fno-objc-arc-exceptions"); + } + } + + var enableRtti = ModUtils.moduleProperty(input, "enableRtti"); + if (enableRtti !== undefined && (tag === "cpp" || tag === "objcpp")) { + args.push(enableRtti ? "-frtti" : "-fno-rtti"); + } + + var visibility = ModUtils.moduleProperty(input, 'visibility'); + if (!product.type.contains('staticlibrary') + && !product.moduleProperty("qbs", "toolchain").contains("mingw")) { + if (visibility === 'hidden' || visibility === 'minimal') + args.push('-fvisibility=hidden'); + if ((visibility === 'hiddenInlines' || visibility === 'minimal') && tag === 'cpp') + args.push('-fvisibility-inlines-hidden'); + if (visibility === 'default') + args.push('-fvisibility=default') + } + + var prefixHeaders = ModUtils.moduleProperty(input, "prefixHeaders"); + for (i in prefixHeaders) { + args.push('-include'); + args.push(prefixHeaders[i]); + } + + if (compilerInfo.language) + // Only push '-x language' if we have to. + args.push("-x", compilerInfo.language); + + args = args.concat(ModUtils.moduleProperty(input, 'platformFlags'), + ModUtils.moduleProperty(input, 'flags'), + ModUtils.moduleProperty(input, 'platformFlags', tag), + ModUtils.moduleProperty(input, 'flags', tag)); + + var pchOutput = output.fileTags.contains(compilerInfo.tag + "_pch"); + + if (!pchOutput && ModUtils.moduleProperty(input, 'usePrecompiledHeader', tag)) { + var pchFilePath = FileInfo.joinPaths(product.buildDirectory, product.name + "_" + tag); + args.push('-include', pchFilePath); + } + + var positionIndependentCode = input.moduleProperty('cpp', 'positionIndependentCode') + if (effectiveType === EffectiveTypeEnum.LIB) { + if (positionIndependentCode !== false && !product.moduleProperty("qbs", "toolchain").contains("mingw")) + args.push('-fPIC'); + } else if (effectiveType === EffectiveTypeEnum.APP) { + if (positionIndependentCode && !product.moduleProperty("qbs", "toolchain").contains("mingw")) + args.push('-fPIE'); + } else { + throw ("The product's type must be in " + JSON.stringify( + Object.getOwnPropertyNames(libTypes).concat(Object.getOwnPropertyNames(appTypes))) + + ". But it is " + JSON.stringify(product.type) + '.'); + } + var cppFlags = ModUtils.moduleProperty(input, 'cppFlags'); + for (i in cppFlags) + args.push('-Wp,' + cppFlags[i]) + + var allDefines = []; + if (platformDefines) + allDefines = allDefines.uniqueConcat(platformDefines); + if (defines) + allDefines = allDefines.uniqueConcat(defines); + args = args.concat(allDefines.map(function(define) { return '-D' + define })); + if (includePaths) + args = args.concat([].uniqueConcat(includePaths).map(function(path) { return '-I' + path })); + if (systemIncludePaths) + args = args.concat([].uniqueConcat(systemIncludePaths).map(function(path) { return '-isystem' + path })); + + var minimumWindowsVersion = ModUtils.moduleProperty(input, "minimumWindowsVersion"); + if (minimumWindowsVersion && product.moduleProperty("qbs", "targetOS").contains("windows")) { + var hexVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'hex'); + if (hexVersion) { + var versionDefs = [ 'WINVER', '_WIN32_WINNT', '_WIN32_WINDOWS' ]; + for (i in versionDefs) + args.push('-D' + versionDefs[i] + '=' + hexVersion); + } + } + + if (tag === "c" || tag === "objc") { + var cVersion = ModUtils.moduleProperty(input, "cLanguageVersion"); + if (cVersion) { + var gccCVersionsMap = { + "c11": "c1x" // Deprecated, but compatible with older gcc versions. + }; + args.push("-std=" + (gccCVersionsMap[cVersion] || cVersion)); + } + } + + if (tag === "cpp" || tag === "objcpp") { + var cxxVersion = ModUtils.moduleProperty(input, "cxxLanguageVersion"); + if (cxxVersion) { + var gccCxxVersionsMap = { + "c++11": "c++0x", // Deprecated, but compatible with older gcc versions. + "c++14": "c++1y" + }; + args.push("-std=" + (gccCxxVersionsMap[cxxVersion] || cxxVersion)); + } + + var cxxStandardLibrary = product.moduleProperty("cpp", "cxxStandardLibrary"); + if (cxxStandardLibrary && product.moduleProperty("qbs", "toolchain").contains("clang")) { + args.push("-stdlib=" + cxxStandardLibrary); + } + } + + args.push("-o", output.filePath); + args.push("-c", input.filePath); + + return args; +} + +function haveTargetOption(product) { + var toolchain = product.moduleProperty("qbs", "toolchain"); + var major = product.moduleProperty("cpp", "compilerVersionMajor"); + var minor = product.moduleProperty("cpp", "compilerVersionMinor"); + + // Apple Clang 3.1 (shipped with Xcode 4.3) just happened to also correspond to LLVM 3.1, + // so no special version check is needed for Apple + return toolchain.contains("clang") && (major > 3 || (major === 3 && minor >= 1)); +} + +function additionalCompilerAndLinkerFlags(product) { + var args = [] + + var requireAppExtensionSafeApi = ModUtils.moduleProperty(product, "requireAppExtensionSafeApi"); + if (requireAppExtensionSafeApi !== undefined && product.moduleProperty("qbs", "targetOS").contains("darwin")) { + args.push(requireAppExtensionSafeApi ? "-fapplication-extension" : "-fno-application-extension"); + } + + return args +} + +// Returns the GCC language name equivalent to fileTag, accepted by the -x argument +function languageName(fileTag) { + if (fileTag === 'c') + return 'c'; + else if (fileTag === 'cpp') + return 'c++'; + else if (fileTag === 'objc') + return 'objective-c'; + else if (fileTag === 'objcpp') + return 'objective-c++'; + else if (fileTag === 'asm') + return 'assembler'; + else if (fileTag === 'asm_cpp') + return 'assembler-with-cpp'; +} + +function prepareAssembler(project, product, inputs, outputs, input, output) { + var assemblerPath = ModUtils.moduleProperty(product, "assemblerPath"); + + var includePaths = ModUtils.moduleProperty(input, 'includePaths'); + var systemIncludePaths = ModUtils.moduleProperty(input, 'systemIncludePaths'); + + var args = []; + var arch = product.moduleProperty("cpp", "targetArch"); + if (product.moduleProperty("qbs", "targetOS").contains("darwin")) + args.push("-arch", arch); + else if (arch === 'x86_64') + args.push('--64'); + else if (arch === 'i386') + args.push('--32'); + + if (ModUtils.moduleProperty(input, "debugInformation")) + args.push('-g'); + + var warnings = ModUtils.moduleProperty(input, "warningLevel") + if (warnings === 'none') + args.push('-W'); + + var tag = "asm"; + if (tag !== languageTagFromFileExtension(product.moduleProperty("qbs", "toolchain"), + input.fileName)) + // Only push '-x language' if we have to. + args.push("-x", languageName(tag)); + + args = args.concat(ModUtils.moduleProperty(input, 'platformFlags', tag), + ModUtils.moduleProperty(input, 'flags', tag)); + + var allIncludePaths = []; + if (includePaths) + allIncludePaths = allIncludePaths.uniqueConcat(includePaths); + if (systemIncludePaths) + allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); + args = args.concat(allIncludePaths.map(function(path) { return '-I' + path })); + + args.push("-o", output.filePath); + args.push(input.filePath); + + var cmd = new Command(assemblerPath, args); + cmd.description = "assembling " + input.fileName; + cmd.highlight = "compiler"; + return cmd; +} + +function prepareCompiler(project, product, inputs, outputs, input, output) { + var compilerInfo = effectiveCompilerInfo(product.moduleProperty("qbs", "toolchain"), + input, output); + var compilerPath = compilerInfo.path; + var pchOutput = output.fileTags.contains(compilerInfo.tag + "_pch"); + + var args = compilerFlags(product, input, output); + var wrapperArgsLength = 0; + var wrapperArgs = ModUtils.moduleProperty(product, "compilerWrapper"); + if (wrapperArgs && wrapperArgs.length > 0) { + wrapperArgsLength = wrapperArgs.length; + args.unshift(compilerPath); + compilerPath = wrapperArgs.shift(); + args = wrapperArgs.concat(args); + } + + var cmd = new Command(compilerPath, args); + cmd.description = (pchOutput ? 'pre' : '') + 'compiling ' + input.fileName; + if (pchOutput) + cmd.description += ' (' + compilerInfo.tag + ')'; + cmd.highlight = "compiler"; + cmd.responseFileArgumentIndex = wrapperArgsLength; + cmd.responseFileUsagePrefix = '@'; + return cmd; +} + +// Concatenates two arrays of library names and preserves the dependency order that ld needs. +function concatLibs(libs, deplibs) { + var r = []; + var s = {}; + + function addLibs(lst) { + for (var i = lst.length; --i >= 0;) { + var lib = lst[i]; + if (!s[lib]) { + s[lib] = true; + r.unshift(lib); + } + } + } + + addLibs(deplibs); + addLibs(libs); + return r; +} + +function prepareLinker(project, product, inputs, outputs, input, output) { + var i, primaryOutput, cmd, commands = []; + + if (outputs.application) { + primaryOutput = outputs.application[0]; + } else if (outputs.dynamiclibrary) { + primaryOutput = outputs.dynamiclibrary[0]; + } else if (outputs.loadablemodule) { + primaryOutput = outputs.loadablemodule[0]; + } + + var linkerPath = effectiveLinkerPath(product, inputs) + + var args = linkerFlags(project, product, inputs, primaryOutput) + var wrapperArgsLength = 0; + var wrapperArgs = ModUtils.moduleProperty(product, "linkerWrapper"); + if (wrapperArgs && wrapperArgs.length > 0) { + wrapperArgsLength = wrapperArgs.length; + args.unshift(linkerPath); + linkerPath = wrapperArgs.shift(); + args = wrapperArgs.concat(args); + } + + cmd = new Command(linkerPath, args); + cmd.description = 'linking ' + primaryOutput.fileName; + cmd.highlight = 'linker'; + cmd.responseFileArgumentIndex = wrapperArgsLength; + cmd.responseFileUsagePrefix = '@'; + commands.push(cmd); + + if (outputs.debuginfo) { + if (product.moduleProperty("qbs", "targetOS").contains("darwin")) { + var dsymPath = outputs.debuginfo[0].filePath; + if (outputs.debuginfo_bundle && outputs.debuginfo_bundle[0]) + dsymPath = outputs.debuginfo_bundle[0].filePath; + var flags = ModUtils.moduleProperty(product, "dsymutilFlags") || []; + cmd = new Command(ModUtils.moduleProperty(product, "dsymutilPath"), flags.concat([ + "-o", dsymPath, primaryOutput.filePath + ])); + cmd.description = "generating dSYM for " + product.name; + commands.push(cmd); + + cmd = new Command(ModUtils.moduleProperty(product, "stripPath"), + ["-S", primaryOutput.filePath]); + cmd.silent = true; + commands.push(cmd); + } else { + var objcopy = ModUtils.moduleProperty(product, "objcopyPath"); + + cmd = new Command(objcopy, ["--only-keep-debug", primaryOutput.filePath, + outputs.debuginfo[0].filePath]); + cmd.silent = true; + commands.push(cmd); + + cmd = new Command(objcopy, ["--strip-debug", primaryOutput.filePath]); + cmd.silent = true; + commands.push(cmd); + + cmd = new Command(objcopy, ["--add-gnu-debuglink=" + outputs.debuginfo[0].filePath, + primaryOutput.filePath]); + cmd.silent = true; + commands.push(cmd); + } + } + + if (outputs.dynamiclibrary) { + // Update the copy, if any global symbols have changed. + cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + var sourceFilePath = outputs.dynamiclibrary[0].filePath; + var targetFilePath = outputs.dynamiclibrary_copy[0].filePath; + if (!File.exists(targetFilePath)) { + File.copy(sourceFilePath, targetFilePath); + return; + } + if (product.moduleProperty("qbs", "toolchain").contains("mingw")) { + // mingw's nm tool does not work correctly. + File.copy(sourceFilePath, targetFilePath); + return; + } + var process = new Process(); + var command = ModUtils.moduleProperty(product, "nmPath"); + var args = ["-g", "-D", "-P"]; + if (process.exec(command, args.concat(sourceFilePath), false) !== 0) { + // Failure to run the nm tool is not fatal. We just fall back to the + // "always relink" behavior. + File.copy(sourceFilePath, targetFilePath); + return; + } + var globalSymbolsSource = process.readStdOut(); + if (process.exec(command, args.concat(targetFilePath), false) !== 0) { + File.copy(sourceFilePath, targetFilePath); + return; + } + var globalSymbolsTarget = process.readStdOut(); + + var globalSymbolsSourceLines = globalSymbolsSource.split('\n'); + var globalSymbolsTargetLines = globalSymbolsTarget.split('\n'); + if (globalSymbolsSourceLines.length !== globalSymbolsTargetLines.length) { + var checkMode = ModUtils.moduleProperty(product, "exportedSymbolsCheckMode"); + if (checkMode === "strict") { + File.copy(sourceFilePath, targetFilePath); + return; + } + + // Collect undefined symbols and remove them from the symbol lists. + // GNU nm has the "--defined" option for this purpose, but POSIX nm does not. + args.push("-u"); + if (process.exec(command, args.concat(sourceFilePath), false) !== 0) { + File.copy(sourceFilePath, targetFilePath); + return; + } + var undefinedSymbolsSource = process.readStdOut(); + if (process.exec(command, args.concat(targetFilePath), false) !== 0) { + File.copy(sourceFilePath, targetFilePath); + return; + } + var undefinedSymbolsTarget = process.readStdOut(); + process.close(); + + var undefinedSymbolsSourceLines = undefinedSymbolsSource.split('\n'); + var undefinedSymbolsTargetLines = undefinedSymbolsTarget.split('\n'); + + globalSymbolsSourceLines = globalSymbolsSourceLines.filter(function(line) { + return !undefinedSymbolsSourceLines.contains(line); }); + globalSymbolsTargetLines = globalSymbolsTargetLines.filter(function(line) { + return !undefinedSymbolsTargetLines.contains(line); }); + if (globalSymbolsSourceLines.length !== globalSymbolsTargetLines.length) { + File.copy(sourceFilePath, targetFilePath); + return; + } + } + + while (globalSymbolsSourceLines.length > 0) { + var sourceLine = globalSymbolsSourceLines.shift(); + var targetLine = globalSymbolsTargetLines.shift(); + var sourceLineElems = sourceLine.split(/\s+/); + var targetLineElems = targetLine.split(/\s+/); + if (sourceLineElems[0] !== targetLineElems[0] // Object name. + || sourceLineElems[1] !== targetLineElems[1]) { // Object type + File.copy(sourceFilePath, targetFilePath); + return; + } + } + } + commands.push(cmd); + + // Create symlinks from {libfoo, libfoo.1, libfoo.1.0} to libfoo.1.0.0 + var links = outputs["dynamiclibrary_symlink"]; + var symlinkCount = links ? links.length : 0; + for (i = 0; i < symlinkCount; ++i) { + cmd = new Command("ln", ["-sf", primaryOutput.fileName, + links[i].filePath]); + cmd.highlight = "filegen"; + cmd.description = "creating symbolic link '" + + links[i].fileName + "'"; + cmd.workingDirectory = FileInfo.path(primaryOutput.filePath); + commands.push(cmd); + } + } + + var actualSigningIdentity = product.moduleProperty("xcode", "actualSigningIdentity"); + var codesignDisplayName = product.moduleProperty("xcode", "actualSigningIdentityDisplayName"); + if (actualSigningIdentity && !product.moduleProperty("bundle", "isBundle")) { + var args = product.moduleProperty("xcode", "codesignFlags") || []; + args.push("--force"); + args.push("--sign", actualSigningIdentity); + args = args.concat(DarwinTools._codeSignTimestampFlags(product)); + + for (var j in inputs.xcent) { + args.push("--entitlements", inputs.xcent[j].filePath); + break; // there should only be one + } + args.push(primaryOutput.filePath); + cmd = new Command(product.moduleProperty("xcode", "codesignPath"), args); + cmd.description = "codesign " + + primaryOutput.fileName + + " using " + codesignDisplayName + + " (" + actualSigningIdentity + ")"; + commands.push(cmd); + } + + return commands; +} + +function concatLibsFromArtifacts(libs, artifacts) +{ + if (!artifacts) + return libs; + var deps = artifacts.map(function (a) { return a.filePath; }); + deps.reverse(); + return concatLibs(deps, libs); +} + +function debugInfoArtifacts(product) { + var artifacts = []; + if (product.moduleProperty("cpp", "separateDebugInformation")) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.debugInfoFilePath(product)), + fileTags: ["debuginfo"] + }); + if (PathTools.debugInfoIsBundle(product)) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.debugInfoBundlePath(product)), + fileTags: ["debuginfo_bundle"] + }); + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, PathTools.debugInfoPlistFilePath(product)), + fileTags: ["debuginfo_plist"] + }); + } + } + return artifacts; +} + +function isNumericProductVersion(version) { + return version && version.match(/^([0-9]+\.){0,3}[0-9]+$/); +} + +function dumpMacros(compilerFilePath, args, nullDevice) { + var p = new Process(); + try { + p.setEnv("LC_ALL", "C"); + p.exec(compilerFilePath, (args || []).concat(["-dM", "-E", "-x", "c", nullDevice])); + var map = {}; + p.readStdOut().trim().split("\n").map(function (line) { + var parts = line.split(" ", 3); + map[parts[1]] = parts[2]; + }); + return map; + } finally { + p.close(); + } +} + +function dumpDefaultPaths(compilerFilePath, args, nullDevice, pathListSeparator, targetOS, + sysroot) { + var p = new Process(); + try { + p.setEnv("LC_ALL", "C"); + args = args || []; + if (sysroot) { + if (targetOS.contains("darwin")) + args.push("-isysroot", sysroot); + else + args.push("--sysroot=" + sysroot); + } + p.exec(compilerFilePath, args.concat(["-v", "-E", "-x", "c++", nullDevice])); + var suffix = " (framework directory)"; + var includePaths = []; + var libraryPaths = []; + var frameworkPaths = []; + var addIncludes = false; + var lines = p.readStdErr().trim().split("\n").map(function (line) { return line.trim(); }); + for (var i = 0; i < lines.length; ++i) { + var line = lines[i]; + var prefix = "LIBRARY_PATH="; + if (line.startsWith(prefix)) { + libraryPaths = libraryPaths.concat(line.substr(prefix.length) + .split(pathListSeparator)); + } else if (line === "#include <...> search starts here:") { + addIncludes = true; + } else if (line === "End of search list.") { + addIncludes = false; + } else if (addIncludes) { + if (line.endsWith(suffix)) + frameworkPaths.push(line.substr(0, line.length - suffix.length)); + else + includePaths.push(line); + } + } + + sysroot = sysroot || ""; + + if (includePaths.length === 0) + includePaths.push(sysroot + "/usr/include", sysroot + "/usr/local/include"); + + if (libraryPaths.length === 0) + libraryPaths.push(sysroot + "/lib", sysroot + "/usr/lib"); + + if (frameworkPaths.length === 0) + frameworkPaths.push(sysroot + "/System/Library/Frameworks"); + + return { + "includePaths": includePaths, + "libraryPaths": libraryPaths, + "frameworkPaths": frameworkPaths + }; + } finally { + p.close(); + } +} diff --git a/share/qbs/modules/cpp/genericunix-gcc.qbs b/share/qbs/modules/cpp/genericunix-gcc.qbs new file mode 100644 index 00000000..5dd032b7 --- /dev/null +++ b/share/qbs/modules/cpp/genericunix-gcc.qbs @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 + +UnixGCC { + condition: qbs.targetOS && !qbs.targetOS.containsAny(['darwin', 'linux']) && + qbs.toolchain && qbs.toolchain.contains('gcc') && !qbs.toolchain.contains('mingw') +} diff --git a/share/qbs/modules/cpp/ios-gcc.qbs b/share/qbs/modules/cpp/ios-gcc.qbs new file mode 100644 index 00000000..2ff4dc49 --- /dev/null +++ b/share/qbs/modules/cpp/ios-gcc.qbs @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.DarwinTools +import qbs.File +import qbs.FileInfo +import qbs.ModUtils + +DarwinGCC { + condition: qbs.targetOS.contains('ios') && + qbs.toolchain && qbs.toolchain.contains('gcc') + + targetSystem: "ios" + (minimumIosVersion || "") + + minimumDarwinVersion: minimumIosVersion + minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("ios-simulator") + ? "-mios-simulator-version-min" + : "-miphoneos-version-min" + minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("ios-simulator") + ? "-ios_simulator_version_min" + : "-iphoneos_version_min" + + platformObjcFlags: base.concat(simulatorObjcFlags) + platformObjcxxFlags: base.concat(simulatorObjcFlags) + + // Private properties + readonly property stringList simulatorObjcFlags: { + // default in Xcode and also required for building 32-bit Simulator binaries with ARC + // since the default ABI version is 0 for 32-bit targets + return qbs.targetOS.contains("ios-simulator") + ? ["-fobjc-abi-version=2", "-fobjc-legacy-dispatch"] + : []; + } + + Rule { + condition: !product.moduleProperty("qbs", "targetOS").contains("ios-simulator") + inputsFromDependencies: ["bundle"] + + Artifact { + filePath: FileInfo.joinPaths(product.destinationDirectory, product.targetName + ".ipa") + fileTags: ["ipa"] + } + + prepare: { + var cmd = new Command("PackageApplication", [input.filePath, "-o", output.filePath]); + cmd.description = "creating ipa"; + cmd.highlight = "codegen"; + return cmd; + } + } +} diff --git a/share/qbs/modules/cpp/macos-gcc.qbs b/share/qbs/modules/cpp/macos-gcc.qbs new file mode 100644 index 00000000..eb5dc3d4 --- /dev/null +++ b/share/qbs/modules/cpp/macos-gcc.qbs @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.ModUtils + +DarwinGCC { + condition: qbs.targetOS.contains('macos') && + qbs.toolchain && qbs.toolchain.contains('gcc') + + targetSystem: "macosx" + (minimumMacosVersion || "") + + minimumDarwinVersion: minimumMacosVersion + minimumDarwinVersionCompilerFlag: "-mmacosx-version-min" + minimumDarwinVersionLinkerFlag: "-macosx_version_min" +} diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js new file mode 100644 index 00000000..cb8c4848 --- /dev/null +++ b/share/qbs/modules/cpp/msvc.js @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var File = loadExtension("qbs.File"); +var FileInfo = loadExtension("qbs.FileInfo"); +var ModUtils = loadExtension("qbs.ModUtils"); +var WindowsUtils = loadExtension("qbs.WindowsUtils"); + +function compilerVersionDefine(cpp) { + var result = '_MSC_VER=' + cpp.compilerVersionMajor; + var s = cpp.compilerVersionMinor.toString(); + while (s.length < 2) + s = '0' + s; + result += s; + return result; +} + +function prepareCompiler(project, product, inputs, outputs, input, output) { + var i; + var optimization = ModUtils.moduleProperty(input, "optimization") + var debugInformation = ModUtils.moduleProperty(input, "debugInformation") + var args = ['/nologo', '/c'] + + // Determine which C-language we're compiling + var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(Object.keys(outputs))); + if (!["c", "cpp"].contains(tag)) + throw ("unsupported source language"); + + // Whether we're compiling a precompiled header or normal source file + var pchOutput = outputs[tag + "_pch"] ? outputs[tag + "_pch"][0] : undefined; + + var enableExceptions = ModUtils.moduleProperty(input, "enableExceptions"); + if (enableExceptions) { + var ehModel = ModUtils.moduleProperty(input, "exceptionHandlingModel"); + switch (ehModel) { + case "default": + args.push("/EHsc"); // "Yes" in VS + break; + case "seh": + args.push("/EHa"); // "Yes with SEH exceptions" in VS + break; + case "externc": + args.push("/EHs"); // "Yes with Extern C functions" in VS + break; + } + } + + var enableRtti = ModUtils.moduleProperty(input, "enableRtti"); + if (enableRtti !== undefined) { + args.push(enableRtti ? "/GR" : "/GR-"); + } + + // optimization: + if (optimization === 'small') + args.push('/Os') + else if (optimization === 'fast') + args.push('/O2') + + if (debugInformation) { + if (ModUtils.moduleProperty(product, "separateDebugInformation")) + args.push('/Zi'); + else + args.push('/Z7'); + } + + var rtl = ModUtils.moduleProperty(product, "runtimeLibrary"); + if (rtl) { + rtl = (rtl === "static" ? "/MT" : "/MD"); + if (product.moduleProperty("qbs", "enableDebugCode")) + rtl += "d"; + args.push(rtl); + } + + // warnings: + var warningLevel = ModUtils.moduleProperty(input, "warningLevel") + if (warningLevel === 'none') + args.push('/w') + if (warningLevel === 'all') + args.push('/Wall') + if (ModUtils.moduleProperty(input, "treatWarningsAsErrors")) + args.push('/WX') + var allIncludePaths = []; + var includePaths = ModUtils.moduleProperty(input, 'includePaths'); + if (includePaths) + allIncludePaths = allIncludePaths.uniqueConcat(includePaths); + var systemIncludePaths = ModUtils.moduleProperty(input, 'systemIncludePaths'); + if (systemIncludePaths) + allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths); + for (i in allIncludePaths) + args.push('/I' + FileInfo.toWindowsSeparators(allIncludePaths[i])) + var allDefines = []; + var platformDefines = ModUtils.moduleProperty(input, 'platformDefines'); + if (platformDefines) + allDefines = allDefines.uniqueConcat(platformDefines); + var defines = ModUtils.moduleProperty(input, 'defines'); + if (defines) + allDefines = allDefines.uniqueConcat(defines); + for (i in allDefines) + args.push('/D' + allDefines[i].replace(/%/g, "%%")); + + var minimumWindowsVersion = ModUtils.moduleProperty(product, "minimumWindowsVersion"); + if (minimumWindowsVersion) { + var hexVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'hex'); + if (hexVersion) { + var versionDefs = [ 'WINVER', '_WIN32_WINNT', '_WIN32_WINDOWS' ]; + for (i in versionDefs) { + args.push('/D' + versionDefs[i] + '=' + hexVersion); + } + } + } + + var objOutput = outputs.obj ? outputs.obj[0] : undefined + args.push('/Fo' + FileInfo.toWindowsSeparators(objOutput.filePath)) + args.push(FileInfo.toWindowsSeparators(input.filePath)) + + var prefixHeaders = ModUtils.moduleProperty(product, "prefixHeaders"); + for (i in prefixHeaders) + args.push("/FI" + FileInfo.toWindowsSeparators(prefixHeaders[i])); + + // Language + if (tag === "cpp") + args.push("/TP"); + else if (tag === "c") + args.push("/TC"); + + var commands = []; + var usePch = ModUtils.moduleProperty(input, "usePrecompiledHeader", tag); + if (usePch) { + if (pchOutput) { + // create PCH + args.push("/Yc"); + args.push("/Fp" + FileInfo.toWindowsSeparators(pchOutput.filePath)); + args.push("/Fo" + FileInfo.toWindowsSeparators(objOutput.filePath)); + args.push(FileInfo.toWindowsSeparators(input.filePath)); + var copyCmd = new JavaScriptCommand(); + copyCmd.tag = tag; + copyCmd.silent = true; + copyCmd.sourceCode = function() { + File.copy(input.filePath, outputs[tag + "_pch_copy"]); + }; + commands.push(copyCmd); + } else { + // use PCH + var pchHeaderFilePath = ".obj/" + product.name + '_' + tag + '.pch_copy'; + var pchFilePath = FileInfo.toWindowsSeparators(product.buildDirectory + + "\\.obj\\" + product.name + "_" + tag + ".pch"); + args.push("/FI" + pchHeaderFilePath); + args.push("/Yu" + pchHeaderFilePath); + args.push("/Fp" + pchFilePath); + } + } + + args = args.concat(ModUtils.moduleProperty(input, 'platformFlags'), + ModUtils.moduleProperty(input, 'flags'), + ModUtils.moduleProperty(input, 'platformFlags', tag), + ModUtils.moduleProperty(input, 'flags', tag)); + + var compilerPath = ModUtils.moduleProperty(product, "compilerPath"); + var wrapperArgs = ModUtils.moduleProperty(product, "compilerWrapper"); + if (wrapperArgs && wrapperArgs.length > 0) { + args.unshift(compilerPath); + compilerPath = wrapperArgs.shift(); + args = wrapperArgs.concat(args); + } + var cmd = new Command(compilerPath, args) + cmd.description = (pchOutput ? 'pre' : '') + 'compiling ' + input.fileName; + if (pchOutput) + cmd.description += ' (' + tag + ')'; + cmd.highlight = "compiler"; + cmd.workingDirectory = product.buildDirectory + "/.obj"; + cmd.responseFileUsagePrefix = '@'; + // cl.exe outputs the cpp file name. We filter that out. + cmd.inputFileName = input.fileName; + cmd.stdoutFilterFunction = function(output) { + return output.split(inputFileName + "\r\n").join(""); + }; + commands.push(cmd); + return commands; +} + +function prepareLinker(project, product, inputs, outputs, input, output) { + var i; + var linkDLL = (outputs.dynamiclibrary ? true : false) + var primaryOutput = (linkDLL ? outputs.dynamiclibrary[0] : outputs.application[0]) + var debugInformation = ModUtils.moduleProperty(product, "debugInformation") + var generateManifestFiles = !linkDLL && ModUtils.moduleProperty(product, "generateManifestFile") + + var args = ['/nologo'] + if (linkDLL) { + args.push('/DLL'); + args.push('/IMPLIB:' + FileInfo.toWindowsSeparators(outputs.dynamiclibrary_import[0].filePath)); + } + + if (debugInformation) { + args.push("/DEBUG"); + if (outputs.debuginfo) + args.push("/PDB:" + outputs.debuginfo[0].fileName); + } else { + args.push('/INCREMENTAL:NO') + } + + switch (product.moduleProperty("qbs", "architecture")) { + case "x86": + args.push("/MACHINE:X86"); + break; + case "x86_64": + args.push("/MACHINE:X64"); + break; + case "ia64": + args.push("/MACHINE:IA64"); + break; + case "armv7": + args.push("/MACHINE:ARM"); + break; + } + + var minimumWindowsVersion = ModUtils.moduleProperty(product, "minimumWindowsVersion"); + var subsystemSwitch = undefined; + if (minimumWindowsVersion || product.consoleApplication !== undefined) { + // Ensure that we default to console if product.consoleApplication is undefined + // since that could still be the case if only minimumWindowsVersion had been defined + subsystemSwitch = product.consoleApplication === false ? '/SUBSYSTEM:WINDOWS' : '/SUBSYSTEM:CONSOLE'; + } + + if (minimumWindowsVersion) { + var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, + 'subsystem'); + if (subsystemVersion) { + subsystemSwitch += ',' + subsystemVersion; + args.push('/OSVERSION:' + subsystemVersion); + } + } + + if (subsystemSwitch) + args.push(subsystemSwitch); + + var linkerOutputNativeFilePath; + var manifestFileName; + if (generateManifestFiles) { + linkerOutputNativeFilePath + = FileInfo.toWindowsSeparators( + FileInfo.path(primaryOutput.filePath) + "/intermediate." + + primaryOutput.fileName); + manifestFileName = linkerOutputNativeFilePath + ".manifest"; + args.push('/MANIFEST', '/MANIFESTFILE:' + manifestFileName) + } else { + linkerOutputNativeFilePath = FileInfo.toWindowsSeparators(primaryOutput.filePath); + } + + var allInputs = (inputs.obj || []).concat(inputs.staticlibrary || []) + if (inputs.dynamiclibrary_import) + allInputs = allInputs.concat(inputs.dynamiclibrary_import); + for (i in allInputs) { + var fileName = FileInfo.toWindowsSeparators(allInputs[i].filePath) + args.push(fileName) + } + + function pushLibs(libs) + { + if (!libs) + return; + var s = {}; + var c = libs.length; + for (var i = 0; i < c; ++i) { + var lib = FileInfo.toWindowsSeparators(libs[i]); + if (!lib.match(/\.lib$/i)) + lib += ".lib"; + if (s[lib]) + continue; + s[lib] = true; + args.push(lib); + } + } + + pushLibs(ModUtils.modulePropertiesFromArtifacts(product, inputs.staticlibrary, + "cpp", "staticLibraries")); + pushLibs(ModUtils.moduleProperty(product, "dynamicLibraries")); + + if (product.moduleProperty("cpp", "entryPoint")) + args.push("/ENTRY:" + product.moduleProperty("cpp", "entryPoint")); + + args.push('/OUT:' + linkerOutputNativeFilePath) + var libraryPaths = ModUtils.moduleProperty(product, 'libraryPaths'); + if (libraryPaths) + libraryPaths = [].uniqueConcat(libraryPaths); + for (i in libraryPaths) { + args.push('/LIBPATH:' + FileInfo.toWindowsSeparators(libraryPaths[i])) + } + var linkerFlags = ModUtils.moduleProperty(product, 'platformLinkerFlags').concat( + ModUtils.moduleProperty(product, 'linkerFlags')); + args = args.concat(linkerFlags); + if (ModUtils.moduleProperty(product, "allowUnresolvedSymbols")) + args.push("/FORCE:UNRESOLVED"); + + var linkerPath = product.moduleProperty("cpp", "linkerPath"); + var wrapperArgs = product.moduleProperty("cpp", "linkerWrapper"); + if (wrapperArgs && wrapperArgs.length > 0) { + args.unshift(linkerPath); + linkerPath = wrapperArgs.shift(); + args = wrapperArgs.concat(args); + } + var commands = []; + var cmd = new Command(linkerPath, args) + cmd.description = 'linking ' + primaryOutput.fileName; + cmd.highlight = 'linker'; + cmd.workingDirectory = FileInfo.path(primaryOutput.filePath) + cmd.responseFileUsagePrefix = '@'; + cmd.stdoutFilterFunction = function(output) { + return output.replace(/^ +Creating library.*\r\n$/, ""); + }; + commands.push(cmd); + + if (generateManifestFiles) { + var outputNativeFilePath = FileInfo.toWindowsSeparators(primaryOutput.filePath); + cmd = new JavaScriptCommand(); + cmd.src = linkerOutputNativeFilePath; + cmd.dst = outputNativeFilePath; + cmd.sourceCode = function() { + File.copy(src, dst); + } + cmd.silent = true + commands.push(cmd); + args = [ + '/nologo', '/manifest', manifestFileName, + "/outputresource:" + outputNativeFilePath + ";#" + (linkDLL ? "2" : "1") + ] + cmd = new Command("mt.exe", args) + cmd.description = 'embedding manifest into ' + primaryOutput.fileName; + cmd.highlight = 'linker'; + cmd.workingDirectory = FileInfo.path(primaryOutput.filePath) + commands.push(cmd); + } + + return commands; +} + diff --git a/share/qbs/modules/cpp/tvos-gcc.qbs b/share/qbs/modules/cpp/tvos-gcc.qbs new file mode 100644 index 00000000..d3d7f0a3 --- /dev/null +++ b/share/qbs/modules/cpp/tvos-gcc.qbs @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +DarwinGCC { + condition: qbs.targetOS.contains('tvos') && + qbs.toolchain && qbs.toolchain.contains('gcc') + + targetSystem: "tvos" + (minimumTvosVersion || "") + + minimumDarwinVersion: minimumTvosVersion + minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("tvos-simulator") + ? "-mtvos-simulator-version-min" + : "-mtvos-version-min" + minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("tvos-simulator") + ? "-tvos_simulator_version_min" + : "-tvos_version_min" +} diff --git a/share/qbs/modules/cpp/watchos-gcc.qbs b/share/qbs/modules/cpp/watchos-gcc.qbs new file mode 100644 index 00000000..c3edd86a --- /dev/null +++ b/share/qbs/modules/cpp/watchos-gcc.qbs @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs + +DarwinGCC { + condition: qbs.targetOS.contains('watchos') && + qbs.toolchain && qbs.toolchain.contains('gcc') + + targetSystem: "watchos" + (minimumWatchosVersion || "") + + minimumDarwinVersion: minimumWatchosVersion + minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("watchos-simulator") + ? "-mwatchos-simulator-version-min" + : "-mwatchos-version-min" + minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("watchos-simulator") + ? "-watchos_simulator_version_min" + : "-watchos_version_min" +} diff --git a/share/qbs/modules/cpp/windows-mingw.qbs b/share/qbs/modules/cpp/windows-mingw.qbs new file mode 100644 index 00000000..0f75dad7 --- /dev/null +++ b/share/qbs/modules/cpp/windows-mingw.qbs @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.ModUtils +import qbs.Utilities +import qbs.WindowsUtils + +GenericGCC { + condition: qbs.targetOS.contains("windows") && + qbs.toolchain && qbs.toolchain.contains("mingw") + staticLibraryPrefix: "lib" + dynamicLibraryPrefix: "" + executablePrefix: "" + staticLibrarySuffix: ".a" + dynamicLibrarySuffix: ".dll" + executableSuffix: ".exe" + debugInfoSuffix: ".debug" + imageFormat: "pe" + windowsApiCharacterSet: "unicode" + platformDefines: base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet)) + compilerDefines: ['__GNUC__', 'WIN32', '_WIN32'] + + property string windresName: 'windres' + property path windresPath: { return toolchainPathPrefix + windresName } + + setupBuildEnvironment: { + var v = new ModUtils.EnvironmentVariable("PATH", ";", true); + v.prepend(toolchainInstallPath); + v.set(); + } + + setupRunEnvironment: { + var v = new ModUtils.EnvironmentVariable("PATH", ";", true); + v.prepend(toolchainInstallPath); + v.set(); + } + + FileTagger { + patterns: ["*.rc"] + fileTags: ["rc"] + } + + Rule { + inputs: ["rc"] + auxiliaryInputs: ["hpp"] + + Artifact { + filePath: ".obj/" + Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + "_res.o" + fileTags: ["obj"] + } + + prepare: { + var platformDefines = ModUtils.moduleProperty(input, 'platformDefines'); + var defines = ModUtils.moduleProperty(input, 'defines'); + var includePaths = ModUtils.moduleProperty(input, 'includePaths'); + var systemIncludePaths = ModUtils.moduleProperty(input, 'systemIncludePaths'); + var args = []; + var i; + for (i in platformDefines) { + args.push('-D'); + args.push(platformDefines[i]); + } + for (i in defines) { + args.push('-D'); + args.push(defines[i]); + } + for (i in includePaths) { + args.push('-I'); + args.push(includePaths[i]); + } + for (i in systemIncludePaths) { + args.push('-I'); + args.push(systemIncludePaths[i]); + } + + args = args.concat(['-i', input.filePath, '-o', output.filePath]); + var cmd = new Command(ModUtils.moduleProperty(product, "windresPath"), args); + cmd.description = 'compiling ' + input.fileName; + cmd.highlight = 'compiler'; + return cmd; + } + } +} + diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs new file mode 100644 index 00000000..7ac0f234 --- /dev/null +++ b/share/qbs/modules/cpp/windows-msvc.qbs @@ -0,0 +1,368 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.PathTools +import qbs.Probes +import qbs.Utilities +import qbs.WindowsUtils +import 'msvc.js' as MSVC + +CppModule { + condition: qbs.hostOS.contains('windows') && + qbs.targetOS.contains('windows') && + qbs.toolchain && qbs.toolchain.contains('msvc') + + id: module + + Probes.MsvcProbe { + id: msvcProbe + compilerFilePath: compilerPath + preferredArchitecture: qbs.architecture + } + + qbs.architecture: msvcProbe.found ? msvcProbe.architecture : original + + compilerVersionMajor: msvcProbe.versionMajor + compilerVersionMinor: msvcProbe.versionMinor + compilerVersionPatch: msvcProbe.versionPatch + + windowsApiCharacterSet: "unicode" + platformDefines: base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet)) + platformCommonCompilerFlags: { + var flags = base; + if (compilerVersionMajor >= 18) // 2013 + flags.push("/FS"); + return flags; + } + compilerDefines: ['_WIN32', MSVC.compilerVersionDefine(module)] + warningLevel: "default" + compilerName: "cl.exe" + compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) + assemblerName: { + switch (qbs.architecture) { + case "armv7": + return "armasm.exe"; + case "ia64": + return "ias.exe"; + case "x86": + return "ml.exe"; + case "x86_64": + return "ml64.exe"; + } + } + + linkerName: "link.exe" + runtimeLibrary: "dynamic" + separateDebugInformation: true + + property bool generateManifestFile: true + property path toolchainInstallPath + architecture: qbs.architecture + staticLibraryPrefix: "" + dynamicLibraryPrefix: "" + executablePrefix: "" + staticLibrarySuffix: ".lib" + dynamicLibrarySuffix: ".dll" + executableSuffix: ".exe" + debugInfoSuffix: ".pdb" + property string dynamicLibraryImportSuffix: ".lib" + imageFormat: "pe" + + property var buildEnv: msvcProbe.buildEnv + + setupBuildEnvironment: { + for (var key in buildEnv) { + var v = new ModUtils.EnvironmentVariable(key, ';'); + v.prepend(buildEnv[key]); + v.set(); + } + } + + Rule { + condition: useCPrecompiledHeader + inputs: ["c_pch_src"] + auxiliaryInputs: ["hpp"] + Artifact { + fileTags: ['obj'] + filePath: ".obj/" + Utilities.getHash(input.completeBaseName) + '_c.obj' + } + Artifact { + fileTags: ['c_pch'] + filePath: ".obj/" + product.name + '_c.pch' + } + Artifact { + fileTags: ['c_pch_copy'] + filePath: ".obj/" + product.name + '_c.pch_copy' + } + prepare: { + return MSVC.prepareCompiler.apply(this, arguments); + } + } + + Rule { + condition: useCxxPrecompiledHeader + inputs: ["cpp_pch_src"] + explicitlyDependsOn: ["c_pch"] // to prevent vc--0.pdb conflict + auxiliaryInputs: ["hpp"] + Artifact { + fileTags: ['obj'] + filePath: ".obj/" + Utilities.getHash(input.completeBaseName) + '_cpp.obj' + } + Artifact { + fileTags: ['cpp_pch'] + filePath: ".obj/" + product.name + '_cpp.pch' + } + Artifact { + fileTags: ['cpp_pch_copy'] + filePath: ".obj/" + product.name + '_cpp.pch_copy' + } + + prepare: { + return MSVC.prepareCompiler.apply(this, arguments); + } + } + + Rule { + id: compiler + inputs: ["cpp", "c"] + auxiliaryInputs: ["hpp"] + explicitlyDependsOn: ["c_pch", "cpp_pch"] + + Artifact { + fileTags: ['obj'] + filePath: ".obj/" + Utilities.getHash(input.baseDir) + "/" + input.fileName + ".obj" + } + + prepare: { + return MSVC.prepareCompiler.apply(this, arguments); + } + } + + Rule { + id: applicationLinker + multiplex: true + inputs: ['obj'] + inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo"] + + outputFileTags: ["application", "debuginfo"] + outputArtifacts: { + var app = { + fileTags: ["application"], + filePath: FileInfo.joinPaths( + product.destinationDirectory, + PathTools.applicationFilePath(product)) + }; + var artifacts = [app]; + if (ModUtils.moduleProperty(product, "debugInformation") + && ModUtils.moduleProperty(product, "separateDebugInformation")) { + artifacts.push({ + fileTags: ["debuginfo"], + filePath: app.filePath.substr(0, app.filePath.length - 4) + + ModUtils.moduleProperty(product, "debugInfoSuffix") + }); + } + return artifacts; + } + + prepare: { + return MSVC.prepareLinker.apply(this, arguments); + } + } + + Rule { + id: dynamicLibraryLinker + multiplex: true + inputs: ['obj'] + inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo"] + + outputFileTags: ["dynamiclibrary", "dynamiclibrary_import", "debuginfo"] + outputArtifacts: { + var artifacts = [ + { + fileTags: ["dynamiclibrary"], + filePath: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath(product) + }, + { + fileTags: ["dynamiclibrary_import"], + filePath: product.destinationDirectory + "/" + PathTools.importLibraryFilePath(product), + alwaysUpdated: false + } + ]; + if (ModUtils.moduleProperty(product, "debugInformation") + && ModUtils.moduleProperty(product, "separateDebugInformation")) { + var lib = artifacts[0]; + artifacts.push({ + fileTags: ["debuginfo"], + filePath: lib.filePath.substr(0, lib.filePath.length - 4) + + ModUtils.moduleProperty(product, "debugInfoSuffix") + }); + } + return artifacts; + } + + prepare: { + return MSVC.prepareLinker.apply(this, arguments); + } + } + + Rule { + id: libtool + multiplex: true + inputs: ["obj"] + inputsFromDependencies: ["staticlibrary"] + + Artifact { + fileTags: ["staticlibrary"] + filePath: product.destinationDirectory + "/" + PathTools.staticLibraryFilePath(product) + cpp.staticLibraries: { + var result = ModUtils.moduleProperty(product, 'staticLibraries'); + for (var i in inputs.staticlibrary) { + var lib = inputs.staticlibrary[i] + result.push(lib.filePath) + var impliedLibs = ModUtils.moduleProperty(lib, 'staticLibraries') + result = result.uniqueConcat(impliedLibs); + } + return result + } + } + + prepare: { + var args = ['/nologo'] + var nativeOutputFileName = FileInfo.toWindowsSeparators(output.filePath) + args.push('/OUT:' + nativeOutputFileName) + for (var i in inputs.obj) { + var fileName = FileInfo.toWindowsSeparators(inputs.obj[i].filePath) + args.push(fileName) + } + var cmd = new Command("lib.exe", args); + cmd.description = 'creating ' + output.fileName; + cmd.highlight = 'linker'; + cmd.workingDirectory = FileInfo.path(output.filePath) + cmd.responseFileUsagePrefix = '@'; + return cmd; + } + } + + FileTagger { + patterns: ["*.rc"] + fileTags: ["rc"] + } + + Rule { + inputs: ["rc"] + auxiliaryInputs: ["hpp"] + + Artifact { + filePath: ".obj/" + Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".res" + fileTags: ["obj"] + } + + prepare: { + var platformDefines = ModUtils.moduleProperty(input, 'platformDefines'); + var defines = ModUtils.moduleProperty(input, 'defines'); + var includePaths = ModUtils.moduleProperty(input, 'includePaths'); + var systemIncludePaths = ModUtils.moduleProperty(input, 'systemIncludePaths'); + var args = []; + var i; + var hasNoLogo = ModUtils.moduleProperty(product, "compilerVersionMajor") >= 16; // 2010 + if (hasNoLogo) + args.push("/nologo"); + + for (i in platformDefines) { + args.push('/d'); + args.push(platformDefines[i]); + } + for (i in defines) { + args.push('/d'); + args.push(defines[i]); + } + for (i in includePaths) { + args.push('/i'); + args.push(includePaths[i]); + } + for (i in systemIncludePaths) { + args.push('/i'); + args.push(systemIncludePaths[i]); + } + + args = args.concat(['/fo', output.filePath, input.filePath]); + var cmd = new Command('rc', args); + cmd.description = 'compiling ' + input.fileName; + cmd.highlight = 'compiler'; + + if (!hasNoLogo) { + // Remove the first two lines of stdout. That's the logo. + cmd.stdoutFilterFunction = function(output) { + var idx = 0; + for (var i = 0; i < 3; ++i) + idx = output.indexOf('\n', idx + 1); + return output.substr(idx + 1); + } + } + + return cmd; + } + } + + FileTagger { + patterns: "*.asm" + fileTags: ["asm"] + } + + Rule { + inputs: ["asm"] + Artifact { + filePath: ".obj/" + Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".obj" + fileTags: ["obj"] + } + prepare: { + var args = ["/nologo", "/c", + "/Fo" + FileInfo.toWindowsSeparators(output.filePath), + FileInfo.toWindowsSeparators(input.filePath)]; + if (ModUtils.moduleProperty(product, "debugInformation")) + args.push("/Zi"); + args = args.concat(ModUtils.moduleProperty(input, 'platformFlags', 'asm'), + ModUtils.moduleProperty(input, 'flags', 'asm')); + var cmd = new Command(ModUtils.moduleProperty(product, "assemblerPath"), args); + cmd.description = "assembling " + input.fileName; + cmd.inputFileName = input.fileName; + cmd.stdoutFilterFunction = function(output) { + var lines = output.split("\r\n").filter(function (s) { + return !s.endsWith(inputFileName); }); + return lines.join("\r\n"); + }; + return cmd; + } + } +} diff --git a/share/qbs/modules/ib/IBModule.qbs b/share/qbs/modules/ib/IBModule.qbs new file mode 100644 index 00000000..f0206dd4 --- /dev/null +++ b/share/qbs/modules/ib/IBModule.qbs @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.BundleTools +import qbs.DarwinTools +import qbs.FileInfo +import qbs.ModUtils +import qbs.Process +import 'ib.js' as Ib + +Module { + Depends { name: "xcode"; required: false } + + Probe { + id: ibProbe + property string toolPath: ibtoolPath // input + property string toolVersion // output + configure: { + toolVersion = Ib.ibtoolVersion(toolPath); + found = true; + } + } + + condition: qbs.hostOS.contains("darwin") && qbs.targetOS.contains("darwin") + + property bool warnings: true + property bool errors: true + property bool notices: true + + property stringList flags + + // iconutil specific + property string iconutilName: "iconutil" + property string iconutilPath: FileInfo.joinPaths("/usr/bin", iconutilName) + + // XIB/NIB specific + property string ibtoolName: "ibtool" + property string ibtoolPath: FileInfo.joinPaths(xcode.developerPath, "/usr/bin", ibtoolName) + property bool flatten: true + property string module + property bool autoActivateCustomFonts: true + + // Asset catalog specific + property string actoolName: xcode.present ? "actool" : "ictool" + property string actoolPath: FileInfo.joinPaths(xcode.developerPath, "/usr/bin", actoolName) + property string appIconName + property string launchImageName + property bool compressPngs: true + + // private properties + property string outputFormat: "human-readable-text" + property string appleIconSuffix: ".icns" + property string compiledAssetCatalogSuffix: ".car" + property string compiledNibSuffix: ".nib" + property string compiledStoryboardSuffix: ".storyboardc" + + version: ibtoolVersion + property string ibtoolVersion: ibProbe.toolVersion + property var ibtoolVersionParts: ibtoolVersion ? ibtoolVersion.split('.').map(function(item) { return parseInt(item, 10); }) : [] + property int ibtoolVersionMajor: ibtoolVersionParts[0] + property int ibtoolVersionMinor: ibtoolVersionParts[1] + property int ibtoolVersionPatch: ibtoolVersionParts[2] + + property stringList targetDevices: xcode.present + ? xcode.targetDevices + : DarwinTools.targetDevices(qbs.targetOS) + + validate: { + var validator = new ModUtils.PropertyValidator("ib"); + validator.setRequiredProperty("ibtoolVersion", ibtoolVersion); + validator.setRequiredProperty("ibtoolVersionMajor", ibtoolVersionMajor); + validator.setRequiredProperty("ibtoolVersionMinor", ibtoolVersionMinor); + validator.addVersionValidator("ibtoolVersion", ibtoolVersion, 2, 3); + validator.addRangeValidator("ibtoolVersionMajor", ibtoolVersionMajor, 1); + validator.addRangeValidator("ibtoolVersionMinor", ibtoolVersionMinor, 0); + if (ibtoolVersionPatch !== undefined) + validator.addRangeValidator("ibtoolVersionPatch", ibtoolVersionPatch, 0); + validator.validate(); + } + + FileTagger { + patterns: ["*.iconset"] // bundle + fileTags: ["iconset"] + } + + FileTagger { + patterns: ["*.nib", "*.xib"] + fileTags: ["nib"] + } + + FileTagger { + patterns: ["*.storyboard"] + fileTags: ["storyboard"] + } + + FileTagger { + patterns: ["*.xcassets"] // bundle + fileTags: ["assetcatalog"] + } + + Rule { + inputs: ["iconset"] + + Artifact { + filePath: FileInfo.joinPaths(BundleTools.destinationDirectoryForResource(product, input), + input.completeBaseName + + ModUtils.moduleProperty(product, "appleIconSuffix")) + fileTags: ["icns"] + } + + prepare: { + var args = ["--convert", "icns", "--output", output.filePath, input.filePath]; + var cmd = new Command(ModUtils.moduleProperty(product, "iconutilPath"), args); + cmd.description = "compiling " + input.fileName; + return cmd; + } + } + + Rule { + inputs: ["nib", "storyboard"] + + outputFileTags: { + var tags = []; + for (var i = 0; i < inputs.length; ++i) + tags = tags.uniqueConcat(ModUtils.allFileTags(Ib.ibtoolFileTaggers(inputs[i]))); + return tags; + } + + outputArtifacts: Ib.ibtoolOutputArtifacts(product, inputs, input) + + prepare: { + var cmd = new Command(ModUtils.moduleProperty(product, "ibtoolPath"), + Ib.ibtooldArguments(product, inputs, outputs)); + cmd.description = "compiling " + input.fileName; + + // Also display the language name of the nib/storyboard being compiled if it has one + var localizationKey = DarwinTools.localizationKey(input.filePath); + if (localizationKey) + cmd.description += ' (' + localizationKey + ')'; + + cmd.highlight = 'compiler'; + + // May not be strictly needed, but is set by some versions of Xcode + if (input.fileTags.contains("storyboard")) + cmd.environment.push("IBSC_MINIMUM_COMPATIBILITY_VERSION=" + + (product.moduleProperty("cpp", "minimumDarwinVersion") || "")); + + cmd.stdoutFilterFunction = function(output) { + return ""; + }; + + return cmd; + } + } + + Rule { + inputs: ["assetcatalog"] + multiplex: true + + outputArtifacts: Ib.actoolOutputArtifacts(product, inputs) + outputFileTags: ["compiled_assetcatalog", "partial_infoplist"] + + prepare: { + var cmd = new Command(ModUtils.moduleProperty(product, "actoolPath"), + Ib.ibtooldArguments(product, inputs, outputs)); + cmd.description = inputs["assetcatalog"].map(function (input) { + return "compiling " + input.fileName; + }).join('\n'); + cmd.highlight = "compiler"; + + cmd.stdoutFilterFunction = function(output) { + return ""; + }; + + return cmd; + } + } +} diff --git a/share/qbs/modules/ib/ib.js b/share/qbs/modules/ib/ib.js new file mode 100644 index 00000000..b38fcd80 --- /dev/null +++ b/share/qbs/modules/ib/ib.js @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var BundleTools = loadExtension("qbs.BundleTools"); +var DarwinTools = loadExtension("qbs.DarwinTools"); +var File = loadExtension("qbs.File"); +var FileInfo = loadExtension("qbs.FileInfo"); +var ModUtils = loadExtension("qbs.ModUtils"); +var Process = loadExtension("qbs.Process"); +var PropertyList = loadExtension("qbs.PropertyList"); + +function artifactsFromInputs(inputs) { + var artifacts = []; + for (var tag in inputs) { + artifacts = artifacts.concat(inputs[tag]); + } + return artifacts; +} + +function ibtooldArguments(product, inputs, outputs, overrideOutput) { + var i; + var args = []; + var allInputs = artifactsFromInputs(inputs); + + var outputFormat = ModUtils.moduleProperty(product, "outputFormat"); + if (outputFormat) { + if (!["binary1", "xml1", "human-readable-text"].contains(outputFormat)) + throw("Invalid ibtoold output format: " + outputFormat + ". " + + "Must be in [binary1, xml1, human-readable-text]."); + + args.push("--output-format", outputFormat); + } + + var debugFlags = ["warnings", "errors", "notices"]; + for (var j in debugFlags) { + var flag = debugFlags[j]; + if (ModUtils.modulePropertyFromArtifacts(product, allInputs, product.moduleName, flag)) { + args.push("--" + flag); + } + } + + if (inputs.assetcatalog) { + args.push("--platform", DarwinTools.applePlatformName( + product.moduleProperty("qbs", "targetOS"), + product.moduleProperty("xcode", "platformType"))); + + var appIconName = ModUtils.modulePropertyFromArtifacts(product, inputs.assetcatalog, product.moduleName, "appIconName"); + if (appIconName) + args.push("--app-icon", appIconName); + + var launchImageName = ModUtils.modulePropertyFromArtifacts(product, inputs.assetcatalog, product.moduleName, "launchImageName"); + if (launchImageName) + args.push("--launch-image", launchImageName); + + // Undocumented but used by Xcode (only for iOS?), probably runs pngcrush or equivalent + if (ModUtils.modulePropertyFromArtifacts(product, inputs.assetcatalog, product.moduleName, "compressPngs")) + args.push("--compress-pngs"); + } else { + var sysroot = product.moduleProperty("qbs", "sysroot"); + if (sysroot) + args.push("--sdk", sysroot); + + args.push("--flatten", ModUtils.modulePropertyFromArtifacts(product, allInputs, product.moduleName, "flatten") ? 'YES' : 'NO'); + + // --module and --auto-activate-custom-fonts were introduced in Xcode 6.0 + if (ModUtils.moduleProperty(product, "ibtoolVersionMajor") >= 6) { + var module = ModUtils.moduleProperty(product, "module"); + if (module) + args.push("--module", module); + + if (ModUtils.modulePropertyFromArtifacts(product, allInputs, product.moduleName, "autoActivateCustomFonts")) + args.push("--auto-activate-custom-fonts"); + } + } + + // --minimum-deployment-target was introduced in Xcode 5.0 + var minimumDarwinVersion = product.moduleProperty("cpp", "minimumDarwinVersion"); + if (minimumDarwinVersion && ModUtils.moduleProperty(product, "ibtoolVersionMajor") >= 5) + args.push("--minimum-deployment-target", minimumDarwinVersion); + + // --target-device and -output-partial-info-plist were introduced in Xcode 6.0 for ibtool + if (ModUtils.moduleProperty(product, "ibtoolVersionMajor") >= 6 || inputs.assetcatalog) { + args.push("--output-partial-info-plist", (outputs && outputs.partial_infoplist) + ? outputs.partial_infoplist[0].filePath + : "/dev/null"); + + // For iOS, we'd normally only output the devices specified in TARGETED_DEVICE_FAMILY + // We can't get this info from Info.plist keys due to dependency order, so use the qbs prop + var targetDevices = ModUtils.moduleProperty(product, "targetDevices"); + for (i in targetDevices) { + args.push("--target-device", targetDevices[i]); + } + } + + args = args.concat(ModUtils.modulePropertiesFromArtifacts(product, allInputs, + product.moduleName, "flags")); + + if (overrideOutput) { + args.push("--compile", overrideOutput); + } else { + if (outputs.compiled_ibdoc_main) + args.push("--compile", outputs.compiled_ibdoc_main[0].filePath); + + if (outputs.compiled_assetcatalog) + args.push("--compile", + BundleTools.destinationDirectoryForResource(product, inputs.assetcatalog[0])); + } + + for (i in allInputs) + args.push(allInputs[i].filePath); + + return args; +} + +function ibtoolFileTaggers(fileTags) { + var ext; + if (fileTags.contains("nib") && !fileTags.contains("storyboard")) + ext = "nib"; + if (fileTags.contains("storyboard") && !fileTags.contains("nib")) + ext = "storyboard"; + + if (!ext) + throw "unknown ibtool input file tags: " + fileTags; + + var t = "compiled_ibdoc"; + return { + ".nib": [t, "compiled_" + ext + (ext !== "nib" ? "_nib" : "")], + ".plist": [t, "compiled_" + ext + "_infoplist"], + ".storyboard": [t, "compiled_" + ext] + }; +} + +function ibtoolOutputArtifacts(product, inputs, input) { + var suffix = input.completeBaseName; + if (input.fileTags.contains("nib")) + suffix += ModUtils.moduleProperty(product, "compiledNibSuffix"); + else if (input.fileTags.contains("storyboard")) + suffix += ModUtils.moduleProperty(product, "compiledStoryboardSuffix"); + + var tracker = new ModUtils.BlackboxOutputArtifactTracker(); + tracker.hostOS = product.moduleProperty("qbs", "hostOS"); + tracker.shellPath = product.moduleProperty("qbs", "shellPath"); + tracker.fileTaggers = ibtoolFileTaggers(input.fileTags); + tracker.command = ModUtils.moduleProperty(product, "ibtoolPath"); + tracker.commandArgsFunction = function (outputDirectory) { + // Last --output-format argument overrides any previous ones + // Append the name of the base output since it can be either a file or a directory + // in the case of XIB compilations + return ibtooldArguments(product, inputs, + undefined, FileInfo.joinPaths(outputDirectory, suffix)) + .concat(["--output-format", "xml1"]); + }; + + var artifacts = tracker.artifacts( + FileInfo.joinPaths(BundleTools.destinationDirectoryForResource(product, input))); + + if (product.moduleProperty("ib", "ibtoolVersionMajor") >= 6) { + var prefix = input.fileTags.contains("storyboard") ? "SB" : ""; + var path = FileInfo.joinPaths(product.destinationDirectory, input.completeBaseName + + "-" + prefix + "PartialInfo.plist"); + artifacts.push({ filePath: path, fileTags: ["partial_infoplist"] }); + } + + // Tag the "main" output + // This can be either a file or directory so the artifact might already exist in the output list + var main = FileInfo.joinPaths(BundleTools.destinationDirectoryForResource(product, input), + suffix); + var mainTags = ["compiled_ibdoc", "compiled_ibdoc_main"]; + var mainIndex = -1; + for (var i = 0; i < artifacts.length; ++i) { + if (artifacts[i].filePath === main) { + mainIndex = i; + break; + } + } + + if (mainIndex === -1) { + // artifact not in list - the output was a directory (unflatted nib or storyboard) + artifacts.splice(0, 0, { + filePath: main, + fileTags: mainTags + }); + } else { + // artifact in list - the output was a file (flattened nib) + artifacts[mainIndex].fileTags = mainTags.uniqueConcat(artifacts[mainIndex].fileTags); + } + + return artifacts; +} + +function actoolOutputArtifacts(product, inputs) { + // actool has no --dry-run option (rdar://21786925), + // so compile to a fake temporary directory in order to extract the list of output files + var tracker = new ModUtils.BlackboxOutputArtifactTracker(); + tracker.hostOS = product.moduleProperty("qbs", "hostOS"); + tracker.shellPath = product.moduleProperty("qbs", "shellPath"); + tracker.command = ModUtils.moduleProperty(product, "actoolPath"); + tracker.commandArgsFunction = function (outputDirectory) { + // Last --output-format argument overrides any previous ones + return ibtooldArguments(product, inputs, + undefined, outputDirectory).concat(["--output-format", "xml1"]); + }; + tracker.processStdOutFunction = parseActoolOutput; + var artifacts = tracker.artifacts(BundleTools.destinationDirectoryForResource(product, + inputs.assetcatalog[0])); + + // Newer versions of actool don't generate *anything* if there's no input; + // in that case a partial Info.plist would not have been generated either + if (artifacts && artifacts.length > 0) { + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, + "assetcatalog_generated_info.plist"), + fileTags: ["partial_infoplist"] + }); + } + + return artifacts; +} + +function parseActoolOutput(output) { + var propertyList = new PropertyList(); + try { + propertyList.readFromString(output); + + var plist = propertyList.toObject(); + if (plist) + plist = plist["com.apple.actool.compilation-results"]; + if (plist) { + var artifacts = []; + files = plist["output-files"]; + for (var i in files) { + if (files[i] === "/dev/null") + continue; + var tags = files[i].endsWith(".plist") + ? ["partial_infoplist"] + : ["compiled_assetcatalog"]; + artifacts.push({ + filePath: files[i], + fileTags: tags + }); + } + + return artifacts; + } + } finally { + propertyList.clear(); + } +} + +function ibtoolVersion(ibtool) { + var process; + var version; + try { + process = new Process(); + if (process.exec(ibtool, ["--version", "--output-format", "xml1"], true) !== 0) + console.error(process.readStdErr()); + + var propertyList = new PropertyList(); + try { + propertyList.readFromString(process.readStdOut()); + + var plist = propertyList.toObject(); + if (plist) + plist = plist["com.apple.ibtool.version"]; + if (plist) + version = plist["short-bundle-version"]; + } finally { + propertyList.clear(); + } + } finally { + process.close(); + } + return version; +} diff --git a/share/qbs/modules/innosetup/InnoSetupModule.qbs b/share/qbs/modules/innosetup/InnoSetupModule.qbs new file mode 100644 index 00000000..70e36f77 --- /dev/null +++ b/share/qbs/modules/innosetup/InnoSetupModule.qbs @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes + +Module { + condition: qbs.targetOS.contains("windows") + + Probes.InnoSetupProbe { + id: innoSetupProbe + } + + property path toolchainInstallPath: innoSetupProbe.path + version: innoSetupProbe.version + property var versionParts: version ? version.split('.').map(function (item) { return parseInt(item, 10); }) : [] + property int versionMajor: versionParts[0] + property int versionMinor: versionParts[1] + property int versionPatch: versionParts[2] + + property string compilerName: "ISCC.exe" + property string compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) + + property bool verboseOutput: false + PropertyOptions { + name: "verboseOutput" + description: "display verbose output from the Inno Setup compiler" + } + + property pathList includePaths + PropertyOptions { + name: "includePaths" + description: "directories to add to the include search path" + } + + property stringList defines + PropertyOptions { + name: "defines" + description: "variables that are defined when using the Inno Setup compiler" + } + + property stringList compilerFlags + PropertyOptions { + name: "compilerFlags" + description: "additional flags for the Inno Setup compiler" + } + + readonly property string executableSuffix: ".exe" + + validate: { + var validator = new ModUtils.PropertyValidator("innosetup"); + validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath); + validator.setRequiredProperty("version", version); + validator.setRequiredProperty("versionMajor", versionMajor); + validator.setRequiredProperty("versionMinor", versionMinor); + validator.setRequiredProperty("versionPatch", versionPatch); + validator.addVersionValidator("version", version, 3, 3); + validator.addRangeValidator("versionMajor", versionMajor, 1); + validator.addRangeValidator("versionMinor", versionMinor, 0); + validator.addRangeValidator("versionPatch", versionPatch, 0); + validator.validate(); + } + + // Inno Setup Script + FileTagger { + patterns: ["*.iss"] + fileTags: ["innosetup.iss"] + } + + Rule { + id: innoSetupCompiler + inputs: ["innosetup.iss"] + + Artifact { + fileTags: ["innosetup.exe", "application"] + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + + ModUtils.moduleProperty(product, "executableSuffix")) + } + + prepare: { + var i; + var args = [ + "/O" + FileInfo.toNativeSeparators(FileInfo.path(output.filePath)), + "/F" + FileInfo.toNativeSeparators(FileInfo.completeBaseName(output.fileName)) + ]; + + if (!ModUtils.moduleProperty(product, "verboseOutput")) + args.push("/Q"); + + var includePaths = ModUtils.moduleProperty(product, "includePaths"); + for (i in includePaths) + args.push("/I" + FileInfo.toNativeSeparators(includePaths[i])); + + // User-supplied defines + var defines = ModUtils.moduleProperty(product, "defines"); + for (i in defines) + args.push("/D" + defines[i]); + + // User-supplied flags + var flags = ModUtils.moduleProperty(product, "compilerFlags"); + for (i in flags) + args.push(flags[i]); + + args.push(FileInfo.toNativeSeparators(input.filePath)); + var cmd = new Command(ModUtils.moduleProperty(product, "compilerPath"), args); + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.workingDirectory = FileInfo.path(input.filePath); + return cmd; + } + } +} diff --git a/share/qbs/modules/java/JavaModule.qbs b/share/qbs/modules/java/JavaModule.qbs new file mode 100644 index 00000000..d1d92c83 --- /dev/null +++ b/share/qbs/modules/java/JavaModule.qbs @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes +import qbs.Process +import qbs.TextFile + +import "utils.js" as JavaUtils + +Module { + Probes.JdkProbe { + id: jdk + environmentPaths: (jdkPath ? [jdkPath] : []).concat(base) + } + + property stringList additionalClassPaths + property stringList additionalCompilerFlags + property stringList additionalJarFlags + property stringList bootClassPaths + property string compilerFilePath: FileInfo.joinPaths(jdkPath, "bin", compilerName) + property string compilerName: "javac" + property bool enableWarnings: true + property string interpreterFilePath : FileInfo.joinPaths(jdkPath, "bin", interpreterName) + property string interpreterName: "java" + property string jarFilePath: FileInfo.joinPaths(jdkPath, "bin", jarName) + property string jarName: "jar" + + property path jdkPath: jdk.path + + version: compilerVersion + property string compilerVersion: jdk.version ? jdk.version[1] : undefined + property var compilerVersionParts: compilerVersion ? compilerVersion.split(/[\._]/).map(function(item) { return parseInt(item, 10); }) : [] + property int compilerVersionMajor: compilerVersionParts[0] + property int compilerVersionMinor: compilerVersionParts[1] + property int compilerVersionPatch: compilerVersionParts[2] + property int compilerVersionUpdate: compilerVersionParts[3] + + property string languageVersion + PropertyOptions { + name: "languageVersion" + description: "Java language version to interpret source code as" + } + + property string runtimeVersion + PropertyOptions { + name: "runtimeVersion" + description: "version of the Java runtime to generate compatible bytecode for" + } + + property var manifest: { + return { + "Manifest-Version": "1.0", + "Class-Path": manifestClassPath ? manifestClassPath.join(" ") : undefined + }; + } + + PropertyOptions { + name: "manifest" + description: "properties to add to the manifest file when building a JAR" + } + + property path manifestFile + PropertyOptions { + name: "manifestFile" + description: "manifest file to embed when building a JAR" + } + + property stringList manifestClassPath + PropertyOptions { + name: "manifestClassPath" + description: "entries to add to the manifest's Class-Path when building a JAR" + } + + property bool warningsAsErrors: false + + property pathList jdkIncludePaths: { + var paths = []; + if (qbs.hostOS.contains("darwin") && compilerVersionMinor <= 6) { + paths.push("/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers"); + } else { + paths.push(FileInfo.joinPaths(jdkPath, "include")); + + var hostOS = qbs.hostOS.contains("windows") ? qbs.hostOS.concat(["win32"]) : qbs.hostOS; + var platforms = ["win32", "darwin", "linux", "bsd", "solaris"]; + for (var i = 0; i < platforms.length; ++i) { + if (hostOS.contains(platforms[i])) { + // Corresponds to JDK_INCLUDE_SUBDIR in the JDK Makefiles + paths.push(FileInfo.joinPaths(jdkPath, "include", platforms[i])); + break; + } + } + } + + return paths; + } + + // Internal properties + property path classFilesDir: FileInfo.joinPaths(product.buildDirectory, "classes") + property path internalClassFilesDir: FileInfo.joinPaths(product.buildDirectory, ".classes") + + property path runtimeJarPath: { + if (qbs.hostOS.contains("macos") && compilerVersionMajor === 1 && compilerVersionMinor < 7) + return FileInfo.joinPaths(jdkPath, "bundle", "Classes", "classes.jar"); + return FileInfo.joinPaths(jdkPath, "jre", "lib", "rt.jar"); + } + + property path toolsJarPath: { + if (compilerVersionMajor > 1 || (compilerVersionMajor === 1 && compilerVersionMinor >= 7)) + return FileInfo.joinPaths(jdkPath, "lib", "tools.jar"); + } + + validate: { + var validator = new ModUtils.PropertyValidator("java"); + validator.setRequiredProperty("jdkPath", jdkPath); + validator.setRequiredProperty("compilerVersion", compilerVersion); + validator.setRequiredProperty("compilerVersionParts", compilerVersionParts); + validator.setRequiredProperty("compilerVersionMajor", compilerVersionMajor); + validator.setRequiredProperty("compilerVersionMinor", compilerVersionMinor); + validator.setRequiredProperty("compilerVersionUpdate", compilerVersionUpdate); + validator.addVersionValidator("compilerVersion", compilerVersion + ? compilerVersion.replace("_", ".") : undefined, 4, 4); + validator.addRangeValidator("compilerVersionMajor", compilerVersionMajor, 1); + validator.addRangeValidator("compilerVersionMinor", compilerVersionMinor, 0); + validator.addRangeValidator("compilerVersionPatch", compilerVersionPatch, 0); + validator.addRangeValidator("compilerVersionUpdate", compilerVersionUpdate, 0); + validator.validate(); + } + + FileTagger { + patterns: "*.java" + fileTags: ["java.java"] + } + + Group { + name: "io.qt.qbs.internal.java-helper" + files: { + return JavaUtils.helperFullyQualifiedNames("java").map(function(name) { + return FileInfo.joinPaths(path, name + ".java"); + }); + } + + fileTags: ["java.java-internal"] + } + + Rule { + multiplex: true + inputs: ["java.java-internal"] + + outputFileTags: ["java.class-internal"] + outputArtifacts: { + return JavaUtils.helperOutputArtifacts(product); + } + + prepare: { + var cmd = new Command(ModUtils.moduleProperty(product, "compilerFilePath"), + JavaUtils.javacArguments(product, inputs, + JavaUtils.helperOverrideArgs(product, + "javac"))); + cmd.ignoreDryRun = true; + cmd.silent = true; + return [cmd]; + } + } + + Rule { + multiplex: true + inputs: ["java.java"] + inputsFromDependencies: ["java.jar"] + explicitlyDependsOn: ["java.class-internal"] + + outputFileTags: ["java.class", "hpp"] // Annotations can produce additional java source files. Ignored for now. + outputArtifacts: { + return JavaUtils.outputArtifacts(product, inputs); + } + prepare: { + var cmd = new Command(ModUtils.moduleProperty(product, "compilerFilePath"), + JavaUtils.javacArguments(product, inputs)); + cmd.description = "Compiling Java sources"; + cmd.highlight = "compiler"; + return [cmd]; + } + } + + Rule { + inputs: ["java.class"] + multiplex: true + + Artifact { + fileTags: ["java.jar"] + filePath: FileInfo.joinPaths(product.destinationDirectory, product.targetName + ".jar") + } + + prepare: { + var i, key; + var flags = "cf"; + var args = [output.filePath]; + + var manifestFile = ModUtils.moduleProperty(product, "manifestFile"); + var manifest = ModUtils.moduleProperty(product, "manifest"); + var aggregateManifest = JavaUtils.manifestContents(manifestFile) || {}; + + // Add local key-value pairs (overrides equivalent keys specified in the file if + // one was given) + for (key in manifest) { + if (manifest.hasOwnProperty(key)) + aggregateManifest[key] = manifest[key]; + } + + for (key in aggregateManifest) { + if (aggregateManifest.hasOwnProperty(key) && aggregateManifest[key] === undefined) + delete aggregateManifest[key]; + } + + // Use default manifest unless we actually have properties to set + var needsManifestFile = manifestFile !== undefined || aggregateManifest !== {"Manifest-Version": "1.0"}; + + manifestFile = FileInfo.joinPaths(product.buildDirectory, "manifest.mf"); + + var mf; + try { + mf = new TextFile(manifestFile, TextFile.WriteOnly); + + // Ensure that manifest version comes first + mf.write("Manifest-Version: " + (aggregateManifest["Manifest-Version"] || "1.0") + "\n"); + delete aggregateManifest["Manifest-Version"]; + + for (key in aggregateManifest) + mf.write(key + ": " + aggregateManifest[key] + "\n"); + + mf.write("\n"); + } finally { + if (mf) { + mf.close(); + } + } + + if (needsManifestFile) { + flags += "m"; + args.push(manifestFile); + } + + var entryPoint = ModUtils.moduleProperty(product, "entryPoint"); + var entryPoint = product.entryPoint; + if (entryPoint) { + flags += "e"; + args.push(entryPoint); + } + + args.unshift(flags); + + var otherFlags = ModUtils.moduleProperty(product, "additionalJarFlags"); + if (otherFlags) + args = args.concat(otherFlags); + + for (i in inputs["java.class"]) + args.push(FileInfo.relativePath(ModUtils.moduleProperty(product, "classFilesDir"), + inputs["java.class"][i].filePath)); + var cmd = new Command(ModUtils.moduleProperty(product, "jarFilePath"), args); + cmd.workingDirectory = ModUtils.moduleProperty(product, "classFilesDir"); + cmd.description = "building " + FileInfo.fileName(output.fileName); + cmd.highlight = "linker"; + return cmd; + } + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/Artifact.java b/share/qbs/modules/java/io/qt/qbs/Artifact.java new file mode 100644 index 00000000..50df3777 --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/Artifact.java @@ -0,0 +1,75 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 Jake Petroules. + ** Contact: http://www.qt.io/licensing + ** + ** This file is part of Qbs. + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs; + +import java.util.ArrayList; +import java.util.List; + +public class Artifact { + private String filePath; + private List fileTags; + + public Artifact(String filePath) { + if (filePath == null) + throw new IllegalArgumentException("filePath"); + + this.filePath = filePath; + this.fileTags = new ArrayList(); + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public List getFileTags() { + return fileTags; + } + + public void setFileTags(List fileTags) { + this.fileTags = fileTags; + } + + public void addFileTag(String fileTag) { + this.fileTags.add(fileTag); + } + + public void removeFileTag(String fileTag) { + this.fileTags.remove(fileTag); + } + + public void clearFileTags() { + this.fileTags.clear(); + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/ArtifactListJsonWriter.java b/share/qbs/modules/java/io/qt/qbs/ArtifactListJsonWriter.java new file mode 100644 index 00000000..d7f60ddc --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/ArtifactListJsonWriter.java @@ -0,0 +1,153 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 Jake Petroules. + ** Contact: http://www.qt.io/licensing + ** + ** This file is part of Qbs. + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.List; + +/** + * This uses a custom JSON implementation because the Java Standard Library does + * not yet have native support for JSON, and only minimal support is required + * here. + */ +public class ArtifactListJsonWriter implements ArtifactListWriter { + private static final int TAB_WIDTH = 4; + + // based on escapeString from qtbase/qjsonwriter.cpp + private static String escapeString(String s) { + String out = ""; + for (int i = 0; i < s.length();) { + int u = s.codePointAt(i); + + // unpaired surrogate + if (u >= Character.MIN_SURROGATE && u <= Character.MAX_SURROGATE) { + out += "\ufffd"; + i += Character.charCount(u); + continue; + } + + if (u < 0x80) { + if (u < 0x20 || u == 0x22 || u == 0x5c) { + out += "\\"; + switch (u) { + case 0x22: + out += "\""; + break; + case 0x5c: + out += "\\"; + break; + case 0x8: + out += "b"; + break; + case 0xc: + out += "f"; + break; + case 0xa: + out += "n"; + break; + case 0xd: + out += "r"; + break; + case 0x9: + out += "t"; + break; + default: + out += "u"; + out += "0"; + out += "0"; + String hex = Integer.toHexString(u); + if (hex.length() == 1) + out += "0"; + out += hex; + break; + } + } else { + out += s.substring(i, i + Character.charCount(u)); + } + } else { + out += s.substring(i, i + Character.charCount(u)); + } + + i += Character.charCount(u); + } + + return out; + } + + private static void writeString(PrintStream printWriter, String s) { + printWriter.print("\""); + printWriter.print(escapeString(s)); + printWriter.print("\""); + } + + private static void writeIndent(PrintStream printWriter, int level) { + for (int i = 0; i < level * TAB_WIDTH; ++i) { + printWriter.print(" "); + } + } + + private static void writeArtifact(Artifact artifact, + PrintStream printWriter, int indentLevel, Boolean comma) { + writeIndent(printWriter, indentLevel++); + printWriter.print("{\n"); + writeIndent(printWriter, indentLevel); + writeString(printWriter, "filePath"); + printWriter.print(": "); + writeString(printWriter, artifact.getFilePath()); + printWriter.println(","); + writeIndent(printWriter, indentLevel); + writeString(printWriter, "fileTags"); + printWriter.print(": ["); + for (int i = 0; i < artifact.getFileTags().size(); ++i) { + writeString(printWriter, artifact.getFileTags().get(i)); + if (i != artifact.getFileTags().size() - 1) + printWriter.print(", "); + } + printWriter.println("]"); + writeIndent(printWriter, --indentLevel); + printWriter.println("}" + (comma ? "," : "")); + } + + @Override + public void write(List artifacts, OutputStream outputStream) + throws IOException { + PrintStream printWriter = new PrintStream(outputStream); + printWriter.print("[\n"); + for (int i = 0; i < artifacts.size(); ++i) { + writeArtifact(artifacts.get(i), printWriter, 1, + i != artifacts.size() - 1); + } + + printWriter.println("]"); + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/ArtifactListTextWriter.java b/share/qbs/modules/java/io/qt/qbs/ArtifactListTextWriter.java new file mode 100644 index 00000000..df4e8b60 --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/ArtifactListTextWriter.java @@ -0,0 +1,60 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 Jake Petroules. + ** Contact: http://www.qt.io/licensing + ** + ** This file is part of Qbs. + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.List; + +public class ArtifactListTextWriter implements ArtifactListWriter { + private String join(List list, String separator) { + String string = ""; + if (!list.isEmpty()) { + string += list.get(0); + for (int i = 1; i < list.size(); ++i) { + string += separator + list; + } + } + + return string; + } + + @Override + public void write(List artifacts, OutputStream outputStream) + throws IOException { + PrintStream stream = new PrintStream(outputStream); + for (Artifact artifact : artifacts) { + stream.format("%s [%s]\n", artifact.getFilePath(), + join(artifact.getFileTags(), ", ")); + } + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/ArtifactListWriter.java b/share/qbs/modules/java/io/qt/qbs/ArtifactListWriter.java new file mode 100644 index 00000000..2eb9ba2f --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/ArtifactListWriter.java @@ -0,0 +1,40 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 Jake Petroules. + ** Contact: http://www.qt.io/licensing + ** + ** This file is part of Qbs. + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +public interface ArtifactListWriter { + public abstract void write(List artifacts, + OutputStream outputStream) throws IOException; +} diff --git a/share/qbs/modules/java/io/qt/qbs/ArtifactListXmlWriter.java b/share/qbs/modules/java/io/qt/qbs/ArtifactListXmlWriter.java new file mode 100644 index 00000000..6646612a --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/ArtifactListXmlWriter.java @@ -0,0 +1,98 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 Jake Petroules. + ** Contact: http://www.qt.io/licensing + ** + ** This file is part of Qbs. + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class ArtifactListXmlWriter implements ArtifactListWriter { + @Override + public void write(List artifacts, OutputStream outputStream) + throws IOException { + try { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory + .newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory + .newDocumentBuilder(); + + Document document = documentBuilder.newDocument(); + Element rootElement = document.createElement("artifacts"); + document.appendChild(rootElement); + + for (Artifact artifact : artifacts) { + Element artifactElement = document.createElement("artifact"); + rootElement.appendChild(artifactElement); + + Element filePathElement = document.createElement("filePath"); + artifactElement.appendChild(filePathElement); + filePathElement.appendChild(document.createTextNode(artifact + .getFilePath())); + + Element fileTagsElement = document.createElement("fileTags"); + artifactElement.appendChild(fileTagsElement); + + for (String fileTag : artifact.getFileTags()) { + Element fileTagElement = document.createElement("fileTag"); + fileTagsElement.appendChild(fileTagElement); + fileTagElement + .appendChild(document.createTextNode(fileTag)); + } + } + + TransformerFactory transformerFactory = TransformerFactory + .newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(document); + StreamResult result = new StreamResult(outputStream); + + transformer.transform(source, result); + new PrintStream(outputStream).println(); + } catch (ParserConfigurationException pce) { + throw new IOException(pce); + } catch (TransformerException tfe) { + throw new IOException(tfe); + } + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/tools/JavaCompilerScannerTool.java b/share/qbs/modules/java/io/qt/qbs/tools/JavaCompilerScannerTool.java new file mode 100644 index 00000000..e9770f65 --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/tools/JavaCompilerScannerTool.java @@ -0,0 +1,66 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 Jake Petroules. + ** Contact: http://www.qt.io/licensing + ** + ** This file is part of Qbs. + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs.tools; + +import io.qt.qbs.tools.utils.JavaCompilerScanner; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class JavaCompilerScannerTool { + public static void main(String[] args) { + JavaCompilerScanner scanner = new JavaCompilerScanner(); + + List compilerArguments = new ArrayList( + Arrays.asList(args)); + if (args.length >= 1 && args[0].equals("--output-format")) { + compilerArguments.remove(0); + if (args.length < 2) { + throw new IllegalArgumentException( + "--output-format requires an argument"); + } + + scanner.setOutputFormat(args[1]); + compilerArguments.remove(0); + } + + try { + int result = scanner.run(compilerArguments); + scanner.write(System.out); + System.exit(result); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/tools/utils/ArtifactProcessor.java b/share/qbs/modules/java/io/qt/qbs/tools/utils/ArtifactProcessor.java new file mode 100644 index 00000000..30ba7380 --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/tools/utils/ArtifactProcessor.java @@ -0,0 +1,74 @@ +/**************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: http://www.qt.io/licensing + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs.tools.utils; + +import com.sun.source.util.Trees; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import java.util.Set; + +@SupportedAnnotationTypes("*") +public class ArtifactProcessor extends AbstractProcessor { + private final ArtifactScanner treeScanner; + private Trees trees; + + public ArtifactProcessor(final ArtifactScanner treeScanner) { + this.treeScanner = treeScanner; + } + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + trees = Trees.instance(processingEnv); + } + + @Override + public boolean process(final Set types, final RoundEnvironment environment) { + treeScanner.init(processingEnv); + if (!environment.processingOver()) { + for (final Element element : environment.getRootElements()) { + treeScanner.scan(trees.getPath(element), trees); + } + } + return true; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return treeScanner.getSourceVersion(); + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/tools/utils/ArtifactScanner.java b/share/qbs/modules/java/io/qt/qbs/tools/utils/ArtifactScanner.java new file mode 100644 index 00000000..eb8e736c --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/tools/utils/ArtifactScanner.java @@ -0,0 +1,126 @@ +/**************************************************************************** + ** + ** Copyright (C) 2016 The Qt Company Ltd. + ** Contact: http://www.qt.io/licensing + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs.tools.utils; + +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.*; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public class ArtifactScanner extends TreePathScanner { + private ProcessingEnvironment processingEnv; + private SourceVersion sourceVersion; + private Set binaryNames = new HashSet(); + private Set nativeHeaderBinaryNames = new HashSet(); + + public ArtifactScanner(SourceVersion sourceVersion) { + this.sourceVersion = sourceVersion; + } + + public synchronized void init(ProcessingEnvironment processingEnv) { + this.processingEnv = processingEnv; + } + + public SourceVersion getSourceVersion() { + return sourceVersion; + } + + public Set getBinaryNames() { + return binaryNames; + } + + public Set getNativeHeaderBinaryNames() { + return nativeHeaderBinaryNames; + } + + private static TypeElement getEnclosingTypeElement(final Element element) { + Element typeElement = element; + while (!(typeElement instanceof TypeElement)) + typeElement = typeElement.getEnclosingElement(); + return (TypeElement) typeElement; + } + + private static boolean isNativeHeaderFileGenerationSupported() { + // This appears to depend on the actual JDK version, NOT the -source version given. + return SourceVersion.latest().ordinal() >= 8; + } + + @Override + public Void visitClass(ClassTree node, Trees trees) { + onVisitType((TypeElement)trees.getElement(getCurrentPath())); + return super.visitClass(node, trees); + } + + @Override + public Void visitMethod(MethodTree node, Trees trees) { + onVisitExecutable((ExecutableElement)trees.getElement(getCurrentPath())); + return super.visitMethod(node, trees); + } + + @Override + public Void visitVariable(VariableTree node, Trees trees) { + onVisitVariable((VariableElement)trees.getElement(getCurrentPath())); + return super.visitVariable(node, trees); + } + + public void onVisitType(TypeElement type) { + binaryNames.add(processingEnv.getElementUtils().getBinaryName(type).toString()); + } + + public void onVisitExecutable(ExecutableElement element) { + if (isNativeHeaderFileGenerationSupported() && element.getModifiers().contains(javax.lang.model.element.Modifier.NATIVE)) { + nativeHeaderBinaryNames.add(processingEnv.getElementUtils().getBinaryName(getEnclosingTypeElement(element)).toString()); + } + } + + public void onVisitVariable(VariableElement variable) { + // Java 8: a native header is generated for a Java source file if it has either native methods, + // or constant fields annotated with java.lang.annotation.Native (JDK-7150368). + if (isNativeHeaderFileGenerationSupported()) { + List annotations = variable.getAnnotationMirrors(); + for (Iterator it = annotations.iterator(); it.hasNext(); ) { + // We could do variable.getAnnotation(java.lang.annotation.Native.class) != null, + // but the helper tool must compile with older JDK versions. + if (it.next().getAnnotationType().toString().equals("java.lang.annotation.Native")) + nativeHeaderBinaryNames.add(processingEnv.getElementUtils().getBinaryName(getEnclosingTypeElement(variable)).toString()); + } + } + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/tools/utils/JavaCompilerOptions.java b/share/qbs/modules/java/io/qt/qbs/tools/utils/JavaCompilerOptions.java new file mode 100644 index 00000000..77bc1bf5 --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/tools/utils/JavaCompilerOptions.java @@ -0,0 +1,154 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 Jake Petroules. + ** Contact: http://www.qt.io/licensing + ** + ** This file is part of Qbs. + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs.tools.utils; + +import javax.lang.model.SourceVersion; +import javax.tools.JavaCompiler; +import javax.tools.StandardJavaFileManager; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class JavaCompilerOptions { + private final List recognizedOptions; + private final List classNames; + private final List files; + private String classFilesDir; + private String headerFilesDir; + private SourceVersion sourceVersion; + + public String getClassFilesDir() { + return classFilesDir; + } + + public String getHeaderFilesDir() { + return headerFilesDir; + } + + private JavaCompilerOptions(List recognizedOptions, List classNames, List files, + String classFilesDir, String headerFilesDir, SourceVersion sourceVersion) { + this.recognizedOptions = recognizedOptions; + this.classNames = classNames; + this.files = files; + this.classFilesDir = classFilesDir; + this.headerFilesDir = headerFilesDir; + this.sourceVersion = sourceVersion; + } + + public static JavaCompilerOptions parse(JavaCompiler compiler, + StandardJavaFileManager fileManager, String... arguments) { + List recognizedOptions = new ArrayList(); + List classNames = new ArrayList(); + List files = new ArrayList(); + String classFilesDir = null; + String headerFilesDir = null; + String sourceVersionString = null; + int sourceVersion = SourceVersion.latest().ordinal(); + + for (int i = 0; i < arguments.length; ++i) { + int argumentCount = compiler.isSupportedOption(arguments[i]); + if (argumentCount < 0) + argumentCount = fileManager.isSupportedOption(arguments[i]); + + if (argumentCount >= 0) { + if (arguments[i].equals("-d") && argumentCount == 1) + classFilesDir = arguments[i + 1]; + + if (arguments[i].equals("-h") && argumentCount == 1) + headerFilesDir = arguments[i + 1]; + + if (arguments[i].equals("-source") && argumentCount == 1) { + String a = arguments[i + 1]; + if (a.equals("1.0")) + sourceVersion = 0; + else if (a.equals("1.1")) + sourceVersion = 1; + else if (a.equals("1.2")) + sourceVersion = 2; + else if (a.equals("1.3")) + sourceVersion = 3; + else if (a.equals("1.4")) + sourceVersion = 4; + else if (a.equals("1.5") || a.equals("5")) + sourceVersion = 5; + else if (a.equals("1.6") || a.equals("6")) + sourceVersion = 6; + else if (a.equals("1.7") || a.equals("7")) + sourceVersion = 7; + else if (a.equals("1.8") || a.equals("8")) + sourceVersion = 8; + sourceVersionString = a; + } + + for (int j = 0; j < argumentCount + 1; ++j) { + if (i + j >= arguments.length) { + throw new IllegalArgumentException(arguments[i]); + } + + recognizedOptions.add(arguments[i + j]); + } + + i += argumentCount; + } else { + File file = new File(arguments[i]); + if (file.exists()) + files.add(file); + else if (SourceVersion.isName(arguments[i])) + classNames.add(arguments[i]); + else + throw new IllegalArgumentException(arguments[i]); + } + } + + if (sourceVersion >= SourceVersion.values().length) + throw new RuntimeException("invalid source release: " + sourceVersionString); + + return new JavaCompilerOptions(recognizedOptions, classNames, files, classFilesDir, headerFilesDir, + SourceVersion.values()[sourceVersion]); + } + + public List getRecognizedOptions() { + return Collections.unmodifiableList(recognizedOptions); + } + + public List getFiles() { + return Collections.unmodifiableList(files); + } + + public List getClassNames() { + return Collections.unmodifiableList(classNames); + } + + public SourceVersion getSourceVersion() { + return sourceVersion; + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/tools/utils/JavaCompilerScanner.java b/share/qbs/modules/java/io/qt/qbs/tools/utils/JavaCompilerScanner.java new file mode 100644 index 00000000..1be804ee --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/tools/utils/JavaCompilerScanner.java @@ -0,0 +1,194 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 Jake Petroules. + ** Contact: http://www.qt.io/licensing + ** + ** This file is part of Qbs. + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs.tools.utils; + +import io.qt.qbs.*; + +import javax.tools.*; +import javax.tools.JavaCompiler.CompilationTask; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.*; + +public class JavaCompilerScanner { + private static final String humanReadableTextFormat = "human-readable-text"; + private static final String jsonFormat = "json"; + private static final String xmlFormat = "xml1"; + + private Set compilationOutputFilePaths = new HashSet(); + private Set parsedOutputFilePaths = new HashSet(); + private List artifacts = new ArrayList(); + private String outputFormat = humanReadableTextFormat; + + private static Map getOutputFormatters() { + Map outputFormatters = new HashMap(); + outputFormatters.put(humanReadableTextFormat, + new ArtifactListTextWriter()); + outputFormatters.put(jsonFormat, new ArtifactListJsonWriter()); + outputFormatters.put(xmlFormat, new ArtifactListXmlWriter()); + return outputFormatters; + } + + public String getOutputFormat() { + return this.outputFormat; + } + + public void setOutputFormat(String outputFormat) { + this.outputFormat = outputFormat; + } + + public List getArtifacts() { + return this.artifacts; + } + + private void addArtifact(Artifact a) { + for (int i = 0; i < this.artifacts.size(); ++i) { + if (this.artifacts.get(i).getFilePath().equals(a.getFilePath())) { + return; + } + } + + this.artifacts.add(a); + this.parsedOutputFilePaths.add(a.getFilePath()); + } + + public int run(List compilerArguments) { + artifacts.clear(); + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager standardFileManager = compiler + .getStandardFileManager(null, null, null); + JavaFileManager fileManager = new ForwardingJavaFileManager( + standardFileManager) { + @Override + public JavaFileObject getJavaFileForOutput( + JavaFileManager.Location location, String className, + JavaFileObject.Kind kind, FileObject sibling) + throws IOException { + JavaFileObject o = super.getJavaFileForOutput(location, + className, kind, sibling); + compilationOutputFilePaths.add(new File(o.toUri().getSchemeSpecificPart()).toString()); + return new NullFileObject(o); + } + + @Override + public FileObject getFileForOutput( + JavaFileManager.Location location, String packageName, + String relativeName, FileObject sibling) throws IOException { + FileObject o = super.getFileForOutput(location, packageName, + relativeName, sibling); + compilationOutputFilePaths.add(new File(o.toUri().getSchemeSpecificPart()).toString()); + return new NullFileObject(o); + } + }; + + final JavaCompilerOptions options = JavaCompilerOptions + .parse(compiler, standardFileManager, compilerArguments + .toArray(new String[compilerArguments.size()])); + + final ArtifactScanner scanner = new ArtifactScanner(options.getSourceVersion()); + final ArtifactProcessor processor = new ArtifactProcessor(scanner); + + final CompilationTask task = compiler.getTask( + null, + fileManager, + null, + options.getRecognizedOptions(), + options.getClassNames(), + standardFileManager.getJavaFileObjectsFromFiles(options + .getFiles())); + task.setProcessors(Arrays.asList(processor)); + task.call(); // exit code is not relevant, we have to ignore compilation errors sometimes + + final Set binaryNames = scanner.getBinaryNames(); + final Iterator it = binaryNames.iterator(); + while (it.hasNext()) { + final String className = it.next(); + Artifact a = new Artifact(options.getClassFilesDir().replace('/', File.separatorChar) + + File.separatorChar + className.replace('.', File.separatorChar) + ".class"); + a.addFileTag("java.class"); + addArtifact(a); + } + + final Set nativeHeaderBinaryNames = scanner.getNativeHeaderBinaryNames(); + final Iterator it2 = nativeHeaderBinaryNames.iterator(); + while (it2.hasNext()) { + final String className = it2.next(); + Artifact a = new Artifact(options.getHeaderFilesDir().replace('/', File.separatorChar) + + File.separatorChar + className.replace('.', '_').replace('$', '_') + ".h"); + a.addFileTag("hpp"); + addArtifact(a); + } + + // We can't simply compare both lists for equality because if compilation stopped due to an error, + // only a partial list of artifacts may have been generated so far. If the parsed outputs contains file + // paths it should not, qbs' --check-outputs option should catch that. + if (!parsedOutputFilePaths.containsAll(compilationOutputFilePaths)) { + ArrayList parsedOutputFilePathsArray = new ArrayList(parsedOutputFilePaths); + Collections.sort(parsedOutputFilePathsArray); + ArrayList compilationOutputFilePathsArray = new ArrayList(compilationOutputFilePaths); + Collections.sort(compilationOutputFilePathsArray); + compilationOutputFilePaths.removeAll(parsedOutputFilePaths); + ArrayList differenceArray = new ArrayList(compilationOutputFilePaths); + Collections.sort(differenceArray); + + System.err.println("The set of output files determined by source code parsing:\n\n" + + join("\n", parsedOutputFilePathsArray) + "\n\n" + + "is missing some files from the list that would be produced by the compiler:\n\n" + + join("\n", compilationOutputFilePathsArray) + "\n\n" + + "The missing files are:\n\n" + + join("\n", differenceArray) + "\n\n" + + "Compilation will still continue, though a build error *might* appear later;\n" + + "please check the bug report at https://bugreports.qt.io/browse/QBS-1069\n"); + } + + return !parsedOutputFilePaths.isEmpty() ? 0 : 1; + } + + public void write(OutputStream outputStream) throws IOException { + getOutputFormatters().get(outputFormat).write(artifacts, outputStream); + } + + // Java 8 has String.join but the helper tool must build with Java 6. + private static String join(CharSequence delimiter, Iterable elements) { + StringBuilder sb = new StringBuilder(); + for (Iterator it = elements.iterator(); it.hasNext(); ) { + sb.append(it.next()); + sb.append(delimiter); + } + + // Remove the last delimiter + if (sb.length() > 0) + sb.replace(sb.length() - delimiter.length(), sb.length(), ""); + + return sb.toString(); + } +} diff --git a/share/qbs/modules/java/io/qt/qbs/tools/utils/NullFileObject.java b/share/qbs/modules/java/io/qt/qbs/tools/utils/NullFileObject.java new file mode 100644 index 00000000..d74a69fb --- /dev/null +++ b/share/qbs/modules/java/io/qt/qbs/tools/utils/NullFileObject.java @@ -0,0 +1,147 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 Jake Petroules. + ** Contact: http://www.qt.io/licensing + ** + ** This file is part of Qbs. + ** + ** 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 The Qt Company. For licensing terms and + ** conditions see http://www.qt.io/terms-conditions. For further information + ** use the contact form at http://www.qt.io/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 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ****************************************************************************/ + +package io.qt.qbs.tools.utils; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; +import javax.tools.FileObject; +import javax.tools.JavaFileObject; +import java.io.*; +import java.net.URI; + +/** + * Represents a FileObject that discards its output when written. + */ +public class NullFileObject implements FileObject, JavaFileObject { + FileObject obj; + + public NullFileObject(FileObject obj) { + this.obj = obj; + } + + @Override + public URI toUri() { + return obj.toUri(); + } + + @Override + public String getName() { + return obj.getName(); + } + + @Override + public InputStream openInputStream() throws IOException { + return obj.openInputStream(); + } + + @Override + public OutputStream openOutputStream() throws IOException { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + } + }; + } + + @Override + public Reader openReader(boolean ignoreEncodingErrors) throws IOException { + return obj.openReader(ignoreEncodingErrors); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) + throws IOException { + return obj.getCharContent(ignoreEncodingErrors); + } + + @Override + public Writer openWriter() throws IOException { + return new Writer() { + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + } + + @Override + public void flush() throws IOException { + } + + @Override + public void close() throws IOException { + } + }; + } + + @Override + public long getLastModified() { + return obj.getLastModified(); + } + + @Override + public boolean delete() { + return true; + } + + @Override + public Kind getKind() { + if (obj instanceof JavaFileObject) { + return ((JavaFileObject) obj).getKind(); + } + + throw new UnsupportedOperationException(); + } + + @Override + public boolean isNameCompatible(String simpleName, Kind kind) { + if (obj instanceof JavaFileObject) { + return ((JavaFileObject) obj).isNameCompatible(simpleName, kind); + } + + throw new UnsupportedOperationException(); + } + + @Override + public NestingKind getNestingKind() { + if (obj instanceof JavaFileObject) { + return ((JavaFileObject) obj).getNestingKind(); + } + + throw new UnsupportedOperationException(); + } + + @Override + public Modifier getAccessLevel() { + if (obj instanceof JavaFileObject) { + return ((JavaFileObject) obj).getAccessLevel(); + } + + throw new UnsupportedOperationException(); + } +} diff --git a/share/qbs/modules/java/utils.js b/share/qbs/modules/java/utils.js new file mode 100644 index 00000000..632fc810 --- /dev/null +++ b/share/qbs/modules/java/utils.js @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var File = loadExtension("qbs.File"); +var FileInfo = loadExtension("qbs.FileInfo"); +var ModUtils = loadExtension("qbs.ModUtils"); +var Process = loadExtension("qbs.Process"); +var TextFile = loadExtension("qbs.TextFile"); +var Utilities = loadExtension("qbs.Utilities"); +var WindowsUtils = loadExtension("qbs.WindowsUtils"); + +function is64bitProcess() { + var y = jdkRootRegistryKey(true); + var n = jdkRootRegistryKey(false); + y = Utilities.getNativeSetting(y + "\\" + Utilities.getNativeSetting(y, "CurrentVersion"), "JavaHome"); + n = Utilities.getNativeSetting(n + "\\" + Utilities.getNativeSetting(n, "CurrentVersion"), "JavaHome"); + return y !== n; +} + +function useWow64Key(arch) { + var wow64 = false; + switch (arch) { + case "x86_64": + case "ia64": + // QTBUG-3845 + if (!is64bitProcess()) + return undefined; + break; + case "x86": + case "armv7": + wow64 = is64bitProcess(); + break; + } + return wow64; +} + +function jdkRootRegistryKey(wow64) { + // If an architecture is specified, search the appropriate key for that architecture, + // on this version of Windows (i.e. WOW64 or not) if compatible, + // otherwise get both keys since any JDK will be usable + if (wow64 === undefined) + return undefined; + return FileInfo.toWindowsSeparators(FileInfo.joinPaths("HKEY_LOCAL_MACHINE", "SOFTWARE", + (wow64 ? "Wow6432Node" : undefined), + "JavaSoft", "Java Development Kit")); +} + +function findJdkPath(hostOS, arch, environmentPaths, searchPaths) { + var i; + for (var key in environmentPaths) { + if (environmentPaths[key]) { + return environmentPaths[key]; + } + } + + if (hostOS.contains("windows")) { + var rootKey = jdkRootRegistryKey(useWow64Key(arch)); + if (rootKey) { + var current = Utilities.getNativeSetting(rootKey, "CurrentVersion"); // 1.8 etc. + if (current) { + var home = Utilities.getNativeSetting([rootKey, current].join("\\"), "JavaHome"); + if (home) { + return home; + } + } + } + + return undefined; + } + + if (hostOS.contains("macos")) { + var p = new Process(); + try { + // We filter by architecture here so that we'll get a compatible JVM for JNI use. + var args = []; + if (arch) { + args.push("--arch", arch === "x86" ? "i386" : arch); + } + + // --failfast doesn't print the default JVM if nothing matches the filter(s). + var status = p.exec("/usr/libexec/java_home", args.concat(["--failfast"])); + return status === 0 ? p.readStdOut().trim() : undefined; + } finally { + p.close(); + } + } + + if (hostOS.contains("unix")) { + var requiredTools = ["javac", "java", "jar"]; + for (i = 0; i < searchPaths.length; ++i) { + function fullToolPath(tool) { + return FileInfo.joinPaths(searchPaths[i], "bin", tool); + } + + if (requiredTools.map(function(p) { return fullToolPath(p); }) + .every(function(p) { return File.exists(p); })) { + return searchPaths[i]; + } + } + + return undefined; + } +} + +function findJdkVersion(compilerFilePath) { + var p = new Process(); + try { + p.exec(compilerFilePath, ["-version"]); + var re = /^javac (([0-9]+(?:\.[0-9]+){2,2})_([0-9]+))$/m; + var match = p.readStdErr().trim().match(re); + if (match !== null) + return match; + } finally { + p.close(); + } +} + +function supportsGeneratedNativeHeaderFiles(product) { + var compilerVersionMajor = ModUtils.moduleProperty(product, "compilerVersionMajor"); + if (compilerVersionMajor === 1) { + if (ModUtils.moduleProperty(product, "compilerVersionMinor") >= 8) { + return true; + } + } + + return compilerVersionMajor > 1; +} + +function javacArguments(product, inputs, overrides) { + function getModuleProperty(product, propertyName, overrides) { + if (overrides && overrides[propertyName]) + return overrides[propertyName]; + return ModUtils.moduleProperty(product, propertyName); + } + + function getModuleProperties(product, propertyName, overrides) { + if (overrides && overrides[propertyName]) + return overrides[propertyName]; + return ModUtils.moduleProperty(product, propertyName); + } + + var i; + var outputDir = getModuleProperty(product, "classFilesDir", overrides); + var classPaths = [outputDir]; + var additionalClassPaths = getModuleProperties(product, "additionalClassPaths", overrides); + if (additionalClassPaths) + classPaths = classPaths.concat(additionalClassPaths); + for (i in inputs["java.jar"]) + classPaths.push(inputs["java.jar"][i].filePath); + var debugArg = product.moduleProperty("qbs", "buildVariant") === "debug" + ? "-g" : "-g:none"; + var pathListSeparator = product.moduleProperty("qbs", "pathListSeparator"); + var args = [ + "-classpath", classPaths.join(pathListSeparator), + "-s", product.buildDirectory, + debugArg, "-d", outputDir + ]; + if (supportsGeneratedNativeHeaderFiles(product)) + args.push("-h", product.buildDirectory); + var runtimeVersion = getModuleProperty(product, "runtimeVersion", overrides); + if (runtimeVersion) + args.push("-target", runtimeVersion); + var languageVersion = getModuleProperty(product, "languageVersion", overrides); + if (languageVersion) + args.push("-source", languageVersion); + var bootClassPaths = getModuleProperties(product, "bootClassPaths", overrides); + if (bootClassPaths && bootClassPaths.length > 0) + args.push("-bootclasspath", bootClassPaths.join(pathListSeparator)); + if (!getModuleProperty(product, "enableWarnings", overrides)) + args.push("-nowarn"); + if (getModuleProperty(product, "warningsAsErrors", overrides)) + args.push("-Werror"); + var otherFlags = getModuleProperties(product, "additionalCompilerFlags", overrides); + if (otherFlags) + args = args.concat(otherFlags); + for (i in inputs["java.java"]) + args.push(inputs["java.java"][i].filePath); + for (i in inputs["java.java-internal"]) + args.push(inputs["java.java-internal"][i].filePath); + return args; +} + +/** + * Returns a list of fully qualified Java class names for the compiler helper tool. + * + * @param type @c java to return names of sources, @c to return names of compiled classes + */ +function helperFullyQualifiedNames(type) { + var names = [ + "io/qt/qbs/Artifact", + "io/qt/qbs/ArtifactListJsonWriter", + "io/qt/qbs/ArtifactListTextWriter", + "io/qt/qbs/ArtifactListWriter", + "io/qt/qbs/ArtifactListXmlWriter", + "io/qt/qbs/tools/JavaCompilerScannerTool", + "io/qt/qbs/tools/utils/ArtifactProcessor", + "io/qt/qbs/tools/utils/ArtifactScanner", + "io/qt/qbs/tools/utils/JavaCompilerOptions", + "io/qt/qbs/tools/utils/JavaCompilerScanner", + "io/qt/qbs/tools/utils/JavaCompilerScanner$1", + "io/qt/qbs/tools/utils/NullFileObject", + "io/qt/qbs/tools/utils/NullFileObject$1", + "io/qt/qbs/tools/utils/NullFileObject$2", + ]; + if (type === "java") { + return names.filter(function (name) { + return !name.contains("$"); + }); + } else if (type === "class") { + return names; + } +} + +function helperOutputArtifacts(product) { + File.makePath(ModUtils.moduleProperty(product, "internalClassFilesDir")); + return helperFullyQualifiedNames("class").map(function (name) { + return { + filePath: FileInfo.joinPaths(ModUtils.moduleProperty(product, "internalClassFilesDir"), + name + ".class"), + fileTags: ["java.class-internal"] + }; + }); +} + +function helperOverrideArgs(product, tool) { + var overrides = {}; + if (tool === "javac") { + // Build the helper tool with the same source and target version as the JDK it's being + // compiled with. Both are irrelevant here since the resulting tool will only be run + // with the same JDK as it was built with, and we know in advance the source is + // compatible with all Java language versions from 1.6 and above. + var jdkVersion = [ModUtils.moduleProperty(product, "compilerVersionMajor"), + ModUtils.moduleProperty(product, "compilerVersionMinor")].join("."); + overrides["languageVersion"] = jdkVersion; + overrides["runtimeVersion"] = jdkVersion; + + // Build the helper tool's class files separately from the actual product's class files + overrides["classFilesDir"] = ModUtils.moduleProperty(product, "internalClassFilesDir"); + + // Add tools.jar to the classpath as required for the tree scanner API on JDK 7+ + var toolsJarPath = ModUtils.moduleProperty(product, "toolsJarPath"); + if (toolsJarPath) + overrides["additionalClassPaths"] = [toolsJarPath].concat( + ModUtils.moduleProperty(product, "additionalClassPaths")); + } + + // Inject the current JDK's runtime classes into the boot class path when building/running the + // dependency scanner. This is normally not necessary but is important for Android platforms + // where android.jar is the only JAR on the boot classpath and JSR 199 is unavailable. + overrides["bootClassPaths"] = [ModUtils.moduleProperty(product, "runtimeJarPath")].concat( + ModUtils.moduleProperty(product, "bootClassPaths")); + return overrides; +} + +function outputArtifacts(product, inputs) { + // Handle the case where a product depends on Java but has no Java sources + if (!inputs["java.java"] || inputs["java.java"].length === 0) + return []; + + // We need to ensure that the output directory is created first, because the Java compiler + // internally checks that it is present before performing any actions + File.makePath(ModUtils.moduleProperty(product, "classFilesDir")); + + var process; + try { + process = new Process(); + process.setWorkingDirectory( + FileInfo.joinPaths(ModUtils.moduleProperty(product, "internalClassFilesDir"))); + + var sep = product.moduleProperty("qbs", "pathListSeparator"); + var toolsJarPath = ModUtils.moduleProperty(product, "toolsJarPath"); + var javaArgs = [ + "-classpath", process.workingDirectory() + (toolsJarPath ? (sep + toolsJarPath) : ""), + "io/qt/qbs/tools/JavaCompilerScannerTool", + "--output-format", "json", + ]; + process.exec(ModUtils.moduleProperty(product, "interpreterFilePath"), javaArgs + .concat(javacArguments(product, inputs, helperOverrideArgs(product))), true); + var out = JSON.parse(process.readStdOut()); + console.error(process.readStdErr()); + return out; + } finally { + if (process) + process.close(); + } +} + +function manifestContents(filePath) { + if (filePath === undefined) + return undefined; + + var contents, file; + try { + file = new TextFile(filePath); + contents = file.readAll(); + } finally { + if (file) { + file.close(); + } + } + + if (contents) { + var dict = {}; + var lines = contents.split(/\r?\n/g); + for (var i in lines) { + var kv = lines[i].split(":"); + if (kv.length !== 2) + return undefined; + dict[kv[0]] = kv[1]; + } + + return dict; + } +} diff --git a/share/qbs/modules/lex_yacc/lexyacc.js b/share/qbs/modules/lex_yacc/lexyacc.js new file mode 100644 index 00000000..c30e4fdd --- /dev/null +++ b/share/qbs/modules/lex_yacc/lexyacc.js @@ -0,0 +1,17 @@ +var FileInfo = loadExtension("qbs.FileInfo"); + +function outputFilePath(product, input, posixFileName, forYacc) +{ + var outDir = product.moduleProperty("lex_yacc", "outputDir"); + var fileName; + if (product.moduleProperty("lex_yacc", "uniqueSymbolPrefix")) { + fileName = input.baseName; + if (forYacc) + fileName += posixFileName.slice(1); + else + fileName += posixFileName; + } else { + fileName = posixFileName; + } + return FileInfo.joinPaths(outDir, fileName); +} diff --git a/share/qbs/modules/lex_yacc/lexyacc.qbs b/share/qbs/modules/lex_yacc/lexyacc.qbs new file mode 100644 index 00000000..178a04fb --- /dev/null +++ b/share/qbs/modules/lex_yacc/lexyacc.qbs @@ -0,0 +1,70 @@ +import qbs +import "lexyacc.js" as HelperFunctions + +Module { + Depends { name: "cpp" } + + property string lexBinary: "lex" + property string yaccBinary: "yacc" + property string outputTag: "c" + property bool uniqueSymbolPrefix: false + property stringList lexFlags: [] + property stringList yaccFlags: [] + + readonly property string outputDir: product.buildDirectory + "/lexyacc" + + Rule { + inputs: ["lex.input"] + Artifact { + filePath: HelperFunctions.outputFilePath(product, input, "lex.yy.c", false) + fileTags: [product.moduleProperty("lex_yacc", "outputTag")] + cpp.includePaths: (product.moduleProperty("cpp", "includePaths") || []) + .concat([product.moduleProperty("lex_yacc", "outputDir")]) + } + prepare: { + var args = product.moduleProperty("lex_yacc", "lexFlags"); + if (product.moduleProperty("lex_yacc", "uniqueSymbolPrefix")) + args.push("-P", input.baseName, "-o", output.filePath); + args.push(input.filePath); + var cmd = new Command(product.moduleProperty("lex_yacc", "lexBinary"), args); + cmd.workingDirectory = product.moduleProperty("lex_yacc", "outputDir"); + cmd.description = "generating " + output.fileName; + return [cmd]; + } + } + + Rule { + inputs: ["yacc.input"] + Artifact { + filePath: HelperFunctions.outputFilePath(product, input, "y.tab.c", true) + fileTags: [product.moduleProperty("lex_yacc", "outputTag")] + } + Artifact { + filePath: HelperFunctions.outputFilePath(product, input, "y.tab.h", true) + fileTags: ["hpp"] + } + prepare: { + var args = product.moduleProperty("lex_yacc", "yaccFlags"); + args.push("-d"); + if (product.moduleProperty("lex_yacc", "uniqueSymbolPrefix")) { + args.push("-b", input.baseName, "-p", input.baseName); + } + args.push(input.filePath); + var cmd = new Command(product.moduleProperty("lex_yacc", "yaccBinary"), args); + cmd.workingDirectory = product.moduleProperty("lex_yacc", "outputDir"); + cmd.description = "generating " + + outputs[product.moduleProperty("lex_yacc", "outputTag")][0].fileName + + " and " + outputs["hpp"][0].fileName; + return [cmd]; + } + } + + FileTagger { + patterns: ["*.l"] + fileTags: ["lex.input"]; + } + FileTagger { + patterns: ["*.y"] + fileTags: ["yacc.input"]; + } +} diff --git a/share/qbs/modules/nodejs/NodeJS.qbs b/share/qbs/modules/nodejs/NodeJS.qbs new file mode 100644 index 00000000..933489ce --- /dev/null +++ b/share/qbs/modules/nodejs/NodeJS.qbs @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.Environment +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes + +Module { + // JavaScript files which have been "processed" - currently this simply means "copied to output + // directory" but might later include minification and obfuscation processing + additionalProductTypes: ["nodejs_processed_js"].concat(applicationFile ? ["application"] : []) + + Probes.NodeJsProbe { + id: nodejs + pathPrefixes: toolchainInstallPath ? [toolchainInstallPath] : [] + } + + Probes.NpmProbe { + id: npm + pathPrefixes: toolchainInstallPath ? [toolchainInstallPath] : [] + } + + property path applicationFile + PropertyOptions { + name: "applicationFile" + description: "file whose corresponding output will be executed when running the Node.js app" + } + + Group { name: "Application file"; files: nodejs.applicationFile ? [nodejs.applicationFile] : [] } + + property path toolchainInstallPath: { + if (nodejs.path && npm.path && nodejs.path !== npm.path) + throw("node and npm binaries do not belong to the same installation (" + + nodejs.path + " vs " + npm.path + ")"); + return nodejs.path || npm.path; + } + + property path interpreterFileName: nodejs.fileName + property path interpreterFilePath: nodejs.filePath + + property path packageManagerFileName: npm.fileName + property path packageManagerFilePath: npm.filePath + + property path packageManagerBinPath: npm.npmBin + property path packageManagerRootPath: npm.npmRoot + property path packageManagerPrefixPath: npm.npmPrefix + + // private properties + readonly property path compiledIntermediateDir: FileInfo.joinPaths(product.buildDirectory, + "tmp", "nodejs.intermediate") + + setupBuildEnvironment: { + var v = new ModUtils.EnvironmentVariable("PATH", qbs.pathListSeparator, qbs.hostOS.contains("windows")); + v.prepend(toolchainInstallPath); + v.set(); + } + + setupRunEnvironment: { + var v = new ModUtils.EnvironmentVariable("NODE_PATH", qbs.pathListSeparator, qbs.hostOS.contains("windows")); + v.prepend(FileInfo.path(Environment.getEnv("QBS_RUN_FILE_PATH"))); + v.set(); + } + + FileTagger { + patterns: ["*.js"] + fileTags: ["js"] + } + + validate: { + var validator = new ModUtils.PropertyValidator("nodejs"); + validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath); + validator.setRequiredProperty("interpreterFileName", interpreterFileName); + validator.setRequiredProperty("interpreterFilePath", interpreterFilePath); + validator.setRequiredProperty("packageManagerFileName", packageManagerFileName); + validator.setRequiredProperty("packageManagerFilePath", packageManagerFilePath); + validator.setRequiredProperty("packageManagerBinPath", packageManagerBinPath); + validator.setRequiredProperty("packageManagerRootPath", packageManagerRootPath); + validator.setRequiredProperty("packageManagerPrefixPath", packageManagerPrefixPath); + validator.validate(); + } + + Rule { + inputs: ["js"] + + outputArtifacts: { + var tags = ["nodejs_processed_js"]; + if (input.fileTags.contains("application_js") || + product.moduleProperty("nodejs", "applicationFile") === input.filePath) + tags.push("application"); + + // Preserve directory structure of input files + var intermediatePath = product.sourceDirectory; + + // Handle nodejs.compiledIntermediateDir (QBS-5 workaround) + var compiled = product.moduleProperty("nodejs", "compiledIntermediateDir"); + if (input.filePath.startsWith(compiled)) { + intermediatePath = compiled; + } + + intermediatePath = FileInfo.path(FileInfo.relativePath(intermediatePath, + input.filePath)); + + return [{ + filePath: FileInfo.joinPaths(product.destinationDirectory, intermediatePath, + input.fileName), + fileTags: tags + }]; + } + + outputFileTags: ["nodejs_processed_js", "application"] + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "copying " + input.fileName; + cmd.sourceCode = function() { + File.copy(input.filePath, output.filePath); + }; + return cmd; + } + } +} diff --git a/share/qbs/modules/nodejs/nodejs.js b/share/qbs/modules/nodejs/nodejs.js new file mode 100644 index 00000000..c1ae314c --- /dev/null +++ b/share/qbs/modules/nodejs/nodejs.js @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var Process = loadExtension("qbs.Process"); + +function findLocation(packageManagerFilePath, location) { + var p = new Process(); + try { + p.exec(packageManagerFilePath, [location, "-g"]); + return p.readStdOut().trim(); + } finally { + p.close(); + } +} diff --git a/share/qbs/modules/nsis/NSISModule.qbs b/share/qbs/modules/nsis/NSISModule.qbs new file mode 100644 index 00000000..c4a1339e --- /dev/null +++ b/share/qbs/modules/nsis/NSISModule.qbs @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Utilities + +Module { + condition: qbs.targetOS.contains("windows") + + property path toolchainInstallPath: Utilities.getNativeSetting(registryKey) + + version: (versionMajor !== undefined && versionMinor !== undefined) ? (versionMajor + "." + versionMinor) : undefined + property var versionParts: [ versionMajor, versionMinor, versionPatch, versionBuild ] + property int versionMajor: Utilities.getNativeSetting(registryKey, "VersionMajor") + property int versionMinor: Utilities.getNativeSetting(registryKey, "VersionMinor") + property int versionPatch: Utilities.getNativeSetting(registryKey, "VersionBuild") + property int versionBuild: Utilities.getNativeSetting(registryKey, "VersionRevision") + + property string compilerName: "makensis" + property string compilerPath: compilerName + + property string warningLevel: "normal" + PropertyOptions { + name: "warningLevel" + allowedValues: ["none", "normal", "errors", "warnings", "info", "all"] + } + + property bool disableConfig: false + PropertyOptions { + name: "disableConfig" + description: "disable inclusion of nsisconf.nsh" + } + + property bool enableQbsDefines: true + PropertyOptions { + name: "enableQbsDefines" + description: "built-in variables that are defined when using the NSIS compiler" + } + + property stringList defines + PropertyOptions { + name: "defines" + description: "variables that are defined when using the NSIS compiler" + } + + property stringList compilerFlags + PropertyOptions { + name: "compilerFlags" + description: "additional flags for the NSIS compiler" + } + + property string compressor: "default" + PropertyOptions { + name: "compressor" + description: "the compression algorithm used to compress files/data in the installer" + allowedValues: ["default", "zlib", "zlib-solid", "bzip2", "bzip2-solid", "lzma", "lzma-solid"] + } + + property string executableSuffix: ".exe" + + // Private properties + property string registryKey: { + if (!qbs.hostOS.contains("windows")) + return undefined; + + var keys = [ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS" ]; + for (var i in keys) { + if (Utilities.getNativeSetting(keys[i])) + return keys[i]; + } + } + + validate: { + var validator = new ModUtils.PropertyValidator("nsis"); + + // Only *require* the toolchain install path on Windows + // On other (Unix-like) operating systems it'll probably be in the PATH + if (qbs.targetOS.contains("windows")) + validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath); + + validator.setRequiredProperty("versionMajor", versionMajor); + validator.setRequiredProperty("versionMinor", versionMinor); + validator.setRequiredProperty("versionPatch", versionPatch); + validator.setRequiredProperty("versionBuild", versionBuild); + validator.addVersionValidator("version", version, 2, 4); + validator.addRangeValidator("versionMajor", versionMajor, 1); + validator.addRangeValidator("versionMinor", versionMinor, 0); + validator.addRangeValidator("versionPatch", versionPatch, 0); + validator.addRangeValidator("versionBuild", versionBuild, 0); + validator.validate(); + } + + setupBuildEnvironment: { + if (toolchainInstallPath) { + var v = new ModUtils.EnvironmentVariable("PATH", ";", true); + v.prepend(toolchainInstallPath); + v.prepend(FileInfo.joinPaths(toolchainInstallPath, "bin")); + v.set(); + } + } + + // NSIS Script File + FileTagger { + patterns: ["*.nsi"] + fileTags: ["nsi"] + } + + // NSIS Header File + FileTagger { + patterns: ["*.nsh"] + fileTags: ["nsh"] + } + + Rule { + id: nsisCompiler + multiplex: true + inputs: ["nsi"] + + Artifact { + fileTags: ["nsissetup", "application"] + filePath: product.destinationDirectory + "/" + product.targetName + ModUtils.moduleProperty(product, "executableSuffix") + } + + prepare: { + var i; + var args = []; + + // Prefix character for makensis options + var opt = product.moduleProperty("qbs", "hostOS").contains("windows") ? "/" : "-"; + + if (ModUtils.moduleProperty(product, "disableConfig")) { + args.push(opt + "NOCONFIG"); + } + + var warningLevel = ModUtils.moduleProperty(product, "warningLevel"); + var warningLevels = ["none", "errors", "warnings", "info", "all"]; + if (warningLevel !== "normal") { + var level = warningLevels.indexOf(warningLevel); + if (level < 0) { + throw("Unexpected warning level '" + warningLevel + "'."); + } else { + args.push(opt + "V" + level); + } + } + + var enableQbsDefines = ModUtils.moduleProperty(product, "enableQbsDefines") + if (enableQbsDefines) { + var map = { + "project.": project, + "product.": product + }; + + for (var prefix in map) { + var obj = map[prefix]; + for (var prop in obj) { + var val = obj[prop]; + if (typeof val !== 'function' && typeof val !== 'object' && !prop.startsWith("_")) { + args.push(opt + "D" + prefix + prop + "=" + val); + } + } + } + + // Users are likely to need this + var arch = product.moduleProperty("qbs", "architecture"); + args.push(opt + "Dqbs.architecture=" + arch); + + // Helper define for alternating between 32-bit and 64-bit logic + if (arch === "x86_64" || arch === "ia64") { + args.push(opt + "DWin64"); + } + } + + // User-supplied defines + var defines = ModUtils.moduleProperty(product, "defines"); + for (i in defines) { + args.push(opt + "D" + defines[i]); + } + + // User-supplied flags + var flags = ModUtils.moduleProperty(product, "compilerFlags"); + for (i in flags) { + args.push(flags[i]); + } + + // Override the compression algorithm if needed + var compressor = ModUtils.moduleProperty(product, "compressor"); + if (compressor !== "default") { + var compressorFlag = opt + "XSetCompressor /FINAL "; + if (compressor.endsWith("-solid")) { + compressorFlag += "/SOLID "; + } + args.push(compressorFlag + compressor.split('-')[0]); + } + + var inputFileNames = []; + for (i in inputs.nsi) { + inputFileNames.push(inputs.nsi[i].fileName); + args.push(FileInfo.toNativeSeparators(inputs.nsi[i].filePath, + product.moduleProperty("qbs", "hostOS"))); + } + + // Output file name - this goes last to override any OutFile command in the script + args.push(opt + "XOutFile " + output.filePath); + + var cmd = new Command(ModUtils.moduleProperty(product, "compilerPath"), args); + cmd.description = "compiling " + inputFileNames.join(", "); + cmd.highlight = "compiler"; + cmd.workingDirectory = FileInfo.path(output.filePath); + return cmd; + } + } +} diff --git a/share/qbs/modules/qbs/common.qbs b/share/qbs/modules/qbs/common.qbs new file mode 100644 index 00000000..ca578524 --- /dev/null +++ b/share/qbs/modules/qbs/common.qbs @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs 1.0 +import qbs.Environment +import qbs.FileInfo +import qbs.ModUtils +import qbs.PathTools +import qbs.Utilities + +Module { + readonly property string configurationName: "default" + property string buildVariant: { + switch (configurationName.toLowerCase()) { + case "release": + return "release"; + default: + return "debug"; + } + } + + property bool enableDebugCode: buildVariant == "debug" + property bool debugInformation: (buildVariant == "debug") + property string optimization: (buildVariant == "debug" ? "none" : "fast") + readonly property stringList hostOS: undefined // set internally + property string hostOSVersion: { + if (hostOS && hostOS.contains("macos")) { + return Utilities.getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductVersion") || + Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductVersion"); + } else if (hostOS && hostOS.contains("windows")) { + var version = Utilities.getNativeSetting(windowsRegistryKey, "CurrentVersion"); + return version + "." + hostOSBuildVersion; + } + } + + property string hostOSBuildVersion: { + if (hostOS.contains("macos")) { + return Utilities.getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductBuildVersion") || + Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductBuildVersion"); + } else if (hostOS.contains("windows")) { + return Utilities.getNativeSetting(windowsRegistryKey, "CurrentBuildNumber"); + } + } + + readonly property var hostOSVersionParts: hostOSVersion ? hostOSVersion.split('.').map(function(item) { return parseInt(item, 10); }) : [] + readonly property int hostOSVersionMajor: hostOSVersionParts[0] || 0 + readonly property int hostOSVersionMinor: hostOSVersionParts[1] || 0 + readonly property int hostOSVersionPatch: hostOSVersionParts[2] || 0 + + property stringList targetOS: hostOS + property string pathListSeparator: hostOS.contains("windows") ? ";" : ":" + property string pathSeparator: hostOS.contains("windows") ? "\\" : "/" + property string nullDevice: hostOS.contains("windows") ? "NUL" : "/dev/null" + property path shellPath: hostOS.contains("windows") ? windowsShellPath : "/bin/sh" + property string profile + property stringList toolchain: [] + property string architecture + property bool install: false + property path installSourceBase + readonly property string installRoot: undefined + property string installDir + property string installPrefix: "" + property path sysroot + + PropertyOptions { + name: "buildVariant" + allowedValues: ['debug', 'release'] + description: "name of the build variant" + } + + PropertyOptions { + name: "optimization" + allowedValues: ['none', 'fast', 'small'] + description: "optimization level" + } + + validate: { + var validator = new ModUtils.PropertyValidator("qbs"); + validator.setRequiredProperty("hostOS", hostOS); + validator.setRequiredProperty("targetOS", targetOS); + validator.addCustomValidator("targetOS", targetOS, function (value) { + if (!value || (value.contains("osx") && !value.contains("macos"))) + return false; + return true; + }, "the value 'osx' has been replaced by 'macos'; use that instead and update " + + "hostOS and targetOS condition checks in your project accordingly"); + if (hostOS && (hostOS.contains("windows") || hostOS.contains("macos"))) { + validator.setRequiredProperty("hostOSVersion", hostOSVersion, + "could not detect host operating system version; " + + "verify that system files and registry keys have not " + + "been modified."); + if (hostOSVersion) + validator.addVersionValidator("hostOSVersion", hostOSVersion, 2, 4); + + validator.setRequiredProperty("hostOSBuildVersion", hostOSBuildVersion, + "could not detect host operating system build version; " + + "verify that system files or registry have not been " + + "tampered with."); + } + + validator.addCustomValidator("architecture", architecture, function (value) { + return !architecture || architecture === Utilities.canonicalArchitecture(architecture); + }, "'" + architecture + "' is invalid. You must use the canonical name '" + + Utilities.canonicalArchitecture(architecture) + "'"); + + validator.addCustomValidator("toolchain", toolchain, function (value) { + if (toolchain === undefined) + return false; // cannot have null toolchain, empty is valid... for now + var canonical = Utilities.canonicalToolchain.apply(this, toolchain); + for (var i = 0; i < Math.max(canonical.length, toolchain.length); ++i) { + if (canonical[i] !== toolchain[i]) + return false; + } + return true; + }, "'" + toolchain + "' is invalid. You must use the canonical list '" + + Utilities.canonicalToolchain.apply(this, toolchain) + "'"); + + validator.addCustomValidator("toolchain", toolchain, function (value) { + // None of the pairs listed here may appear in the same toolchain list. + // Note that this check is applied AFTER canonicalization, so for example + // {"clang", "msvc"} need not be checked, since a toolchain containing clang is + // guaranteed to also contain gcc. + var pairs = [ + ["gcc", "msvc"], + ["llvm", "mingw"] + ]; + var canonical = Utilities.canonicalToolchain.apply(this, value); + for (var i = 0; i < pairs.length; ++i) { + if (canonical.contains(pairs[i][0]) && canonical.contains(pairs[i][1])) + return false; + } + return true; + }, "'" + toolchain + "' contains one or more mutually exclusive toolchain types."); + + validator.validate(); + } + + // private properties + property string windowsRegistryKey: "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion" + property path windowsSystemRoot: FileInfo.fromWindowsSeparators(Utilities.getNativeSetting(windowsRegistryKey, "SystemRoot")) + property path windowsShellPath: FileInfo.fromWindowsSeparators(Environment.getEnv("COMSPEC")) || FileInfo.joinPaths(windowsSystemRoot, "System32", "cmd.exe") + property string windowsPathVariable: hostOS.contains("windows") ? "PATH" : "WINEPATH" + + property var commonRunEnvironment: { + var env = Environment.currentEnv(); + if (targetOS.contains("windows")) { + var newEntry = FileInfo.toWindowsSeparators(FileInfo.joinPaths(installRoot, + installPrefix)); + env[windowsPathVariable] = PathTools.prependOrSetPath(newEntry, + env[windowsPathVariable], + qbs.pathListSeparator); + } else if (hostOS.contains("macos") && targetOS.contains("macos")) { + env["DYLD_FRAMEWORK_PATH"] = PathTools.prependOrSetPath([ + FileInfo.joinPaths(installRoot, installPrefix, "Library", "Frameworks"), + FileInfo.joinPaths(installRoot, installPrefix, "lib"), + FileInfo.joinPaths(installRoot, installPrefix) + ].join(pathListSeparator), env["DYLD_FRAMEWORK_PATH"], qbs.pathListSeparator); + env["DYLD_LIBRARY_PATH"] = PathTools.prependOrSetPath([ + FileInfo.joinPaths(installRoot, installPrefix, "lib"), + FileInfo.joinPaths(installRoot, installPrefix, "Library", "Frameworks"), + FileInfo.joinPaths(installRoot, installPrefix) + ].join(pathListSeparator), env["DYLD_LIBRARY_PATH"], qbs.pathListSeparator); + } else if (hostOS.contains("unix") && targetOS.contains("unix")) { + env["LD_LIBRARY_PATH"] = PathTools.prependOrSetPath( + FileInfo.joinPaths(installRoot, installPrefix, "lib"), env["LD_LIBRARY_PATH"], + qbs.pathListSeparator); + } + + return env; + } + + // internal properties + readonly property string version: [versionMajor, versionMinor, versionPatch].join(".") + readonly property int versionMajor: undefined // set internally + readonly property int versionMinor: undefined // set internally + readonly property int versionPatch: undefined // set internally +} diff --git a/share/qbs/modules/typescript/TypeScriptModule.qbs b/share/qbs/modules/typescript/TypeScriptModule.qbs new file mode 100644 index 00000000..7cbd0226 --- /dev/null +++ b/share/qbs/modules/typescript/TypeScriptModule.qbs @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes +import qbs.Process +import "typescript.js" as TypeScript + +Module { + // Qbs does NOT support standalone TypeScript installations + // (for example, %PROGRAMFILES%\Microsoft SDKs\TypeScript and some Debian and RPM packages), + // because they do not include typescript.d.ts, which is necessary for building internal tools. + // Only npm-based installations of TypeScript are supported (this is also the most common). + Depends { name: "nodejs" } + + additionalProductTypes: ["compiled_typescript"] + + Probes.TypeScriptProbe { + id: tsc + interpreterPath: FileInfo.path(nodejs.interpreterFilePath) + packageManagerBinPath: nodejs.packageManagerBinPath + packageManagerRootPath: nodejs.packageManagerRootPath + } + + property path toolchainInstallPath: tsc.path + + property path toolchainLibDirName: (versionMajor > 1 || (versionMajor === 1 && versionMinor >= 6)) ? "lib" : "bin" + property path toolchainLibInstallPath: FileInfo.joinPaths(nodejs.packageManagerRootPath, "typescript", toolchainLibDirName) + + version: tsc.version ? tsc.version[2] : undefined + property var versionParts: version ? version.split('.').map(function(item) { return parseInt(item, 10); }) : [] + property int versionMajor: versionParts[0] + property int versionMinor: versionParts[1] + property int versionPatch: versionParts[2] + property int versionBuild: versionParts[3] + property string versionSuffix: tsc.version ? tsc.version[3] : undefined + + property string compilerName: tsc.fileName + property string compilerPath: tsc.filePath + + property string warningLevel: "normal" + PropertyOptions { + name: "warningLevel" + description: "pedantic to warn on expressions and declarations with an implied 'any' type" + allowedValues: ["normal", "pedantic"] + } + + property string targetVersion + PropertyOptions { + name: "targetVersion" + description: "ECMAScript target version" + allowedValues: ["ES3", "ES5", "ES2015"] + } + + property string moduleLoader + PropertyOptions { + name: "moduleLoader" + allowedValues: ["commonjs", "amd"] + } + + property bool stripComments: !qbs.debugInformation + PropertyOptions { + name: "stripComments" + description: "whether to remove comments from the generated output" + } + + property bool generateDeclarations: false + PropertyOptions { + name: "generateDeclarations" + description: "whether to generate corresponding .d.ts files during compilation" + } + + // In release mode, nodejs can/should default-enable minification and obfuscation, + // making the source maps useless, so these default settings work out fine + property bool generateSourceMaps: qbs.debugInformation + PropertyOptions { + name: "generateSourceMaps" + description: "whether to generate corresponding .map files during compilation" + } + + property stringList compilerFlags + PropertyOptions { + name: "compilerFlags" + description: "additional flags for the TypeScript compiler" + } + + property bool singleFile: false + PropertyOptions { + name: "singleFile" + description: "whether to compile all source files to a single output file" + } + + validate: { + var interpreterMessage = "TypeScript requires the Node.js interpreter to be called 'node'."; + if (File.exists("/etc/debian_version")) { + interpreterMessage += " Did you forget to install the nodejs-legacy package? " + + "See https://lists.debian.org/debian-devel-announce/2012/07/msg00002.html " + + "for more information."; + } + + var preValidator = new ModUtils.PropertyValidator("nodejs"); + preValidator.addCustomValidator("interpreterFileName", nodejs.interpreterFileName, function (value) { + return value === "node" + (qbs.hostOS.contains("windows") ? ".exe" : ""); + }, interpreterMessage); + preValidator.addCustomValidator("interpreterFilePath", nodejs.interpreterFilePath, function (value) { + return value.endsWith(nodejs.interpreterFileName); + }, interpreterMessage); + preValidator.validate(); + + var validator = new ModUtils.PropertyValidator("typescript"); + validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath); + validator.setRequiredProperty("compilerName", compilerName); + validator.setRequiredProperty("compilerPath", compilerPath); + validator.setRequiredProperty("version", version); + validator.setRequiredProperty("versionParts", versionParts); + validator.setRequiredProperty("versionMajor", versionMajor); + validator.setRequiredProperty("versionMinor", versionMinor); + validator.setRequiredProperty("versionPatch", versionPatch); + validator.addVersionValidator("version", version, 3, 4); + validator.addRangeValidator("versionMajor", versionMajor, 1); + validator.addRangeValidator("versionMinor", versionMinor, 0); + validator.addRangeValidator("versionPatch", versionPatch, 0); + + if (versionParts && versionParts.length >= 4) { + validator.setRequiredProperty("versionBuild", versionBuild); + validator.addRangeValidator("versionBuild", versionBuild, 0); + } + + validator.validate(); + } + + // TypeScript declaration files + FileTagger { + patterns: ["*.d.ts"] + fileTags: ["typescript_declaration"] + } + + // TypeScript source files + FileTagger { + patterns: ["*.ts"] + fileTags: ["typescript"] + } + + Group { + name: "io.qt.qbs.internal.typescript-helper" + files: [ + FileInfo.joinPaths(path, "qbs-tsc-scan", "qbs-tsc-scan.ts"), + FileInfo.joinPaths(typescript.toolchainLibInstallPath, "typescript.d.ts"), + FileInfo.joinPaths(typescript.toolchainLibInstallPath, "..", "package.json") + ] + fileTags: ["typescript.typescript-internal"] + } + + Rule { + multiplex: true + inputs: ["typescript.typescript-internal"] + + outputFileTags: ["typescript.compiled_typescript-internal"] + outputArtifacts: { + if (!TypeScript.supportsModernFeatures(product)) + return []; + return [{ + filePath: FileInfo.joinPaths(product.buildDirectory, + ".io.qt.qbs.internal.typescript", "qbs-tsc-scan.ts"), + fileTags: ["typescript.typescript-internal.copy"] + }, + { + filePath: FileInfo.joinPaths(product.buildDirectory, + ".io.qt.qbs.internal.typescript", + "node_modules", "typescript", "lib", "typescript.d.ts"), + fileTags: ["typescript.typescript-internal.copy"] + }, + { + filePath: FileInfo.joinPaths(product.buildDirectory, + ".io.qt.qbs.internal.typescript", + "node_modules", "typescript", "package.json"), + fileTags: ["typescript.typescript-internal.copy"] + }, + { + filePath: FileInfo.joinPaths(product.buildDirectory, + ".io.qt.qbs.internal.typescript", "qbs-tsc-scan.js"), + fileTags: ["typescript.compiled_typescript-internal"] + }]; + } + + prepare: { + var inputPaths = inputs["typescript.typescript-internal"].map(function (input) { + return input.filePath; + }); + + var outputPaths = outputs["typescript.typescript-internal.copy"].map(function (output) { + return output.filePath; + }); + + var sortFunc = function (a, b) { + return FileInfo.fileName(a).localeCompare(FileInfo.fileName(b)); + }; + + var jcmd = new JavaScriptCommand(); + jcmd.ignoreDryRun = true; + jcmd.silent = true; + jcmd.inputPaths = inputPaths.sort(sortFunc); + jcmd.outputPaths = outputPaths.sort(sortFunc); + jcmd.sourceCode = function() { + for (var i = 0; i < inputPaths.length; ++i) + File.copy(inputPaths[i], outputPaths[i]); + }; + + var outDir = FileInfo.path( + outputs["typescript.compiled_typescript-internal"][0].filePath); + var args = ["--module", "commonjs", + "--outDir", outDir].concat(outputPaths.filter(function (f) { return !f.endsWith(".json"); })); + var cmd = new Command(ModUtils.moduleProperty(product, "compilerPath"), args); + cmd.ignoreDryRun = true; + cmd.silent = true; + return [jcmd, cmd]; + } + } + + Rule { + id: typescriptCompiler + multiplex: true + inputs: ["typescript"] + inputsFromDependencies: ["typescript_declaration"] + explicitlyDependsOn: ["typescript.compiled_typescript-internal"] + + outputArtifacts: TypeScript.outputArtifacts(product, inputs) + + outputFileTags: ["application_js", "compiled_typescript", "js", "source_map", "typescript_declaration"] + + prepare: { + var cmd, cmds = []; + + cmd = new Command(ModUtils.moduleProperty(product, "compilerPath"), + TypeScript.tscArguments(product, inputs)); + cmd.description = "compiling " + (ModUtils.moduleProperty(product, "singleFile") + ? outputs.compiled_typescript[0].fileName + : inputs.typescript.map(function(obj) { + return obj.fileName; }).join(", ")); + cmd.highlight = "compiler"; + cmds.push(cmd); + + // QBS-5 + // Move the compiled JavaScript files compiled by TypeScript to an intermediate + // directory so that the nodejs module can perform any necessary postprocessing + // on the result. The nodejs module will move the files back to their original + // locations after postprocessing. + cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.outDir = product.buildDirectory; + cmd.sourceCode = function() { + for (var i = 0; i < outputs.compiled_typescript.length; ++i) { + var output = outputs.compiled_typescript[i]; + var intermediatePath = FileInfo.path(FileInfo.relativePath(product.moduleProperty("nodejs", "compiledIntermediateDir"), output.filePath)); + var originalFilePath = FileInfo.joinPaths(outDir, intermediatePath, output.fileName); + File.copy(originalFilePath, output.filePath); + File.remove(originalFilePath); + } + }; + cmds.push(cmd); + + return cmds; + } + } +} diff --git a/share/qbs/modules/typescript/qbs-tsc-scan/.gitignore b/share/qbs/modules/typescript/qbs-tsc-scan/.gitignore new file mode 100644 index 00000000..2ac7185a --- /dev/null +++ b/share/qbs/modules/typescript/qbs-tsc-scan/.gitignore @@ -0,0 +1,5 @@ +# Visual Studio Code IDE +tsconfig.json +qbs-tsc-scan.js +typings/node/node.d.ts +typings/typescript/typescript.d.ts diff --git a/share/qbs/modules/typescript/qbs-tsc-scan/qbs-tsc-scan.ts b/share/qbs/modules/typescript/qbs-tsc-scan/qbs-tsc-scan.ts new file mode 100644 index 00000000..ca5eb420 --- /dev/null +++ b/share/qbs/modules/typescript/qbs-tsc-scan/qbs-tsc-scan.ts @@ -0,0 +1,68 @@ +import ts = require("typescript"); + +declare var process: any; + +export namespace io.qt.qbs { + export class Artifact { + filePath: string; + fileTags: string[]; + } + + export namespace tools { + export namespace utils { + function stringEndsWith(s: string, e: string) { + return s.slice(-e.length) === e; + } + + export function artifactFromFilePath(filePath: string): Artifact { + var fileTags: string[] = []; + if (stringEndsWith(filePath, ".js.map")) { + fileTags.push("source_map"); + } else if (stringEndsWith(filePath, ".js")) { + fileTags.push("js", "compiled_typescript"); + } else if (stringEndsWith(filePath, ".d.ts")) { + fileTags.push("typescript_declaration"); + } + + return { filePath: filePath, fileTags: fileTags }; + } + } + + function compileInternal(fileNames: string[], options: ts.CompilerOptions): qbs.Artifact[] { + var outputArtifacts: qbs.Artifact[] = []; + var program = ts.createProgram(fileNames, options); + var emitResult = program.emit(undefined, filePath => { + outputArtifacts.push(utils.artifactFromFilePath(filePath)); + }); + + var allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics); + allDiagnostics.forEach(diagnostic => { + var message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); + if (diagnostic.file) { + var { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); + console.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); + } else { + console.error(message); + } + }); + + return emitResult.emitSkipped ? undefined : outputArtifacts; + } + + export function compile(commandLineArguments: string[]): qbs.Artifact[] { + var parsedCommandLine = ts.parseCommandLine(commandLineArguments); + return compileInternal(parsedCommandLine.fileNames, parsedCommandLine.options); + } + + export function TypeScriptCompilerScannerToolMain(): void { + var outputArtifacts = compile(process.argv.slice(2)); + if (outputArtifacts !== undefined) { + console.log(JSON.stringify(outputArtifacts)); + } else { + process.exit(1); + } + } + } +} + +io.qt.qbs.tools.TypeScriptCompilerScannerToolMain(); diff --git a/share/qbs/modules/typescript/typescript.js b/share/qbs/modules/typescript/typescript.js new file mode 100644 index 00000000..133f42c0 --- /dev/null +++ b/share/qbs/modules/typescript/typescript.js @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = loadExtension("qbs.FileInfo"); +var ModUtils = loadExtension("qbs.ModUtils"); +var Process = loadExtension("qbs.Process"); + +function findTscVersion(compilerFilePath, nodejsPath) { + var p = new Process(); + try { + if (nodejsPath) + p.setEnv("PATH", nodejsPath); + p.exec(compilerFilePath, ["--version"]); + var re = /^(?:message TS6029: )?Version (([0-9]+(?:\.[0-9]+){1,3})(?:-(.+?))?)$/m; + var match = p.readStdOut().trim().match(re); + if (match !== null) + return match; + } finally { + p.close(); + } +} + +function tscArguments(product, inputs) { + var i; + var args = []; + + if (ModUtils.moduleProperty(product, "warningLevel") === "pedantic") { + args.push("--noImplicitAny"); + } + + var targetVersion = ModUtils.moduleProperty(product, "targetVersion"); + if (targetVersion) { + args.push("--target"); + args.push(targetVersion); + } + + var moduleLoader = ModUtils.moduleProperty(product, "moduleLoader"); + if (moduleLoader) { + args.push("--module"); + args.push(moduleLoader); + } + + if (ModUtils.moduleProperty(product, "stripComments")) { + args.push("--removeComments"); + } + + if (ModUtils.moduleProperty(product, "generateDeclarations")) { + args.push("--declaration"); + } + + if (ModUtils.moduleProperty(product, "generateSourceMaps")) { + args.push("--sourcemap"); + } + + // User-supplied flags + var flags = ModUtils.moduleProperty(product, "compilerFlags"); + for (i in flags) { + args.push(flags[i]); + } + + if (supportsModernFeatures(product)) { + args.push("--rootDir", product.sourceDirectory); + } + + args.push("--outDir", product.buildDirectory); + + if (ModUtils.moduleProperty(product, "singleFile")) { + args.push(outOption(product), + FileInfo.joinPaths(product.destinationDirectory, product.targetName) + ".js"); + } + + if (inputs.typescript_declaration) { + for (i = 0; i < inputs.typescript_declaration.length; ++i) { + args.push(inputs.typescript_declaration[i].filePath); + } + } + + if (inputs.typescript) { + for (i = 0; i < inputs.typescript.length; ++i) { + args.push(inputs.typescript[i].filePath); + } + } + + if (inputs["typescript.typescript-internal"]) { + for (i = 0; i < inputs["typescript.typescript-internal"].length; ++i) { + args.push(inputs["typescript.typescript-internal"][i].filePath); + } + } + + return args; +} + +function outputArtifacts(product, inputs) { + if (!supportsModernFeatures(product)) { + console.warn("Qbs does not properly support TypeScript versions prior to 1.5 due to " + + "severe limitations in dependency tracking. This is TypeScript version " + + ModUtils.moduleProperty(product, "version") + ". It is strongly recommended " + + "that you upgrade TypeScript, or continue at your own risk."); + return legacyOutputArtifacts(product, inputs); + } + + var process; + try { + process = new Process(); + process.setEnv("NODE_PATH", [ + ModUtils.moduleProperty(product, "toolchainInstallPath"), + product.moduleProperty("nodejs", "packageManagerRootPath") + ].join(product.moduleProperty("qbs", "pathListSeparator"))); + process.exec(product.moduleProperty("nodejs", "interpreterFilePath"), + [FileInfo.joinPaths(product.buildDirectory, + ".io.qt.qbs.internal.typescript", + "qbs-tsc-scan.js")] + .concat(tscArguments(product, inputs)), true); + var artifacts = JSON.parse(process.readStdOut()); + + // Find and tag the "main" output file + var applicationFile = product.moduleProperty("nodejs", "applicationFile"); + if (applicationFile) { + var i, appIndex = -1; + if (product.moduleProperty("typescript", "singleFile")) { + for (i = 0; i < artifacts.length; ++i) { + if (artifacts[i].fileTags.contains("compiled_typescript")) { + appIndex = i; + break; + } + } + } else { + var expected = FileInfo.relativePath(product.sourceDirectory, applicationFile); + if (!expected.endsWith(".ts")) + // tsc doesn't allow this anyways, so it's a perfectly reasonable restriction + throw "TypeScript source file name '" + applicationFile + + "' does not end with .ts"; + + expected = expected.slice(0, -2) + "js"; + + for (i = 0; i < artifacts.length; ++i) { + if (expected === FileInfo.relativePath(product.buildDirectory, + artifacts[i].filePath)) { + appIndex = i; + break; + } + } + } + + if (appIndex === -1 || !artifacts[appIndex].fileTags.contains("compiled_typescript")) + throw "nodejs.applicationFile was set, but Qbs couldn't find the compiled " + + "JavaScript file corresponding to '" + applicationFile + "'"; + + artifacts[appIndex].fileTags = artifacts[appIndex].fileTags.concat(["application_js"]); + } + + return artifacts; + } finally { + if (process) + process.close(); + } +} + +function legacyOutputArtifacts(product, inputs) { + var artifacts = []; + + if (!inputs.typescript) { + return artifacts; + } + + var jsTags = ["js", "compiled_typescript"]; + var filePath = FileInfo.joinPaths(product.destinationDirectory, product.targetName); + if (product.moduleProperty("typescript", "singleFile")) { + // We could check + // if (product.moduleProperty("nodejs", "applicationFile") === inputs.typescript[i].filePath) + // but since we're compiling to a single file there's no need to state it explicitly + jsTags.push("application_js"); + + artifacts.push({fileTags: jsTags, + filePath: FileInfo.joinPaths( + product.moduleProperty("nodejs", + "compiledIntermediateDir"), + product.targetName + ".js")}); + + if (product.moduleProperty("typescript", "generateDeclarations")) { + artifacts.push({fileTags: ["typescript_declaration"], + filePath: filePath + ".d.ts"}); + } + + if (product.moduleProperty("typescript", "generateSourceMaps")) { + artifacts.push({fileTags: ["source_map"], + filePath: filePath + ".js.map"}); + } + } else { + for (var i = 0; i < inputs.typescript.length; ++i) { + jsTags = ["js", "compiled_typescript"]; + if (product.moduleProperty("nodejs", "applicationFile") === inputs.typescript[i].filePath) + jsTags.push("application_js"); + + var intermediatePath = FileInfo.path(FileInfo.relativePath( + product.sourceDirectory, + inputs.typescript[i].filePath)); + + var baseName = FileInfo.baseName(inputs.typescript[i].fileName); + filePath = FileInfo.joinPaths(product.destinationDirectory, + intermediatePath, + baseName); + + artifacts.push({fileTags: jsTags, + filePath: FileInfo.joinPaths( + product.moduleProperty("nodejs", + "compiledIntermediateDir"), + intermediatePath, + baseName + ".js")}); + + if (product.moduleProperty("typescript", "generateDeclarations")) { + artifacts.push({fileTags: ["typescript_declaration"], + filePath: filePath + ".d.ts"}); + } + + if (product.moduleProperty("typescript", "generateSourceMaps")) { + artifacts.push({fileTags: ["source_map"], + filePath: filePath + ".js.map"}); + } + } + } + + return artifacts; +} + +function outOption(product) { + var compilerVersionMajor = ModUtils.moduleProperty(product, "versionMajor"); + if (compilerVersionMajor === 1) { + if (ModUtils.moduleProperty(product, "versionMinor") < 6) { + return "--out"; + } + } + + return "--outFile"; +} + +function supportsModernFeatures(product) { + var compilerVersionMajor = ModUtils.moduleProperty(product, "versionMajor"); + if (compilerVersionMajor === 1) { + if (ModUtils.moduleProperty(product, "versionMinor") >= 5) { + return true; + } + } + + return compilerVersionMajor > 1; +} diff --git a/share/qbs/modules/wix/WiXModule.qbs b/share/qbs/modules/wix/WiXModule.qbs new file mode 100644 index 00000000..1ef816f8 --- /dev/null +++ b/share/qbs/modules/wix/WiXModule.qbs @@ -0,0 +1,441 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Probes +import qbs.Utilities + +Module { + condition: qbs.targetOS.contains("windows") + + Probes.WiXProbe { + id: wixProbe + } + + property path toolchainInstallPath: wixProbe.path + property path toolchainInstallRoot: wixProbe.root + version: wixProbe.version + property var versionParts: version ? version.split('.').map(function(item) { return parseInt(item, 10); }) : [] + property int versionMajor: versionParts[0] + property int versionMinor: versionParts[1] + property int versionPatch: versionParts[2] + property int versionBuild: versionParts[3] + + property string compilerName: "candle.exe" + property string compilerPath: FileInfo.joinPaths(toolchainInstallRoot, compilerName) + property string linkerName: "light.exe" + property string linkerPath: FileInfo.joinPaths(toolchainInstallRoot, linkerName) + + property string warningLevel: "normal" + PropertyOptions { + name: "warningLevel" + allowedValues: ["none", "normal", "pedantic"] + } + + property bool debugInformation: qbs.debugInformation + property bool treatWarningsAsErrors: false + property bool verboseOutput: false + PropertyOptions { + name: "verboseOutput" + description: "display verbose output from the compiler and linker" + } + + property bool visualStudioCompatibility: true + PropertyOptions { + name: "visualStudioCompatibility" + description: "whether to define most of the same variables as " + + "Visual Studio when using the Candle compiler" + } + + property bool enableQbsDefines: true + PropertyOptions { + name: "enableQbsDefines" + description: "built-in variables that are defined when using the Candle compiler" + } + + property pathList includePaths + PropertyOptions { + name: "includePaths" + description: "directories to add to the include search path" + } + + property stringList defines + PropertyOptions { + name: "defines" + description: "variables that are defined when using the Candle compiler" + } + + property stringList compilerFlags + PropertyOptions { + name: "compilerFlags" + description: "additional flags for the Candle compiler" + } + + property stringList linkerFlags + PropertyOptions { + name: "linkerFlags" + description: "additional flags for the Light linker" + } + + property stringList cultures + PropertyOptions { + name: "cultures" + description: "the list of localizations to build the MSI for; leave undefined to build all localizations" + } + + property stringList extensions: product.type.contains("wixsetup") ? ["WixBalExtension"] : [] // default to WiX Standard Bootstrapper extension + + // private properties + property string targetSuffix: { + if (product.type.contains("msi")) { + return windowsInstallerSuffix; + } else if (product.type.contains("wixsetup")) { + return executableSuffix; + } + } + + // MSI/MSM package validation only works natively on Windows + property bool enablePackageValidation: qbs.hostOS.contains("windows") + + property string executableSuffix: ".exe" + property string windowsInstallerSuffix: ".msi" + + validate: { + var validator = new ModUtils.PropertyValidator("wix"); + validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath); + validator.setRequiredProperty("toolchainInstallRoot", toolchainInstallRoot); + validator.setRequiredProperty("version", version); + validator.setRequiredProperty("versionMajor", versionMajor); + validator.setRequiredProperty("versionMinor", versionMinor); + validator.setRequiredProperty("versionPatch", versionPatch); + validator.addVersionValidator("version", version, 3, 4); + validator.addRangeValidator("versionMajor", versionMajor, 1); + validator.addRangeValidator("versionMinor", versionMinor, 0); + validator.addRangeValidator("versionPatch", versionPatch, 0); + + if (versionParts && versionParts.length >= 4) { + validator.setRequiredProperty("versionBuild", versionBuild); + validator.addRangeValidator("versionBuild", versionBuild, 0); + } + + validator.validate(); + } + + setupBuildEnvironment: { + var v = new ModUtils.EnvironmentVariable("PATH", qbs.pathListSeparator, true); + v.prepend(toolchainInstallPath); + v.prepend(toolchainInstallRoot); + v.set(); + } + + // WiX Include Files + FileTagger { + patterns: ["*.wxi"] + fileTags: ["wxi"] + } + + // WiX Localization Files + FileTagger { + patterns: ["*.wxl"] + fileTags: ["wxl"] + } + + // WiX Source Files + FileTagger { + patterns: ["*.wxs"] + fileTags: ["wxs"] + } + + Rule { + id: candleCompiler + inputs: ["wxs"] + auxiliaryInputs: ['wxi'] + + Artifact { + fileTags: ["wixobj"] + filePath: FileInfo.joinPaths(".obj", Utilities.getHash(input.baseDir), + FileInfo.baseName(input.fileName) + ".wixobj") + } + + prepare: { + var i; + var args = ["-nologo"]; + + if (ModUtils.moduleProperty(input, "warningLevel") === "none") { + args.push("-sw"); + } else { + if (ModUtils.moduleProperty(input, "warningLevel") === "pedantic") { + args.push("-pedantic"); + } + + if (ModUtils.moduleProperty(input, "treatWarningsAsErrors")) { + args.push("-wx"); + } + } + + if (ModUtils.moduleProperty(input, "verboseOutput")) { + args.push("-v"); + } + + var arch = product.moduleProperty("qbs", "architecture"); + if (!["x86", "x86_64", "ia64", "arm"].contains(arch)) { + // http://wixtoolset.org/documentation/manual/v3/xsd/wix/package.html + throw("WiX: unsupported architecture '" + arch + "'"); + } + + // QBS uses "x86_64", Microsoft uses "x64" + if (arch === "x86_64") { + arch = "x64"; + } + + // Visual Studio defines these variables along with various solution and project names and paths; + // we'll pass most of them to ease compatibility between QBS and WiX projects originally created + // using Visual Studio. The only definitions we don't pass are the ones which make no sense at all + // in QBS, like the solution and project directories since they do not exist. + if (ModUtils.moduleProperty(input, "visualStudioCompatibility")) { + var toolchain = product.moduleProperty("qbs", "toolchain"); + var toolchainInstallPath = product.moduleProperty("cpp", "toolchainInstallPath"); + if (toolchain && toolchain.contains("msvc") && toolchainInstallPath) { + var vcDir = toolchainInstallPath.replace(/[\\/]bin$/i, ""); + var vcRootDir = vcDir.replace(/[\\/]VC$/i, ""); + args.push("-dDevEnvDir=" + FileInfo.toWindowsSeparators(FileInfo.joinPaths(vcRootDir, 'Common7', 'IDE'))); + } + + var buildVariant = product.moduleProperty("qbs", "buildVariant"); + if (buildVariant === "debug") { + args.push("-dDebug"); + args.push("-dConfiguration=Debug"); + } else if (buildVariant === "release") { + // VS doesn't define "Release" + args.push("-dConfiguration=Release"); + } + + var productTargetExt = ModUtils.moduleProperty(input, "targetSuffix"); + if (!productTargetExt) { + throw("WiX: Unsupported product type '" + product.type + "'"); + } + + var builtBinaryFilePath = FileInfo.joinPaths(product.buildDirectory, product.destinationDirectory, product.targetName + productTargetExt); + args.push("-dOutDir=" + FileInfo.toWindowsSeparators(FileInfo.path(builtBinaryFilePath))); // in VS, relative to the project file by default + + args.push("-dPlatform=" + arch); + + args.push("-dProjectName=" + project.name); + + args.push("-dTargetDir=" + FileInfo.toWindowsSeparators(FileInfo.path(builtBinaryFilePath))); // in VS, an absolute path + args.push("-dTargetExt=" + productTargetExt); + args.push("-dTargetFileName=" + product.targetName + productTargetExt); + args.push("-dTargetName=" + product.targetName); + args.push("-dTargetPath=" + FileInfo.toWindowsSeparators(builtBinaryFilePath)); + } + + var includePaths = ModUtils.moduleProperty(input, "includePaths"); + for (i in includePaths) { + args.push("-I" + includePaths[i]); + } + + var enableQbsDefines = ModUtils.moduleProperty(input, "enableQbsDefines") + if (enableQbsDefines) { + var map = { + "project.": project, + "product.": product + }; + + for (var prefix in map) { + var obj = map[prefix]; + for (var prop in obj) { + var val = obj[prop]; + if (typeof val !== 'function' && typeof val !== 'object' && !prop.startsWith("_")) { + args.push("-d" + prefix + prop + "=" + val); + } + } + } + + // Helper define for alternating between 32-bit and 64-bit logic + if (arch === "x64" || arch === "ia64") { + args.push("-dWin64"); + } + } + + // User-supplied defines + var defines = ModUtils.moduleProperty(input, "defines"); + for (i in defines) { + args.push("-d" + defines[i]); + } + + // User-supplied flags + var flags = ModUtils.moduleProperty(input, "compilerFlags"); + for (i in flags) { + args.push(flags[i]); + } + + args.push("-out"); + args.push(FileInfo.toWindowsSeparators(output.filePath)); + args.push("-arch"); + args.push(arch); + + var extensions = ModUtils.moduleProperty(input, "extensions"); + for (i in extensions) { + args.push("-ext"); + args.push(extensions[i]); + } + + args.push(FileInfo.toWindowsSeparators(input.filePath)); + + var cmd = new Command(ModUtils.moduleProperty(product, "compilerPath"), args); + cmd.description = "compiling " + input.fileName; + cmd.highlight = "compiler"; + cmd.workingDirectory = FileInfo.path(output.filePath); + return cmd; + } + } + + Rule { + id: lightLinker + multiplex: true + inputs: ["wixobj", "wxl"] + + outputArtifacts: { + var artifacts = []; + + if (product.type.contains("wixsetup")) { + artifacts.push({ + fileTags: ["wixsetup", "application"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + + ModUtils.moduleProperty(product, + "executableSuffix")) + }); + } + + if (product.type.contains("msi")) { + artifacts.push({ + fileTags: ["msi"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + + ModUtils.moduleProperty(product, + "windowsInstallerSuffix")) + }); + } + + if (ModUtils.moduleProperty(product, "debugInformation")) { + artifacts.push({ + fileTags: ["wixpdb"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + ".wixpdb") + }); + } + + return artifacts; + } + + outputFileTags: ["application", "msi", "wixpdb", "wixsetup"] + + prepare: { + var i; + var primaryOutput; + if (product.type.contains("wixsetup")) { + primaryOutput = outputs.wixsetup[0]; + } else if (product.type.contains("msi")) { + primaryOutput = outputs.msi[0]; + } else { + throw("WiX: Unsupported product type '" + product.type + "'"); + } + + var args = ["-nologo"]; + + if (!ModUtils.moduleProperty(product, "enablePackageValidation")) { + args.push("-sval"); + } + + if (ModUtils.moduleProperty(product, "warningLevel") === "none") { + args.push("-sw"); + } else { + if (ModUtils.moduleProperty(product, "warningLevel") === "pedantic") { + args.push("-pedantic"); + } + + if (ModUtils.moduleProperty(product, "treatWarningsAsErrors")) { + args.push("-wx"); + } + } + + if (ModUtils.moduleProperty(product, "verboseOutput")) { + args.push("-v"); + } + + args.push("-out"); + args.push(FileInfo.toWindowsSeparators(primaryOutput.filePath)); + + if (ModUtils.moduleProperty(product, "debugInformation")) { + args.push("-pdbout"); + args.push(FileInfo.toWindowsSeparators(outputs.wixpdb[0].filePath)); + } else { + args.push("-spdb"); + } + + var extensions = ModUtils.moduleProperty(product, "extensions"); + for (i in extensions) { + args.push("-ext"); + args.push(extensions[i]); + } + + for (i in inputs.wxl) { + args.push("-loc"); + args.push(FileInfo.toWindowsSeparators(inputs.wxl[i].filePath)); + } + + if (product.type.contains("msi")) { + var cultures = ModUtils.moduleProperty(product, "cultures"); + args.push("-cultures:" + + (cultures && cultures.length > 0 ? cultures.join(";") : "null")); + } + + // User-supplied flags + var flags = ModUtils.moduleProperty(product, "linkerFlags"); + for (i in flags) { + args.push(flags[i]); + } + + for (i in inputs.wixobj) { + args.push(FileInfo.toWindowsSeparators(inputs.wixobj[i].filePath)); + } + + var cmd = new Command(ModUtils.moduleProperty(product, "linkerPath"), args); + cmd.description = "linking " + primaryOutput.fileName; + cmd.highlight = "linker"; + cmd.workingDirectory = FileInfo.path(primaryOutput.filePath); + return cmd; + } + } +} diff --git a/share/qbs/modules/xcode/xcode.js b/share/qbs/modules/xcode/xcode.js new file mode 100644 index 00000000..ffea9137 --- /dev/null +++ b/share/qbs/modules/xcode/xcode.js @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var File = loadExtension("qbs.File"); +var FileInfo = loadExtension("qbs.FileInfo"); +var Process = loadExtension("qbs.Process"); +var PropertyList = loadExtension("qbs.PropertyList"); + +function sdkInfoList(sdksPath) { + var sdkInfo = []; + var sdks = File.directoryEntries(sdksPath, File.Dirs | File.NoDotAndDotDot); + for (var i in sdks) { + // SDK directory name must contain a version number; + // we don't want the versionless iPhoneOS.sdk directory for example + if (!sdks[i].match(/[0-9]+/)) + continue; + + var settingsPlist = FileInfo.joinPaths(sdksPath, sdks[i], "SDKSettings.plist"); + var propertyList = new PropertyList(); + try { + propertyList.readFromFile(settingsPlist); + + function checkPlist(plist) { + if (!plist || !plist["CanonicalName"] || !plist["Version"]) + return false; + + var re = /^([0-9]+)\.([0-9]+)$/; + return plist["Version"].match(re); + } + + var plist = propertyList.toObject(); + if (!checkPlist(plist)) { + console.warn("Skipping corrupted SDK installation: " + + FileInfo.joinPaths(sdksPath, sdks[i])); + continue; + } + + sdkInfo.push(plist); + } finally { + propertyList.clear(); + } + } + + // Sort by SDK version number + sdkInfo.sort(function (a, b) { + var re = /^([0-9]+)\.([0-9]+)$/; + a = a["Version"].match(re); + if (a) + a = {major: a[1], minor: a[2]}; + b = b["Version"].match(re); + if (b) + b = {major: b[1], minor: b[2]}; + + if (a.major === b.major) + return a.minor - b.minor; + return a.major - b.major; + }); + + return sdkInfo; +} + +function findSigningIdentities(security, searchString) { + var process; + var identities; + if (searchString) { + try { + process = new Process(); + if (process.exec(security, ["find-identity", "-p", "codesigning", "-v"], true) !== 0) + console.error(process.readStdErr()); + + var lines = process.readStdOut().split("\n"); + for (var i in lines) { + // e.g. 1) AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "Mac Developer: John Doe (XXXXXXXXXX) john.doe@example.org" + var match = lines[i].match(/^\s*[0-9]+\)\s+([A-Fa-f0-9]{40})\s+"([^"]+)"$/); + if (match !== null) { + var hexId = match[1]; + var displayName = match[2]; + if (hexId === searchString || displayName.startsWith(searchString)) { + if (!identities) + identities = []; + identities.push([hexId, displayName]); + break; + } + } + } + } finally { + process.close(); + } + } + return identities; +} + +function provisioningProfilePlistContents(filePath) { + if (filePath === undefined) + return undefined; + + var plist = new PropertyList(); + try { + plist.readFromFile(filePath); + return plist.toObject(); + } finally { + plist.clear(); + } +} diff --git a/share/qbs/modules/xcode/xcode.qbs b/share/qbs/modules/xcode/xcode.qbs new file mode 100644 index 00000000..689a5752 --- /dev/null +++ b/share/qbs/modules/xcode/xcode.qbs @@ -0,0 +1,400 @@ +import qbs +import qbs.BundleTools +import qbs.Environment +import qbs.File +import qbs.FileInfo +import qbs.DarwinTools +import qbs.ModUtils +import qbs.Probes +import qbs.PropertyList +import 'xcode.js' as Xcode + +Module { + Probe { + id: xcodeProbe + configure: { + availableSdks = Xcode.sdkInfoList(sdksPath); + found = true; + } + + property var availableSdks + } + + condition: qbs.targetOS.contains("darwin") && + qbs.toolchain && qbs.toolchain.contains("xcode") + + property path developerPath: "/Applications/Xcode.app/Contents/Developer" + property string sdk: DarwinTools.applePlatformName(qbs.targetOS, platformType) + property stringList targetDevices: DarwinTools.targetDevices(qbs.targetOS) + + property string platformType: { + if (qbs.targetOS.containsAny(["ios-simulator", "tvos-simulator", "watchos-simulator"])) + return "simulator"; + if (qbs.targetOS.containsAny(["ios", "tvos", "watchos"])) + return "device"; + } + + readonly property string sdkName: { + if (_sdkSettings) { + return _sdkSettings["CanonicalName"]; + } + } + + readonly property string sdkVersion: { + if (_sdkSettings) { + return _sdkSettings["Version"]; + } + } + + readonly property string latestSdkName: { + if (_availableSdks) { + return _availableSdks[_availableSdks.length - 1]["CanonicalName"]; + } + } + + readonly property string latestSdkVersion: { + if (_availableSdks) { + return _availableSdks[_availableSdks.length - 1]["Version"]; + } + } + + readonly property stringList availableSdkNames: { + if (_availableSdks) { + return _availableSdks.map(function (obj) { return obj["CanonicalName"]; }); + } + } + + readonly property stringList availableSdkVersions: { + if (_availableSdks) { + return _availableSdks.map(function (obj) { return obj["Version"]; }); + } + } + + property string signingIdentity + readonly property string actualSigningIdentity: { + if (_actualSigningIdentity && _actualSigningIdentity.length === 1) + return _actualSigningIdentity[0][0]; + } + + readonly property string actualSigningIdentityDisplayName: { + if (_actualSigningIdentity && _actualSigningIdentity.length === 1) + return _actualSigningIdentity[0][1]; + } + + property string signingTimestamp: "none" + + property string provisioningProfile + + property string securityName: "security" + property string securityPath: securityName + + property string codesignName: "codesign" + property string codesignPath: codesignName + property stringList codesignFlags + + readonly property path toolchainPath: FileInfo.joinPaths(toolchainsPath, + "XcodeDefault" + ".xctoolchain") + readonly property path platformPath: FileInfo.joinPaths(platformsPath, + DarwinTools.applePlatformDirectoryName( + qbs.targetOS, platformType) + + ".platform") + readonly property path sdkPath: FileInfo.joinPaths(sdksPath, + DarwinTools.applePlatformDirectoryName( + qbs.targetOS, platformType, sdkVersion) + + ".sdk") + + // private properties + readonly property path toolchainsPath: FileInfo.joinPaths(developerPath, "Toolchains") + readonly property path platformsPath: FileInfo.joinPaths(developerPath, "Platforms") + readonly property path sdksPath: FileInfo.joinPaths(platformPath, "Developer", "SDKs") + + readonly property path platformInfoPlist: FileInfo.joinPaths(platformPath, "Info.plist") + readonly property path sdkSettingsPlist: FileInfo.joinPaths(sdkPath, "SDKSettings.plist") + readonly property path toolchainInfoPlist: FileInfo.joinPaths(toolchainPath, + "ToolchainInfo.plist") + + readonly property stringList _actualSigningIdentity: { + if (/^[A-Fa-f0-9]{40}$/.test(signingIdentity)) { + return signingIdentity; + } + + var identities = Xcode.findSigningIdentities(securityPath, signingIdentity); + if (identities && identities.length > 1) { + throw "Signing identity '" + signingIdentity + "' is ambiguous"; + } + + return identities; + } + + property path provisioningProfilesPath: { + return FileInfo.joinPaths(Environment.getEnv("HOME"), "Library/MobileDevice/Provisioning Profiles"); + } + + readonly property var _availableSdks: xcodeProbe.availableSdks + + readonly property var _sdkSettings: { + if (_availableSdks) { + for (var i in _availableSdks) { + if (_availableSdks[i]["Version"] === sdk) + return _availableSdks[i]; + if (_availableSdks[i]["CanonicalName"] === sdk) + return _availableSdks[i]; + } + + // Latest SDK available for the platform + if (DarwinTools.applePlatformName(qbs.targetOS, platformType) === sdk) + return _availableSdks[_availableSdks.length - 1]; + } + } + + qbs.sysroot: sdkPath + + validate: { + if (!_availableSdks) { + throw "There are no SDKs available for this platform in the Xcode installation."; + } + + if (!_sdkSettings) { + throw "There is no matching SDK available for " + sdk + "."; + } + + var validator = new ModUtils.PropertyValidator("xcode"); + validator.setRequiredProperty("developerPath", developerPath); + validator.setRequiredProperty("sdk", sdk); + validator.setRequiredProperty("sdkName", sdkName); + validator.setRequiredProperty("sdkVersion", sdkVersion); + validator.setRequiredProperty("toolchainsPath", toolchainsPath); + validator.setRequiredProperty("toolchainPath", toolchainPath); + validator.setRequiredProperty("platformsPath", platformsPath); + validator.setRequiredProperty("platformPath", platformPath); + validator.setRequiredProperty("sdksPath", sdkPath); + validator.setRequiredProperty("sdkPath", sdkPath); + validator.addVersionValidator("sdkVersion", sdkVersion, 2, 2); + validator.addCustomValidator("sdkName", sdkName, function (value) { + return value === DarwinTools.applePlatformDirectoryName( + qbs.targetOS, platformType, sdkVersion, false).toLowerCase(); + }, "is '" + sdkName + "', but target OS is [" + qbs.targetOS.join(",") + + "] and Xcode SDK version is '" + sdkVersion + "'"); + validator.addCustomValidator("sdk", sdk, function (value) { + return value === sdkName || (value + sdkVersion) === sdkName; + }, "is '" + sdk + "', but canonical SDK name is '" + sdkName + "'"); + validator.validate(); + } + + property var buildEnv: { + var env = { + "DEVELOPER_DIR": developerPath, + "SDKROOT": sdkPath + }; + + var prefixes = [platformPath + "/Developer", toolchainPath, developerPath]; + for (var i = 0; i < prefixes.length; ++i) { + var codesign_allocate = prefixes[i] + "/usr/bin/codesign_allocate"; + if (File.exists(codesign_allocate)) { + env["CODESIGN_ALLOCATE"] = codesign_allocate; + break; + } + } + + return env; + } + + setupBuildEnvironment: { + var v = new ModUtils.EnvironmentVariable("PATH", qbs.pathListSeparator, false); + v.prepend(platformPath + "/Developer/usr/bin"); + v.prepend(developerPath + "/usr/bin"); + v.set(); + + for (var key in buildEnv) { + v = new ModUtils.EnvironmentVariable(key); + v.value = buildEnv[key]; + v.set(); + } + } + + Group { + name: "Provisioning Profiles" + prefix: xcode.provisioningProfilesPath + "/" + files: ["*.mobileprovision", "*.provisionprofile"] + fileTags: [] // HACK: provisioning profile handling is not yet ready and can break autotests + } + + FileTagger { + fileTags: ["xcode.entitlements"] + patterns: ["*.entitlements"] + } + + FileTagger { + fileTags: ["xcode.provisioningprofile"] + patterns: ["*.mobileprovision", "*.provisionprofile"] + } + + Rule { + inputs: ["xcode.provisioningprofile"] + + Artifact { + filePath: FileInfo.joinPaths(product.destinationDirectory, + "provisioning-profiles", + input.fileName + ".xml") + fileTags: ["xcode.provisioningprofile.data"] + } + + prepare: { + var cmds = []; + + var cmd = new Command("openssl", ["smime", "-verify", "-noverify", "-inform", "DER", + "-in", input.filePath, "-out", output.filePath]); + cmd.silent = true; + cmd.stderrFilterFunction = function (output) { + return output.replace("Verification successful\n", ""); + }; + cmds.push(cmd); + + cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.inputFilePath = input.filePath; + cmd.outputFilePath = output.filePath; + cmd.sourceCode = function() { + var propertyList = new PropertyList(); + try { + propertyList.readFromFile(outputFilePath); + propertyList.readFromObject({ + data: propertyList.toObject(), + fileName: FileInfo.fileName(inputFilePath), + filePath: inputFilePath + }); + propertyList.writeToFile(outputFilePath, "xml1"); + } finally { + propertyList.clear(); + } + }; + cmds.push(cmd); + + return cmds; + } + } + + Rule { + multiplex: true + inputs: ["xcode.provisioningprofile.data"] + outputFileTags: ["xcode.provisioningprofile.main", "xcode.provisioningprofile.data.main"] + + outputArtifacts: { + var artifacts = []; + for (var i = 0; i < inputs["xcode.provisioningprofile.data"].length; ++i) { + var dataFile = inputs["xcode.provisioningprofile.data"][i].filePath; + var query = product.moduleProperty("xcode", "provisioningProfile"); + var obj = Xcode.provisioningProfilePlistContents(dataFile); + if (obj.data && (obj.data.UUID === query || obj.data.Name === query)) { + console.log("Using provisioning profile: " + obj.filePath); + + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, obj.fileName), + fileTags: ["xcode.provisioningprofile.main", obj.filePath] + }); + + artifacts.push({ + filePath: FileInfo.joinPaths(product.destinationDirectory, obj.fileName + ".xml"), + fileTags: ["xcode.provisioningprofile.data.main", dataFile] + }); + } + } + return artifacts; + } + + prepare: { + var cmds = []; + for (var tag in outputs) { + for (var i = 0; i < outputs[tag].length; ++i) { + var output = outputs[tag][i]; + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.inputFilePath = output.fileTags.filter(function(f) { return f.startsWith('/'); })[0] // QBS-754 + cmd.outputFilePath = output.filePath; + cmd.sourceCode = function() { + File.copy(inputFilePath, outputFilePath); + }; + cmds.push(cmd); + } + } + return cmds; + } + } + + Rule { + inputs: ["xcode.entitlements", "xcode.provisioningprofile.data.main"] + + Artifact { + filePath: FileInfo.joinPaths(product.destinationDirectory, + product.targetName + ".xcent") + fileTags: ["xcent"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating entitlements"; + cmd.highlight = "codegen"; + cmd.bundleIdentifier = product.moduleProperty("bundle", "identifier"); + cmd.signingEntitlements = inputs["xcode.entitlements"].map(function (a) { return a.filePath; }); + cmd.platformPath = ModUtils.moduleProperty(product, "platformPath"); + cmd.sdkPath = ModUtils.moduleProperty(product, "sdkPath"); + cmd.sourceCode = function() { + var i; + var provData = Xcode.provisioningProfilePlistContents(input.filePath); + if (provData) + provData = provData.data; + + var aggregateEntitlements = {}; + + // Start building up an aggregate entitlements plist from the files in the SDKs, + // which contain placeholders in the same manner as Info.plist + function entitlementsFileContents(path) { + return File.exists(path) ? BundleTools.infoPlistContents(path) : undefined; + } + var entitlementsSources = [ + entitlementsFileContents(FileInfo.joinPaths(platformPath, "Entitlements.plist")), + entitlementsFileContents(FileInfo.joinPaths(sdkPath, "Entitlements.plist")) + ]; + + for (i = 0; i < signingEntitlements.length; ++i) { + entitlementsSources.push(entitlementsFileContents(signingEntitlements[i])); + } + + for (i = 0; i < entitlementsSources.length; ++i) { + var contents = entitlementsSources[i]; + for (var key in contents) { + if (contents.hasOwnProperty(key)) + aggregateEntitlements[key] = contents[key]; + } + } + + contents = provData["Entitlements"]; + for (key in contents) { + if (contents.hasOwnProperty(key) && !aggregateEntitlements.hasOwnProperty(key)) + aggregateEntitlements[key] = contents[key]; + } + + // Expand entitlements variables with data from the provisioning profile + var env = { + "AppIdentifierPrefix": provData["ApplicationIdentifierPrefix"] + ".", + "CFBundleIdentifier": bundleIdentifier + }; + DarwinTools.expandPlistEnvironmentVariables(aggregateEntitlements, env, true); + + // Anything with an undefined or otherwise empty value should be removed + // Only JSON-formatted plists can have null values, other formats error out + // This also follows Xcode behavior + DarwinTools.cleanPropertyList(aggregateEntitlements); + + var plist = new PropertyList(); + try { + plist.readFromObject(aggregateEntitlements); + plist.writeToFile(outputs.xcent[0].filePath, "xml1"); + } finally { + plist.clear(); + } + }; + return [cmd]; + } + } +} diff --git a/share/share.qbs b/share/share.qbs new file mode 100644 index 00000000..8f1aa673 --- /dev/null +++ b/share/share.qbs @@ -0,0 +1,53 @@ +import qbs +import qbs.File +import qbs.FileInfo + +Product { + name: "qbs resources" + type: ["copied qbs resources"] + Depends { name: "qbsbuildconfig" } + + Group { + name: "Incredibuild" + prefix: "../bin/" + files: ["ibmsvc.xml", "ibqbs.bat"] + fileTags: [] + qbs.install: qbs.targetOS.contains("windows") + qbs.installDir: qbsbuildconfig.appInstallDir + } + + Group { + name: "Modules and imports" + files: ["qbs/**/*"] + fileTags: ["qbs resources"] + qbs.install: true + qbs.installDir: qbsbuildconfig.resourcesInstallDir + "/share" + qbs.installSourceBase: "." + } + + Group { + name: "Examples as resources" + files: ["../examples/**/*"] + fileTags: [] + qbs.install: true + qbs.installDir: qbsbuildconfig.resourcesInstallDir + "/share/qbs" + qbs.installSourceBase: ".." + } + + Rule { + inputs: ["qbs resources"] + Artifact { + filePath: FileInfo.joinPaths(project.buildDirectory, + product.moduleProperty("qbsbuildconfig", "resourcesInstallDir"), + "share", FileInfo.relativePath(product.sourceDirectory, input.filePath)) + fileTags: ["copied qbs resources"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Copying resource " + input.fileName + " to build directory."; + cmd.highlight = "codegen"; + cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); } + return cmd; + } + } +} diff --git a/src/app/app.pri b/src/app/app.pri new file mode 100644 index 00000000..5904c9dd --- /dev/null +++ b/src/app/app.pri @@ -0,0 +1,22 @@ +include(../install_prefix.pri) + +QT = core +TEMPLATE = app +!isEmpty(QBS_APPS_DESTDIR):DESTDIR = $${QBS_APPS_DESTDIR} +else:DESTDIR = ../../../bin + +!isEmpty(QBS_APPS_RPATH_DIR) { + linux-*:QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,$${QBS_APPS_RPATH_DIR}\' + macx:QMAKE_LFLAGS += -Wl,-rpath,$${QBS_APPS_RPATH_DIR} +} + +CONFIG += console +CONFIG -= app_bundle +CONFIG += c++11 + +include($${PWD}/../lib/corelib/use_corelib.pri) +include($${PWD}/shared/logging/logging.pri) + +!isEmpty(QBS_APPS_INSTALL_DIR):target.path = $${QBS_APPS_INSTALL_DIR} +else:target.path = $${QBS_INSTALL_PREFIX}/bin +INSTALLS += target diff --git a/src/app/app.pro b/src/app/app.pro new file mode 100644 index 00000000..bfa68615 --- /dev/null +++ b/src/app/app.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs +SUBDIRS =\ + qbs\ + qbs-setup-android \ + qbs-setup-toolchains \ + qbs-setup-qt \ + config \ + qbs-qmltypes + +!isEmpty(QT.widgets.name):SUBDIRS += config-ui diff --git a/src/app/apps.qbs b/src/app/apps.qbs new file mode 100644 index 00000000..4eaa5007 --- /dev/null +++ b/src/app/apps.qbs @@ -0,0 +1,13 @@ +import qbs + +Project { + references: [ + "config/config.qbs", + "config-ui/config-ui.qbs", + "qbs/qbs.qbs", + "qbs-qmltypes/qbs-qmltypes.qbs", + "qbs-setup-android/qbs-setup-android.qbs", + "qbs-setup-qt/qbs-setup-qt.qbs", + "qbs-setup-toolchains/qbs-setup-toolchains.qbs", + ] +} diff --git a/src/app/config-ui/Info.plist b/src/app/config-ui/Info.plist new file mode 100644 index 00000000..6b74be4a --- /dev/null +++ b/src/app/config-ui/Info.plist @@ -0,0 +1,14 @@ + + + + + CFBundleIdentifier + org.qt-project.qbs-config-ui + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Qbs Settings + LSUIElement + 1 + + diff --git a/src/app/config-ui/commandlineparser.cpp b/src/app/config-ui/commandlineparser.cpp new file mode 100644 index 00000000..32004d17 --- /dev/null +++ b/src/app/config-ui/commandlineparser.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandlineparser.h" + +#include +#include + +#include + +using qbs::Internal::Tr; + +static QString helpOptionShort() { return QLatin1String("-h"); } +static QString helpOptionLong() { return QLatin1String("--help"); } +static QString settingsDirOption() { return QLatin1String("--settings-dir"); } + +void CommandLineParser::parse(const QStringList &commandLine) +{ + m_commandLine = commandLine; + Q_ASSERT(!m_commandLine.isEmpty()); + m_command = QFileInfo(m_commandLine.takeFirst()).fileName(); + m_helpRequested = false; + m_settingsDir.clear(); + + if (m_commandLine.isEmpty()) + return; + const QString &arg = m_commandLine.first(); + if (arg == helpOptionShort() || arg == helpOptionLong()) { + m_commandLine.removeFirst(); + m_helpRequested = true; + } else if (arg == settingsDirOption()) { + m_commandLine.removeFirst(); + assignOptionArgument(settingsDirOption(), m_settingsDir); + } + + if (!m_commandLine.isEmpty()) + complainAboutExtraArguments(); +} + +void CommandLineParser::throwError(const QString &message) +{ + qbs::ErrorInfo error(Tr::tr("Syntax error: %1").arg(message)); + error.append(usageString()); + throw error; +} + +QString CommandLineParser::usageString() const +{ + QString s = Tr::tr("This tool displays qbs settings in a GUI.\n" + "If you have more than a few settings, this might be preferable to " + "plain \"qbs config\", as it presents a hierarchical view.\n"); + s += Tr::tr("Usage:\n"); + s += Tr::tr(" %1 [%2 ] [%3|%4]\n") + .arg(m_command, settingsDirOption(), helpOptionShort(), helpOptionLong()); + return s; +} + +void CommandLineParser::assignOptionArgument(const QString &option, QString &argument) +{ + if (m_commandLine.isEmpty()) + throwError(Tr::tr("Option '%1' needs an argument.").arg(option)); + argument = m_commandLine.takeFirst(); + if (argument.isEmpty()) + throwError(Tr::tr("Argument for option '%1' must not be empty.").arg(option)); +} + +void CommandLineParser::complainAboutExtraArguments() +{ + throwError(Tr::tr("Extraneous command-line arguments '%1'.") + .arg(m_commandLine.join(QLatin1Char(' ')))); +} diff --git a/src/app/config-ui/commandlineparser.h b/src/app/config-ui/commandlineparser.h new file mode 100644 index 00000000..8abda425 --- /dev/null +++ b/src/app/config-ui/commandlineparser.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_CONFIGUI_COMMANDLINEPARSER_H +#define QBS_CONFIGUI_COMMANDLINEPARSER_H + +#include + +class CommandLineParser +{ +public: + void parse(const QStringList &commandLine); + + bool helpRequested() const { return m_helpRequested; } + QString settingsDir() const { return m_settingsDir; } + + QString usageString() const; + +private: + void throwError(const QString &message); + void assignOptionArgument(const QString &option, QString &argument); + void complainAboutExtraArguments(); + + bool m_helpRequested; + QString m_settingsDir; + QStringList m_commandLine; + QString m_command; +}; + +#endif // Include guard diff --git a/src/app/config-ui/config-ui.pro b/src/app/config-ui/config-ui.pro new file mode 100644 index 00000000..84d3b3e4 --- /dev/null +++ b/src/app/config-ui/config-ui.pro @@ -0,0 +1,26 @@ +include(../app.pri) + +CONFIG -= console +QT += gui widgets + +TARGET = qbs-config-ui + +HEADERS += \ + commandlineparser.h \ + mainwindow.h + +SOURCES += \ + commandlineparser.cpp \ + main.cpp \ + mainwindow.cpp + +OTHER_FILES += \ + Info.plist + +osx { + QMAKE_LFLAGS += -sectcreate __TEXT __info_plist $$shell_quote($$PWD/Info.plist) + OBJECTIVE_SOURCES += fgapp.mm + LIBS += -framework ApplicationServices -framework Cocoa +} + +FORMS += mainwindow.ui diff --git a/src/app/config-ui/config-ui.qbs b/src/app/config-ui/config-ui.qbs new file mode 100644 index 00000000..33fe36ea --- /dev/null +++ b/src/app/config-ui/config-ui.qbs @@ -0,0 +1,31 @@ +import qbs 1.0 + +QbsApp { + Depends { name: "Qt.widgets" } + name: "qbs-config-ui" + consoleApplication: false + files: [ + "commandlineparser.cpp", + "commandlineparser.h", + "main.cpp", + "mainwindow.cpp", + "mainwindow.h", + "mainwindow.ui", + ] + + Group { + condition: qbs.targetOS.contains("macos") + files: [ + "fgapp.mm", + "Info.plist" + ] + } + + Properties { + condition: qbs.targetOS.contains("macos") + cpp.frameworks: ["ApplicationServices", "Cocoa"] + } + + Depends { name: "bundle" } + bundle.isBundle: false +} diff --git a/src/app/config-ui/fgapp.mm b/src/app/config-ui/fgapp.mm new file mode 100644 index 00000000..5ddd1e8a --- /dev/null +++ b/src/app/config-ui/fgapp.mm @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import +#include + +extern "C" void qt_macos_forceTransformProcessToForegroundApplicationAndActivate() +{ + [[NSApplication sharedApplication] setActivationPolicy:NSApplicationActivationPolicyRegular]; + [[NSApplication sharedApplication] activateIgnoringOtherApps:YES]; +} diff --git a/src/app/config-ui/main.cpp b/src/app/config-ui/main.cpp new file mode 100644 index 00000000..cec37baa --- /dev/null +++ b/src/app/config-ui/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "mainwindow.h" + +#include "commandlineparser.h" + +#include +#include + +#include +#include +#include + +using qbs::Internal::Tr; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + CommandLineParser clParser; + try { + clParser.parse(app.arguments()); + if (clParser.helpRequested()) { + std::cout << qPrintable(clParser.usageString()); + return EXIT_SUCCESS; + } + } catch (const qbs::ErrorInfo &error) { + std::cerr << qPrintable(error.toString()); + return EXIT_FAILURE; + } + + MainWindow mw(clParser.settingsDir()); + mw.show(); + return app.exec(); +} diff --git a/src/app/config-ui/mainwindow.cpp b/src/app/config-ui/mainwindow.cpp new file mode 100644 index 00000000..250e1c59 --- /dev/null +++ b/src/app/config-ui/mainwindow.cpp @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MainWindow::MainWindow(const QString &settingsDir, QWidget *parent) + : QMainWindow(parent), ui(new Ui::MainWindow) +{ + ui->setupUi(this); + m_model = new qbs::SettingsModel(settingsDir, this); + ui->treeView->setModel(m_model); + ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->treeView, &QTreeView::expanded, this, &MainWindow::adjustColumns); + connect(ui->treeView, &QWidget::customContextMenuRequested, + this, &MainWindow::provideContextMenu); + adjustColumns(); + + QMenu * const fileMenu = menuBar()->addMenu(tr("&File")); + QMenu * const viewMenu = menuBar()->addMenu(tr("&View")); + + QAction * const reloadAction = new QAction(tr("&Reload"), this); + reloadAction->setShortcut(QKeySequence::Refresh); + connect(reloadAction, &QAction::triggered, this, &MainWindow::reloadSettings); + QAction * const saveAction = new QAction(tr("&Save"), this); + saveAction->setShortcut(QKeySequence::Save); + connect(saveAction, &QAction::triggered, this, &MainWindow::saveSettings); + QAction * const expandAllAction = new QAction(tr("&Expand All"), this); + expandAllAction->setShortcut(Qt::CTRL | Qt::Key_E); + connect(expandAllAction, &QAction::triggered, this, &MainWindow::expandAll); + QAction * const collapseAllAction = new QAction(tr("C&ollapse All"), this); + collapseAllAction->setShortcut(Qt::CTRL | Qt::Key_O); + connect(collapseAllAction, &QAction::triggered, this, &MainWindow::collapseAll); + QAction * const exitAction = new QAction(tr("E&xit"), this); + exitAction->setShortcut(QKeySequence::Quit); + exitAction->setMenuRole(QAction::QuitRole); + connect(exitAction, &QAction::triggered, this, &MainWindow::exit); + + fileMenu->addAction(reloadAction); + fileMenu->addAction(saveAction); + fileMenu->addSeparator(); + fileMenu->addAction(exitAction); + + viewMenu->addAction(expandAllAction); + viewMenu->addAction(collapseAllAction); + + ui->treeView->installEventFilter(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::adjustColumns() +{ + for (int column = 0; column < m_model->columnCount(); ++column) + ui->treeView->resizeColumnToContents(column); +} + +void MainWindow::expandAll() +{ + ui->treeView->expandAll(); + adjustColumns(); +} + +void MainWindow::collapseAll() +{ + ui->treeView->collapseAll(); + adjustColumns(); +} + +void MainWindow::reloadSettings() +{ + if (m_model->hasUnsavedChanges()) { + const QMessageBox::StandardButton answer = QMessageBox::question(this, + tr("Unsaved Changes"), + tr("You have unsaved changes. Do you want to discard them?")); + if (answer != QMessageBox::Yes) + return; + } + m_model->reload(); +} + +void MainWindow::saveSettings() +{ + m_model->save(); +} + +void MainWindow::exit() +{ + if (m_model->hasUnsavedChanges()) { + const QMessageBox::StandardButton answer = QMessageBox::question(this, + tr("Unsaved Changes"), + tr("You have unsaved changes. Do you want to save them now?")); + if (answer == QMessageBox::Yes) + m_model->save(); + } + qApp->quit(); +} + +void MainWindow::provideContextMenu(const QPoint &pos) +{ + const QModelIndex index = ui->treeView->indexAt(pos); + if (index.isValid() && index.column() != m_model->keyColumn()) + return; + const QString settingsKey = m_model->data(index).toString(); + + QMenu contextMenu; + QAction addKeyAction(this); + QAction removeKeyAction(this); + if (index.isValid()) { + addKeyAction.setText(tr("Add new key below '%1'").arg(settingsKey)); + removeKeyAction.setText(tr("Remove key '%1' and all its sub-keys").arg(settingsKey)); + contextMenu.addAction(&addKeyAction); + contextMenu.addAction(&removeKeyAction); + } else { + addKeyAction.setText(tr("Add new top-level key")); + contextMenu.addAction(&addKeyAction); + } + + const QAction *action = contextMenu.exec(ui->treeView->mapToGlobal(pos)); + if (action == &addKeyAction) + m_model->addNewKey(index); + else if (action == &removeKeyAction) + m_model->removeKey(index); +} + +extern "C" void qt_macos_forceTransformProcessToForegroundApplicationAndActivate(); + +bool MainWindow::eventFilter(QObject *watched, QEvent *event) +{ + if (ui->treeView->hasFocus() && event->type() == QEvent::KeyPress) { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->matches(QKeySequence::Delete)) { + const QModelIndexList indexes = ui->treeView->selectionModel()->selectedRows(); + if (indexes.count() == 1) { + const QModelIndex index = indexes.first(); + if (index.isValid()) { + m_model->removeKey(index); + return true; + } + } + } + } + + if (event->type() == QEvent::WindowActivate) { + // Effectively delay the foreground process transformation from QApplication construction to + // when the UI is shown - this prevents the application icon from popping up in the Dock + // when running `qbs help`, and QCoreApplication::arguments() requires the application + // object to be constructed, so it is not easily worked around + #if defined(Q_OS_MACOS) || defined(Q_OS_OSX) + qt_macos_forceTransformProcessToForegroundApplicationAndActivate(); + #endif + } + + + return QMainWindow::eventFilter(watched, event); +} diff --git a/src/app/config-ui/mainwindow.h b/src/app/config-ui/mainwindow.h new file mode 100644 index 00000000..c5ee0e57 --- /dev/null +++ b/src/app/config-ui/mainwindow.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +namespace qbs { class SettingsModel; } + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +class QPoint; +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(const QString &settingsDir, QWidget *parent = 0); + ~MainWindow(); + + bool eventFilter(QObject *watched, QEvent *event); + +private: + void adjustColumns(); + void expandAll(); + void collapseAll(); + void reloadSettings(); + void saveSettings(); + void exit(); + void provideContextMenu(const QPoint &pos); + + Ui::MainWindow *ui; + qbs::SettingsModel *m_model; +}; + +#endif // MAINWINDOW_H diff --git a/src/app/config-ui/mainwindow.ui b/src/app/config-ui/mainwindow.ui new file mode 100644 index 00000000..7a3416ce --- /dev/null +++ b/src/app/config-ui/mainwindow.ui @@ -0,0 +1,37 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + Qbs Settings + + + + + + + + + + + + 0 + 0 + 800 + 27 + + + + + + + + diff --git a/src/app/config/config.pro b/src/app/config/config.pro new file mode 100644 index 00000000..278c481d --- /dev/null +++ b/src/app/config/config.pro @@ -0,0 +1,13 @@ +include(../app.pri) + +TARGET = qbs-config + +SOURCES += \ + configcommandexecutor.cpp \ + configcommandlineparser.cpp \ + configmain.cpp + +HEADERS += \ + configcommand.h \ + configcommandexecutor.h \ + configcommandlineparser.h diff --git a/src/app/config/config.qbs b/src/app/config/config.qbs new file mode 100644 index 00000000..9daa701f --- /dev/null +++ b/src/app/config/config.qbs @@ -0,0 +1,14 @@ +import qbs 1.0 + +QbsApp { + name: "qbs-config" + files: [ + "configcommand.h", + "configcommandexecutor.cpp", + "configcommandexecutor.h", + "configcommandlineparser.cpp", + "configcommandlineparser.h", + "configmain.cpp" + ] +} + diff --git a/src/app/config/configcommand.h b/src/app/config/configcommand.h new file mode 100644 index 00000000..e86be48f --- /dev/null +++ b/src/app/config/configcommand.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CONFIGCOMMAND_H +#define CONFIGCOMMAND_H + +#include + +#include +#include + +class ConfigCommand +{ +public: + enum Command { CfgSet, CfgUnset, CfgList, CfgExport, CfgImport, CfgNone }; + + ConfigCommand() : command(CfgNone) {} + + Command command; + QStringList varNames; + QString varValue; + QString fileName; +}; + +#endif // CONFIGCOMMAND_H diff --git a/src/app/config/configcommandexecutor.cpp b/src/app/config/configcommandexecutor.cpp new file mode 100644 index 00000000..7e36aad8 --- /dev/null +++ b/src/app/config/configcommandexecutor.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "configcommandexecutor.h" + +#include "configcommand.h" +#include "../shared/logging/consolelogger.h" + +#include +#include + +#include +#include +#include + +#include + +using namespace qbs; + +ConfigCommandExecutor::ConfigCommandExecutor(Settings *settings) : m_settings(settings) +{ +} + +void ConfigCommandExecutor::execute(const ConfigCommand &command) +{ + switch (command.command) { + case ConfigCommand::CfgList: + printSettings(command); + break; + case ConfigCommand::CfgSet: + setValue(command.varNames.first(), command.varValue); + break; + case ConfigCommand::CfgUnset: + foreach (const QString &varName, command.varNames) + m_settings->remove(varName); + break; + case ConfigCommand::CfgExport: + exportSettings(command.fileName); + break; + case ConfigCommand::CfgImport: + // Display old and new settings, in case import fails or user accidentally nukes everything + printf("old "); // Will end up as "old settings:" + printSettings(command); + importSettings(command.fileName); + printf("\nnew "); + printSettings(command); + break; + case ConfigCommand::CfgNone: + qFatal("%s: Impossible command value.", Q_FUNC_INFO); + break; + } +} + +void ConfigCommandExecutor::setValue(const QString &key, const QString &rawInput) +{ + m_settings->setValue(key, representationToSettingsValue(rawInput)); +} + +void ConfigCommandExecutor::printSettings(const ConfigCommand &command) +{ + if (command.varNames.isEmpty()) { + foreach (const QString &key, m_settings->allKeys()) + printOneSetting(key); + } else { + foreach (const QString &parentKey, command.varNames) { + if (m_settings->value(parentKey).isValid()) { // Key is a leaf. + printOneSetting(parentKey); + } else { // Key is a node. + foreach (const QString &key, m_settings->allKeysWithPrefix(parentKey)) + printOneSetting(parentKey + QLatin1Char('.') + key); + } + } + } +} + +void ConfigCommandExecutor::printOneSetting(const QString &key) +{ + printf("%s: %s\n", qPrintable(key), + qPrintable(qbs::settingsValueToRepresentation(m_settings->value(key)))); + } + +void ConfigCommandExecutor::exportSettings(const QString &filename) +{ + QFile file(filename); + if (!file.open(QFile::Truncate | QFile::WriteOnly | QFile::Text)) { + throw ErrorInfo(tr("Could not open file '%1' for writing: %2") + .arg(QDir::toNativeSeparators(filename), file.errorString())); + } + QTextStream stream(&file); + stream.setCodec("UTF-8"); + foreach (const QString &key, m_settings->allKeys()) + stream << key << ": " << qbs::settingsValueToRepresentation(m_settings->value(key)) << endl; +} + +void ConfigCommandExecutor::importSettings(const QString &filename) +{ + QFile file(filename); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + throw ErrorInfo(tr("Could not open file '%1' for reading: %2") + .arg(QDir::toNativeSeparators(filename), file.errorString())); + } + // Remove all current settings + foreach (const QString &key, m_settings->allKeys()) + m_settings->remove(key); + + QTextStream stream(&file); + stream.setCodec("UTF-8"); + while (!stream.atEnd()) { + QString line = stream.readLine(); + int colon = line.indexOf(QLatin1Char(':')); + if (colon >= 0 && !line.startsWith(QLatin1Char('#'))) { + const QString key = line.left(colon).trimmed(); + const QString value = line.mid(colon + 1).trimmed(); + m_settings->setValue(key, representationToSettingsValue(value)); + } + } +} diff --git a/src/app/config/configcommandexecutor.h b/src/app/config/configcommandexecutor.h new file mode 100644 index 00000000..50c92df9 --- /dev/null +++ b/src/app/config/configcommandexecutor.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CONFIGCOMMANDEXECUTOR_H +#define CONFIGCOMMANDEXECUTOR_H + +#include + +namespace qbs { class Settings; } + +class ConfigCommand; + +class ConfigCommandExecutor +{ + Q_DECLARE_TR_FUNCTIONS(ConfigCommandExecutor) +public: + ConfigCommandExecutor(qbs::Settings *settings); + + void execute(const ConfigCommand &command); + +private: + void setValue(const QString &key, const QString &rawInput); + void printSettings(const ConfigCommand &command); + void printOneSetting(const QString &key); + void exportSettings(const QString &filename); + void importSettings(const QString &filename); + + qbs::Settings *m_settings; +}; + +#endif // CONFIGCOMMANDEXECUTOR_H diff --git a/src/app/config/configcommandlineparser.cpp b/src/app/config/configcommandlineparser.cpp new file mode 100644 index 00000000..595a23ef --- /dev/null +++ b/src/app/config/configcommandlineparser.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "configcommandlineparser.h" + +#include +#include + +#include + +using namespace qbs; +using namespace Internal; + +void ConfigCommandLineParser::parse(const QStringList &commandLine) +{ + m_command = ConfigCommand(); + m_helpRequested = false; + m_settingsDir.clear(); + + m_commandLine = commandLine; + if (m_commandLine.isEmpty()) + throw ErrorInfo(Tr::tr("No parameters supplied.")); + if (m_commandLine.count() == 1 && (m_commandLine.first() == QLatin1String("--help") + || m_commandLine.first() == QLatin1String("-h"))) { + m_helpRequested = true; + return; + } + + while (!m_commandLine.isEmpty() && m_commandLine.first().startsWith(QLatin1String("--"))) { + const QString arg = m_commandLine.takeFirst().mid(2); + if (arg == QLatin1String("list")) + setCommand(ConfigCommand::CfgList); + else if (arg == QLatin1String("unset")) + setCommand(ConfigCommand::CfgUnset); + else if (arg == QLatin1String("export")) + setCommand(ConfigCommand::CfgExport); + else if (arg == QLatin1String("import")) + setCommand(ConfigCommand::CfgImport); + else if (arg == QLatin1String("settings-dir")) + assignOptionArgument(arg, m_settingsDir); + else + throw ErrorInfo(Tr::tr("Unknown option for config command.")); + } + + switch (command().command) { + case ConfigCommand::CfgNone: + if (m_commandLine.isEmpty()) + throw ErrorInfo(Tr::tr("No parameters supplied.")); + if (m_commandLine.count() > 2) + throw ErrorInfo(Tr::tr("Too many arguments.")); + m_command.varNames << m_commandLine.first(); + if (m_commandLine.count() == 1) { + setCommand(ConfigCommand::CfgList); + } else { + m_command.varValue = m_commandLine.at(1); + setCommand(ConfigCommand::CfgSet); + } + break; + case ConfigCommand::CfgUnset: + if (m_commandLine.isEmpty()) + throw ErrorInfo(Tr::tr("Need name of variable to unset.")); + m_command.varNames = m_commandLine; + break; + case ConfigCommand::CfgExport: + if (m_commandLine.count() != 1) + throw ErrorInfo(Tr::tr("Need name of file to which to export.")); + m_command.fileName = m_commandLine.first(); + break; + case ConfigCommand::CfgImport: + if (m_commandLine.count() != 1) + throw ErrorInfo(Tr::tr("Need name of file from which to import.")); + m_command.fileName = m_commandLine.first(); + break; + case ConfigCommand::CfgList: + m_command.varNames = m_commandLine; + break; + default: + break; + } +} + +void ConfigCommandLineParser::setCommand(ConfigCommand::Command command) +{ + if (m_command.command != ConfigCommand::CfgNone) + throw ErrorInfo(Tr::tr("You cannot specify more than one command.")); + m_command.command = command; +} + +void ConfigCommandLineParser::printUsage() const +{ + puts("Usage:\n" + " qbs config [--settings-dir \n" + " qbs config [--settings-dir \n" + " qbs config [--settings-dir " + "\n" + "Options:\n" + " --list [ ...] list keys under key or all keys\n" + " --unset remove key with given name\n" + " --import import settings from given file\n" + " --export export settings to given file\n"); +} + +void ConfigCommandLineParser::assignOptionArgument(const QString &option, QString &argument) +{ + if (m_commandLine.isEmpty()) + throw ErrorInfo(Tr::tr("Option '%1' needs an argument.").arg(option)); + argument = m_commandLine.takeFirst(); + if (argument.isEmpty()) + throw ErrorInfo(Tr::tr("Argument for option '%1' must not be empty.").arg(option)); +} diff --git a/src/app/config/configcommandlineparser.h b/src/app/config/configcommandlineparser.h new file mode 100644 index 00000000..9d3b1420 --- /dev/null +++ b/src/app/config/configcommandlineparser.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef COMMANDLINEPARSER_H +#define COMMANDLINEPARSER_H + +#include "configcommand.h" + +#include + +class ConfigCommandLineParser +{ +public: + void parse(const QStringList &commandLine); + + ConfigCommand command() const { return m_command; } + + QString settingsDir() const { return m_settingsDir; } + bool helpRequested() const { return m_helpRequested; } + void printUsage() const; + +private: + void assignOptionArgument(const QString &option, QString &argument); + void setCommand(ConfigCommand::Command command); + + ConfigCommand m_command; + bool m_helpRequested; + QString m_settingsDir; + QStringList m_commandLine; +}; + +#endif // COMMANDLINEPARSER_H diff --git a/src/app/config/configmain.cpp b/src/app/config/configmain.cpp new file mode 100644 index 00000000..d8a4e241 --- /dev/null +++ b/src/app/config/configmain.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "configcommandlineparser.h" +#include "configcommandexecutor.h" + +#include +#include +#include + +#include + +#include +#include + +using qbs::Internal::Tr; +using qbs::Settings; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + ConfigCommandLineParser parser; + try { + parser.parse(app.arguments().mid(1)); + if (parser.helpRequested()) { + std::cout << qPrintable(Tr::tr("This tool manages qbs settings.")) << std::endl; + parser.printUsage(); + return EXIT_SUCCESS; + } + Settings settings(parser.settingsDir()); + ConfigCommandExecutor(&settings).execute(parser.command()); + } catch (const qbs::ErrorInfo &e) { + std::cerr << qPrintable(e.toString()) << std::endl; + parser.printUsage(); + return EXIT_FAILURE; + } +} diff --git a/src/app/qbs-qmltypes/main.cpp b/src/app/qbs-qmltypes/main.cpp new file mode 100644 index 00000000..cbf7839e --- /dev/null +++ b/src/app/qbs-qmltypes/main.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "../shared/logging/consolelogger.h" + +#include +#include + +#include +#include + +#include +#include + +using qbs::Internal::Tr; + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + ConsoleLogger::instance(); + + const QStringList args = app.arguments().mid(1); + if (args.count() == 1 && (args.first() == QLatin1String("--help") + || args.first() == QLatin1String("-h"))) { + qbsInfo() << Tr::tr("This tool dumps information about the QML types supported by qbs.\n" + "It takes no command-line parameters.\n" + "The output is intended to be processed by other tools and has " + "little value for humans."); + return EXIT_SUCCESS; + } + if (!args.isEmpty()) { + qbsWarning() << Tr::tr("You supplied command-line parameters, " + "but this tool does not use any."); + } + + qbs::LanguageInfo languageInfo; + const QByteArray typeData = languageInfo.qmlTypeInfo(); + + std::cout << typeData.constData(); + + return 0; +} diff --git a/src/app/qbs-qmltypes/qbs-qmltypes.pro b/src/app/qbs-qmltypes/qbs-qmltypes.pro new file mode 100644 index 00000000..9e344aa7 --- /dev/null +++ b/src/app/qbs-qmltypes/qbs-qmltypes.pro @@ -0,0 +1,6 @@ +include(../app.pri) + +TARGET = qbs-qmltypes + +SOURCES += \ + main.cpp \ diff --git a/src/app/qbs-qmltypes/qbs-qmltypes.qbs b/src/app/qbs-qmltypes/qbs-qmltypes.qbs new file mode 100644 index 00000000..f7368a1a --- /dev/null +++ b/src/app/qbs-qmltypes/qbs-qmltypes.qbs @@ -0,0 +1,9 @@ +import qbs 1.0 + +QbsApp { + name: "qbs-qmltypes" + files: [ + "main.cpp" + ] +} + diff --git a/src/app/qbs-setup-android/android-setup.cpp b/src/app/qbs-setup-android/android-setup.cpp new file mode 100644 index 00000000..5d6a49b5 --- /dev/null +++ b/src/app/qbs-setup-android/android-setup.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "android-setup.h" + +#include +#include +#include +#include +#include + +#include +#include + +using namespace qbs; +using qbs::Internal::Tr; + +static QString qls(const char *s) { return QLatin1String(s); } + +static QStringList expectedArchs() +{ + return QStringList() + << QStringLiteral("arm64") + << QStringLiteral("armv5te") + << QStringLiteral("armv7a") + << QStringLiteral("mips") + << QStringLiteral("mips64") + << QStringLiteral("x86") + << QStringLiteral("x86_64"); +} + + +static QString subProfileName(const QString &mainProfileName, const QString &arch) +{ + return mainProfileName + QLatin1Char('-') + arch; +} + +void setupSdk(qbs::Settings *settings, const QString &profileName, const QString &sdkDirPath) +{ + if (!QDir(sdkDirPath).exists()) { + throw ErrorInfo(Tr::tr("SDK directory '%1' does not exist.") + .arg(QDir::toNativeSeparators(sdkDirPath))); + } + + Profile profile(profileName, settings); + profile.removeProfile(); + if (!sdkDirPath.isEmpty()) + profile.setValue(qls("Android.sdk.sdkDir"), QDir::cleanPath(sdkDirPath)); + profile.setValue(qls("qbs.targetOS"), QStringList() << qls("android") << qls("linux") + << qls("unix")); +} + +void setupNdk(qbs::Settings *settings, const QString &profileName, const QString &ndkDirPath) +{ + if (!QDir(ndkDirPath).exists()) { + throw ErrorInfo(Tr::tr("NDK directory '%1' does not exist.") + .arg(QDir::toNativeSeparators(ndkDirPath))); + } + + Profile mainProfile(profileName, settings); + if (!ndkDirPath.isEmpty()) { + mainProfile.setValue(qls("Android.ndk.ndkDir"), QDir::cleanPath(ndkDirPath)); + mainProfile.setValue(qls("Android.sdk.ndkDir"), QDir::cleanPath(ndkDirPath)); + } + mainProfile.setValue(qls("qbs.toolchain"), QStringList() << qls("gcc")); + foreach (const QString &arch, expectedArchs()) { + Profile p(subProfileName(profileName, arch), settings); + p.removeProfile(); + p.setBaseProfile(mainProfile.name()); + p.setValue(qls("qbs.architecture"), arch); + } +} + +void setupAndroid(Settings *settings, const QString &profileName, const QString &sdkDirPath, + const QString &ndkDirPath) +{ + setupSdk(settings, profileName, sdkDirPath); + setupNdk(settings, profileName, ndkDirPath); +} diff --git a/src/app/qbs-setup-android/android-setup.h b/src/app/qbs-setup-android/android-setup.h new file mode 100644 index 00000000..6314f5c7 --- /dev/null +++ b/src/app/qbs-setup-android/android-setup.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SETUP_ANDROID_SDKSETUP_H +#define QBS_SETUP_ANDROID_SDKSETUP_H + +#include + +namespace qbs { class Settings; } + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +void setupAndroid(qbs::Settings *settings, const QString &profileName, const QString &sdkDirPath, + const QString &ndkDirPath); + +#endif // Include guard. + diff --git a/src/app/qbs-setup-android/commandlineparser.cpp b/src/app/qbs-setup-android/commandlineparser.cpp new file mode 100644 index 00000000..e5ac535f --- /dev/null +++ b/src/app/qbs-setup-android/commandlineparser.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandlineparser.h" + +#include +#include + +#include + +CommandLineParser::CommandLineParser() +{ + +} + +using qbs::Internal::Tr; + +static QString helpOptionShort() { return QLatin1String("-h"); } +static QString helpOptionLong() { return QLatin1String("--help"); } +static QString settingsDirOption() { return QLatin1String("--settings-dir"); } +static QString sdkDirOption() { return QLatin1String("--sdk-dir"); } +static QString ndkDirOption() { return QLatin1String("--ndk-dir"); } + +void CommandLineParser::parse(const QStringList &commandLine) +{ + m_commandLine = commandLine; + Q_ASSERT(!m_commandLine.isEmpty()); + m_command = QFileInfo(m_commandLine.takeFirst()).fileName(); + m_helpRequested = false; + m_sdkDir.clear(); + m_ndkDir.clear(); + m_profileName.clear(); + m_settingsDir.clear(); + + if (m_commandLine.isEmpty()) + throwError(Tr::tr("No command-line arguments provided.")); + + while (!m_commandLine.isEmpty()) { + const QString arg = m_commandLine.first(); + if (!arg.startsWith(QLatin1String("--"))) + break; + m_commandLine.removeFirst(); + if (arg == helpOptionShort() || arg == helpOptionLong()) + m_helpRequested = true; + else if (arg == settingsDirOption()) + assignOptionArgument(settingsDirOption(), m_settingsDir); + else if (arg == sdkDirOption()) + assignOptionArgument(sdkDirOption(), m_sdkDir); + else if (arg == ndkDirOption()) + assignOptionArgument(ndkDirOption(), m_ndkDir); + } + + if (m_helpRequested) { + if (!m_commandLine.isEmpty()) + complainAboutExtraArguments(); + return; + } + + switch (m_commandLine.count()) { + case 0: + throwError(Tr::tr("No profile name supplied.")); + case 1: + m_profileName = m_commandLine.takeFirst(); + break; + default: + complainAboutExtraArguments(); + } +} + +void CommandLineParser::throwError(const QString &message) +{ + qbs::ErrorInfo error(Tr::tr("Syntax error: %1").arg(message)); + error.append(usageString()); + throw error; +} + +QString CommandLineParser::usageString() const +{ + QString s = Tr::tr("This tool creates qbs profiles from Android SDK and NDK installations.\n"); + s += Tr::tr("Usage:\n"); + s += Tr::tr(" %1 [%2 ] [%3 ] [%4 ] \n") + .arg(m_command, settingsDirOption(), ndkDirOption(), sdkDirOption()); + s += Tr::tr(" %1 %2|%3\n").arg(m_command, helpOptionShort(), helpOptionLong()); + s += Tr::tr("If an NDK path is given, additional profiles will be created for each " + "architecture supported by the NDK.\n" + "Their names will be of the form
      _."); + return s; +} + +void CommandLineParser::assignOptionArgument(const QString &option, QString &argument) +{ + if (m_commandLine.isEmpty()) + throwError(Tr::tr("Option '%1' needs an argument.").arg(option)); + argument = m_commandLine.takeFirst(); + if (argument.isEmpty()) + throwError(Tr::tr("Argument for option '%1' must not be empty.").arg(option)); +} + +void CommandLineParser::complainAboutExtraArguments() +{ + throwError(Tr::tr("Extraneous command-line arguments '%1'.") + .arg(m_commandLine.join(QLatin1Char(' ')))); +} + diff --git a/src/app/qbs-setup-android/commandlineparser.h b/src/app/qbs-setup-android/commandlineparser.h new file mode 100644 index 00000000..cd3c6884 --- /dev/null +++ b/src/app/qbs-setup-android/commandlineparser.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_SETUP_ANDROID_COMMANDLINEPARSER_H +#define QBS_SETUP_ANDROID_COMMANDLINEPARSER_H + +#include + +class CommandLineParser +{ +public: + CommandLineParser(); + + void parse(const QStringList &commandLine); + + bool helpRequested() const { return m_helpRequested; } + + QString sdkDir() const { return m_sdkDir; } + QString ndkDir() const { return m_ndkDir; } + QString profileName() const { return m_profileName; } + QString settingsDir() const { return m_settingsDir; } + + QString usageString() const; + +private: + Q_NORETURN void throwError(const QString &message); + void assignOptionArgument(const QString &option, QString &argument); + Q_NORETURN void complainAboutExtraArguments(); + + bool m_helpRequested; + QString m_sdkDir; + QString m_ndkDir; + QString m_profileName; + QString m_settingsDir; + QStringList m_commandLine; + QString m_command; +}; + +#endif // Include guard. diff --git a/src/app/qbs-setup-android/main.cpp b/src/app/qbs-setup-android/main.cpp new file mode 100644 index 00000000..37b4c82f --- /dev/null +++ b/src/app/qbs-setup-android/main.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "commandlineparser.h" +#include "android-setup.h" + +#include +#include +#include + +#include + +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + CommandLineParser clParser; + try { + clParser.parse(app.arguments()); + if (clParser.helpRequested()) { + std::cout << qPrintable(clParser.usageString()) << std::endl; + return EXIT_SUCCESS; + } + qbs::Settings settings(clParser.settingsDir()); + setupAndroid(&settings, clParser.profileName(), clParser.sdkDir(), clParser.ndkDir()); + } catch (const qbs::ErrorInfo &e) { + std::cerr << qPrintable(qbs::Internal::Tr::tr("Error: %1").arg(e.toString())) << std::endl; + return EXIT_FAILURE; + } +} diff --git a/src/app/qbs-setup-android/qbs-setup-android.pro b/src/app/qbs-setup-android/qbs-setup-android.pro new file mode 100644 index 00000000..b5e57761 --- /dev/null +++ b/src/app/qbs-setup-android/qbs-setup-android.pro @@ -0,0 +1,12 @@ +include(../app.pri) + +TARGET = qbs-setup-android + +SOURCES += \ + android-setup.cpp \ + commandlineparser.cpp \ + main.cpp + +HEADERS += \ + android-setup.h \ + commandlineparser.h diff --git a/src/app/qbs-setup-android/qbs-setup-android.qbs b/src/app/qbs-setup-android/qbs-setup-android.qbs new file mode 100644 index 00000000..0eb67f9c --- /dev/null +++ b/src/app/qbs-setup-android/qbs-setup-android.qbs @@ -0,0 +1,12 @@ +import qbs + +QbsApp { + name: "qbs-setup-android" + files: [ + "android-setup.cpp", + "android-setup.h", + "commandlineparser.cpp", + "commandlineparser.h", + "main.cpp", + ] +} diff --git a/src/app/qbs-setup-qt/commandlineparser.cpp b/src/app/qbs-setup-qt/commandlineparser.cpp new file mode 100644 index 00000000..2f12a92b --- /dev/null +++ b/src/app/qbs-setup-qt/commandlineparser.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandlineparser.h" + +#include +#include + +#include + +using qbs::Internal::Tr; + +static QString helpOptionShort() { return QLatin1String("-h"); } +static QString helpOptionLong() { return QLatin1String("--help"); } +static QString detectOption() { return QLatin1String("--detect"); } +static QString settingsDirOption() { return QLatin1String("--settings-dir"); } + +void CommandLineParser::parse(const QStringList &commandLine) +{ + m_commandLine = commandLine; + Q_ASSERT(!m_commandLine.isEmpty()); + m_command = QFileInfo(m_commandLine.takeFirst()).fileName(); + m_helpRequested = false; + m_autoDetectionMode = false; + m_qmakePath.clear(); + m_profileName.clear(); + m_settingsDir.clear(); + + if (m_commandLine.isEmpty()) + throwError(Tr::tr("No command-line arguments provided.")); + + while (!m_commandLine.isEmpty()) { + const QString arg = m_commandLine.first(); + if (!arg.startsWith(QLatin1String("--"))) + break; + m_commandLine.removeFirst(); + if (arg == helpOptionShort() || arg == helpOptionLong()) + m_helpRequested = true; + else if (arg == detectOption()) + m_autoDetectionMode = true; + else if (arg == settingsDirOption()) + assignOptionArgument(settingsDirOption(), m_settingsDir); + } + + if (m_helpRequested || m_autoDetectionMode) { + if (!m_commandLine.isEmpty()) + complainAboutExtraArguments(); + return; + } + + switch (m_commandLine.count()) { + case 0: + case 1: + throwError(Tr::tr("Not enough command-line arguments provided.")); + case 2: + m_qmakePath = m_commandLine.at(0); + m_profileName = m_commandLine.at(1); + break; + default: + complainAboutExtraArguments(); + } +} + +void CommandLineParser::throwError(const QString &message) +{ + qbs::ErrorInfo error(Tr::tr("Syntax error: %1").arg(message)); + error.append(usageString()); + throw error; +} + +QString CommandLineParser::usageString() const +{ + QString s = Tr::tr("This tool creates qbs profiles from Qt versions.\n"); + s += Tr::tr("Usage:\n"); + s += Tr::tr(" %1 [%2 ] %3\n") + .arg(m_command, settingsDirOption(), detectOption()); + s += Tr::tr(" %1 [%2 ] \n") + .arg(m_command, settingsDirOption()); + s += Tr::tr(" %1 %2|%3\n").arg(m_command, helpOptionShort(), helpOptionLong()); + s += Tr::tr("The first form tries to auto-detect all known Qt versions, looking them up " + "via the PATH environment variable.\n"); + s += Tr::tr("The second form creates one profile for one Qt version."); + return s; +} + +void CommandLineParser::assignOptionArgument(const QString &option, QString &argument) +{ + if (m_commandLine.isEmpty()) + throwError(Tr::tr("Option '%1' needs an argument.").arg(option)); + argument = m_commandLine.takeFirst(); + if (argument.isEmpty()) + throwError(Tr::tr("Argument for option '%1' must not be empty.").arg(option)); +} + +void CommandLineParser::complainAboutExtraArguments() +{ + throwError(Tr::tr("Extraneous command-line arguments '%1'.") + .arg(m_commandLine.join(QLatin1Char(' ')))); +} diff --git a/src/app/qbs-setup-qt/commandlineparser.h b/src/app/qbs-setup-qt/commandlineparser.h new file mode 100644 index 00000000..eda3c43c --- /dev/null +++ b/src/app/qbs-setup-qt/commandlineparser.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H +#define QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H + +#include + +class CommandLineParser +{ +public: + void parse(const QStringList &commandLine); + + bool helpRequested() const { return m_helpRequested; } + bool autoDetectionMode() const { return m_autoDetectionMode; } + + QString qmakePath() const { return m_qmakePath; } + QString profileName() const { return m_profileName; } + QString settingsDir() const { return m_settingsDir; } + + QString usageString() const; + +private: + void throwError(const QString &message); + void assignOptionArgument(const QString &option, QString &argument); + void complainAboutExtraArguments(); + + bool m_helpRequested; + bool m_autoDetectionMode; + QString m_qmakePath; + QString m_profileName; + QString m_settingsDir; + QStringList m_commandLine; + QString m_command; +}; + +#endif // Include guard diff --git a/src/app/qbs-setup-qt/main.cpp b/src/app/qbs-setup-qt/main.cpp new file mode 100644 index 00000000..7164d883 --- /dev/null +++ b/src/app/qbs-setup-qt/main.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "setupqt.h" + +#include "commandlineparser.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +using namespace qbs; +using Internal::Tr; + +int main(int argc, char *argv[]) +{ + QCoreApplication application(argc, argv); + + try { + CommandLineParser clParser; + clParser.parse(application.arguments()); + + if (clParser.helpRequested()) { + std::cout << qPrintable(clParser.usageString()) << std::endl; + return EXIT_SUCCESS; + } + + Settings settings(clParser.settingsDir()); + + if (clParser.autoDetectionMode()) { + // search all Qt's in path and dump their settings + QList qtEnvironments = SetupQt::fetchEnvironments(); + foreach (const QtEnvironment &qtEnvironment, qtEnvironments) { + QString profileName = QLatin1String("qt-") + qtEnvironment.qtVersion; + if (SetupQt::checkIfMoreThanOneQtWithTheSameVersion(qtEnvironment.qtVersion, qtEnvironments)) { + QStringList prefixPathParts = qtEnvironment.installPrefixPath + .split(QLatin1Char('/'), QString::SkipEmptyParts); + if (!prefixPathParts.isEmpty()) + profileName += QLatin1String("-") + prefixPathParts.last(); + } + SetupQt::saveToQbsSettings(profileName, qtEnvironment, &settings); + } + return EXIT_SUCCESS; + } + + if (!SetupQt::isQMakePathValid(clParser.qmakePath())) { + std::cerr << qPrintable(Tr::tr("'%1' does not seem to be a qmake executable.") + .arg(clParser.qmakePath())) << std::endl; + return EXIT_FAILURE; + } + + QtEnvironment qtEnvironment = SetupQt::fetchEnvironment(clParser.qmakePath()); + QString profileName = clParser.profileName(); + profileName.replace(QLatin1Char('.'), QLatin1Char('-')); + SetupQt::saveToQbsSettings(profileName, qtEnvironment, &settings); + return EXIT_SUCCESS; + } catch (const ErrorInfo &e) { + std::cerr << qPrintable(e.toString()) << std::endl; + return EXIT_FAILURE; + } +} diff --git a/src/app/qbs-setup-qt/qbs-setup-qt.exe.manifest b/src/app/qbs-setup-qt/qbs-setup-qt.exe.manifest new file mode 100644 index 00000000..a0b8dbac --- /dev/null +++ b/src/app/qbs-setup-qt/qbs-setup-qt.exe.manifest @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/app/qbs-setup-qt/qbs-setup-qt.pro b/src/app/qbs-setup-qt/qbs-setup-qt.pro new file mode 100644 index 00000000..036a4e74 --- /dev/null +++ b/src/app/qbs-setup-qt/qbs-setup-qt.pro @@ -0,0 +1,17 @@ +include(../app.pri) +include($${PWD}/../../lib/qtprofilesetup/use_qtprofilesetup.pri) + +TARGET = qbs-setup-qt + +SOURCES += \ + commandlineparser.cpp \ + main.cpp \ + setupqt.cpp + +HEADERS += \ + commandlineparser.h \ + setupqt.h + +mingw { + RC_FILE = qbs-setup-qt.rc +} diff --git a/src/app/qbs-setup-qt/qbs-setup-qt.qbs b/src/app/qbs-setup-qt/qbs-setup-qt.qbs new file mode 100644 index 00000000..033f046a --- /dev/null +++ b/src/app/qbs-setup-qt/qbs-setup-qt.qbs @@ -0,0 +1,19 @@ +import qbs 1.0 + +QbsApp { + name: "qbs-setup-qt" + Depends { name: "qbsqtprofilesetup" } + files: [ + "commandlineparser.cpp", + "commandlineparser.h", + "main.cpp", + "setupqt.cpp", + "setupqt.h" + ] + Group { + name: "MinGW specific files" + condition: qbs.toolchain.contains("mingw") + files: ["qbs-setup-qt.rc"] + } +} + diff --git a/src/app/qbs-setup-qt/qbs-setup-qt.rc b/src/app/qbs-setup-qt/qbs-setup-qt.rc new file mode 100644 index 00000000..ad2507e4 --- /dev/null +++ b/src/app/qbs-setup-qt/qbs-setup-qt.rc @@ -0,0 +1,4 @@ +#define RT_MANIFEST 24 +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "qbs-setup-qt.exe.manifest" diff --git a/src/app/qbs-setup-qt/setupqt.cpp b/src/app/qbs-setup-qt/setupqt.cpp new file mode 100644 index 00000000..b9531566 --- /dev/null +++ b/src/app/qbs-setup-qt/setupqt.cpp @@ -0,0 +1,590 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "setupqt.h" + +#include "../shared/logging/consolelogger.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace qbs { +using Internal::HostOsInfo; +using Internal::Tr; +using Internal::Version; + +static QStringList qmakeExecutableNames() +{ + const QString baseName = HostOsInfo::appendExecutableSuffix(QStringLiteral("qmake")); + QStringList lst(baseName); + if (HostOsInfo::isLinuxHost()) { + // Some distributions ship binaries called qmake-qt5 or qmake-qt4. + lst << baseName + QLatin1String("-qt5") << baseName + QLatin1String("-qt4"); + } + return lst; +} + +static QStringList collectQmakePaths() +{ + const QStringList qmakeExeNames = qmakeExecutableNames(); + QStringList qmakePaths; + QByteArray environmentPath = qgetenv("PATH"); + QList environmentPaths + = environmentPath.split(HostOsInfo::pathListSeparator().toLatin1()); + foreach (const QByteArray &path, environmentPaths) { + foreach (const QString &qmakeExecutableName, qmakeExeNames) { + QFileInfo pathFileInfo(QDir(QLatin1String(path)), qmakeExecutableName); + if (pathFileInfo.exists()) { + QString qmakePath = pathFileInfo.absoluteFilePath(); + if (!qmakePaths.contains(qmakePath)) + qmakePaths.append(qmakePath); + } + } + } + + return qmakePaths; +} + +bool SetupQt::isQMakePathValid(const QString &qmakePath) +{ + QFileInfo qmakeFileInfo(qmakePath); + return qmakeFileInfo.exists() && qmakeFileInfo.isFile() && qmakeFileInfo.isExecutable(); +} + +QList SetupQt::fetchEnvironments() +{ + QList qtEnvironments; + + foreach (const QString &qmakePath, collectQmakePaths()) { + const QtEnvironment env = fetchEnvironment(qmakePath); + if (std::find_if(qtEnvironments.constBegin(), qtEnvironments.constEnd(), + [&env](const QtEnvironment &otherEnv) { + return env.includePath == otherEnv.includePath; + }) == qtEnvironments.constEnd()) { + qtEnvironments.append(env); + } + } + + return qtEnvironments; +} + +void SetupQt::addQtBuildVariant(QtEnvironment *env, const QString &buildVariantName) +{ + if (env->qtConfigItems.contains(buildVariantName)) + env->buildVariant << buildVariantName; +} + +static QMap qmakeQueryOutput(const QString &qmakePath) +{ + QProcess qmakeProcess; + qmakeProcess.start(qmakePath, QStringList() << QLatin1String("-query")); + if (!qmakeProcess.waitForStarted()) + throw ErrorInfo(SetupQt::tr("%1 cannot be started.").arg(qmakePath)); + qmakeProcess.waitForFinished(); + const QByteArray output = qmakeProcess.readAllStandardOutput(); + + QMap ret; + foreach (const QByteArray &line, output.split('\n')) { + int idx = line.indexOf(':'); + if (idx >= 0) + ret[line.left(idx)] = line.mid(idx + 1).trimmed(); + } + return ret; +} + +static QByteArray readFileContent(const QString &filePath) +{ + QFile file(filePath); + if (file.open(QFile::ReadOnly)) + return file.readAll(); + + return QByteArray(); +} + +static QString configVariable(const QByteArray &configContent, const QString &key) +{ + QRegExp regexp(QLatin1String("\\s*") + key + QLatin1String("\\s*\\+{0,1}=(.*)"), + Qt::CaseSensitive); + + QList configContentLines = configContent.split('\n'); + + bool success = false; + + foreach (const QByteArray &configContentLine, configContentLines) { + success = regexp.exactMatch(QString::fromLocal8Bit(configContentLine)); + if (success) + break; + } + + if (success) + return regexp.capturedTexts()[1].simplified(); + + return QString(); +} + +static QStringList configVariableItems(const QByteArray &configContent, const QString &key) +{ + return configVariable(configContent, key).split(QLatin1Char(' '), QString::SkipEmptyParts); +} + +typedef QMap QueryMap; + +static QString pathQueryValue(const QueryMap &queryMap, const QByteArray &key) +{ + return QDir::fromNativeSeparators(QString::fromLocal8Bit(queryMap.value(key))); +} + +QtEnvironment SetupQt::fetchEnvironment(const QString &qmakePath) +{ + QtEnvironment qtEnvironment; + QueryMap queryOutput = qmakeQueryOutput(qmakePath); + + qtEnvironment.installPrefixPath = pathQueryValue(queryOutput, "QT_INSTALL_PREFIX"); + qtEnvironment.documentationPath = pathQueryValue(queryOutput, "QT_INSTALL_DOCS"); + qtEnvironment.includePath = pathQueryValue(queryOutput, "QT_INSTALL_HEADERS"); + qtEnvironment.libraryPath = pathQueryValue(queryOutput, "QT_INSTALL_LIBS"); + qtEnvironment.binaryPath = pathQueryValue(queryOutput, "QT_HOST_BINS"); + if (qtEnvironment.binaryPath.isEmpty()) + qtEnvironment.binaryPath = pathQueryValue(queryOutput, "QT_INSTALL_BINS"); + qtEnvironment.documentationPath = pathQueryValue(queryOutput, "QT_INSTALL_DOCS"); + qtEnvironment.pluginPath = pathQueryValue(queryOutput, "QT_INSTALL_PLUGINS"); + qtEnvironment.qmlPath = pathQueryValue(queryOutput, "QT_INSTALL_QML"); + qtEnvironment.qmlImportPath = pathQueryValue(queryOutput, "QT_INSTALL_IMPORTS"); + qtEnvironment.qtVersion = QString::fromLocal8Bit(queryOutput.value("QT_VERSION")); + + const Version qtVersion = Version::fromString(qtEnvironment.qtVersion); + + QString mkspecsBaseSrcPath; + if (qtVersion.majorVersion() >= 5) { + qtEnvironment.mkspecBasePath + = pathQueryValue(queryOutput, "QT_HOST_DATA") + QLatin1String("/mkspecs"); + mkspecsBaseSrcPath + = pathQueryValue(queryOutput, "QT_HOST_DATA/src") + QLatin1String("/mkspecs"); + } else { + qtEnvironment.mkspecBasePath + = pathQueryValue(queryOutput, "QT_INSTALL_DATA") + QLatin1String("/mkspecs"); + } + + if (!QFile::exists(qtEnvironment.mkspecBasePath)) + throw ErrorInfo(tr("Cannot extract the mkspecs directory.")); + + const QByteArray qconfigContent = readFileContent(qtEnvironment.mkspecBasePath + + QLatin1String("/qconfig.pri")); + qtEnvironment.qtMajorVersion = configVariable(qconfigContent, + QLatin1String("QT_MAJOR_VERSION")).toInt(); + qtEnvironment.qtMinorVersion = configVariable(qconfigContent, + QLatin1String("QT_MINOR_VERSION")).toInt(); + qtEnvironment.qtPatchVersion = configVariable(qconfigContent, + QLatin1String("QT_PATCH_VERSION")).toInt(); + qtEnvironment.qtNameSpace = configVariable(qconfigContent, QLatin1String("QT_NAMESPACE")); + qtEnvironment.qtLibInfix = configVariable(qconfigContent, QLatin1String("QT_LIBINFIX")); + qtEnvironment.architecture = configVariable(qconfigContent, QLatin1String("QT_TARGET_ARCH")); + if (qtEnvironment.architecture.isEmpty()) + qtEnvironment.architecture = configVariable(qconfigContent, QLatin1String("QT_ARCH")); + if (qtEnvironment.architecture.isEmpty()) + qtEnvironment.architecture = QLatin1String("x86"); + qtEnvironment.configItems = configVariableItems(qconfigContent, QLatin1String("CONFIG")); + qtEnvironment.qtConfigItems = configVariableItems(qconfigContent, QLatin1String("QT_CONFIG")); + + // retrieve the mkspec + if (qtVersion.majorVersion() >= 5) { + const QString mkspecName = QString::fromLocal8Bit(queryOutput.value("QMAKE_XSPEC")); + qtEnvironment.mkspecName = mkspecName; + qtEnvironment.mkspecPath = qtEnvironment.mkspecBasePath + QLatin1Char('/') + mkspecName; + if (!mkspecsBaseSrcPath.isEmpty() && !QFile::exists(qtEnvironment.mkspecPath)) + qtEnvironment.mkspecPath = mkspecsBaseSrcPath + QLatin1Char('/') + mkspecName; + } else { + if (HostOsInfo::isWindowsHost()) { + const QString baseDirPath = qtEnvironment.mkspecBasePath + QLatin1String("/default/"); + const QByteArray fileContent = readFileContent(baseDirPath + + QLatin1String("qmake.conf")); + qtEnvironment.mkspecPath = configVariable(fileContent, + QLatin1String("QMAKESPEC_ORIGINAL")); + if (!QFile::exists(qtEnvironment.mkspecPath)) { + // Work around QTBUG-28792. + // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me? + const QRegExp rex(QLatin1String("\\binclude\\(([^)]+)/qmake\\.conf\\)")); + if (rex.indexIn(QString::fromLocal8Bit(fileContent)) != -1) + qtEnvironment.mkspecPath = QDir::cleanPath(baseDirPath + rex.cap(1)); + } + } else { + qtEnvironment.mkspecPath = QFileInfo(qtEnvironment.mkspecBasePath + + QLatin1String("/default")).symLinkTarget(); + } + + // E.g. in qmake.conf for Qt 4.8/mingw we find this gem: + // QMAKESPEC_ORIGINAL=C:\\Qt\\Qt\\4.8\\mingw482\\mkspecs\\win32-g++ + qtEnvironment.mkspecPath = QDir::cleanPath(qtEnvironment.mkspecPath); + + qtEnvironment.mkspecName = qtEnvironment.mkspecPath; + int idx = qtEnvironment.mkspecName.lastIndexOf(QLatin1Char('/')); + if (idx != -1) + qtEnvironment.mkspecName.remove(0, idx + 1); + } + + // determine whether we have a framework build + qtEnvironment.frameworkBuild = false; + if (qtEnvironment.mkspecPath.contains(QLatin1String("macx"))) { + if (qtEnvironment.configItems.contains(QLatin1String("qt_framework"))) + qtEnvironment.frameworkBuild = true; + else if (!qtEnvironment.configItems.contains(QLatin1String("qt_no_framework"))) + throw ErrorInfo(tr("could not determine whether Qt is a frameworks build")); + } + + // determine whether Qt is built with debug, release or both + addQtBuildVariant(&qtEnvironment, QLatin1String("debug")); + addQtBuildVariant(&qtEnvironment, QLatin1String("release")); + + if (!QFileInfo(qtEnvironment.mkspecPath).exists()) + throw ErrorInfo(tr("mkspec '%1' does not exist").arg(qtEnvironment.mkspecPath)); + + return qtEnvironment; +} + +static bool isToolchainProfile(const Profile &profile) +{ + const QSet actual = profile.allKeys(Profile::KeySelectionRecursive).toSet(); + QSet expected = QSet() << QLatin1String("qbs.toolchain"); + if (HostOsInfo::isMacosHost()) + expected.insert(QLatin1String("qbs.targetOS")); // match only Xcode profiles + return QSet(actual).unite(expected) == actual; +} + +static bool isQtProfile(const Profile &profile) +{ + bool hasQtKey = false; + foreach (const QString &key, profile.allKeys(Profile::KeySelectionRecursive)) { + if (key.startsWith(QLatin1String("Qt."))) { + hasQtKey = true; + break; + } + } + + return hasQtKey; +} + +template bool areProfilePropertiesIncompatible(const T &set1, const T &set2) +{ + // Two objects are only considered incompatible if they are both non empty and compare inequal + // This logic is used for comparing target OS, toolchain lists, and architectures + return !set1.isEmpty() && !set2.isEmpty() && set1 != set2; +} + +static QStringList qbsTargetOsFromQtMkspec(const QString &mkspec) +{ + if (mkspec.startsWith(QLatin1String("aix-"))) + return QStringList() << QLatin1String("aix") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("android-"))) + return QStringList() << QLatin1String("android") << QLatin1String("linux") + << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("blackberry"))) + return QStringList() << QLatin1String("blackberry") << QLatin1String("qnx") + << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("darwin-"))) + return QStringList() << QLatin1String("darwin") << QLatin1String("bsd") + << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("freebsd-"))) + return QStringList() << QLatin1String("freebsd") << QLatin1String("bsd") + << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("haiku-"))) + return QStringList() << QLatin1String("haiku"); + if (mkspec.startsWith(QLatin1String("hpux-")) || mkspec.startsWith(QLatin1String("hpuxi-"))) + return QStringList() << QLatin1String("hpux") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("hurd-"))) + return QStringList() << QLatin1String("hurd") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("irix-"))) + return QStringList() << QLatin1String("irix") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("linux-"))) + return QStringList() << QLatin1String("linux") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("lynxos-"))) + return QStringList() << QLatin1String("lynx") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("macx-"))) + return QStringList() << (mkspec.startsWith(QLatin1String("macx-ios-")) + ? QLatin1String("ios") : QLatin1String("macos")) + << QLatin1String("darwin") << QLatin1String("bsd") + << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("nacl-")) || mkspec.startsWith(QLatin1String("nacl64-"))) + return QStringList() << QLatin1String("nacl"); + if (mkspec.startsWith(QLatin1String("netbsd-"))) + return QStringList() << QLatin1String("netbsd") << QLatin1String("bsd") + << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("openbsd-"))) + return QStringList() << QLatin1String("openbsd") << QLatin1String("bsd") + << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("qnx-"))) + return QStringList() << QLatin1String("qnx") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("sco-"))) + return QStringList() << QLatin1String("sco") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("solaris-"))) + return QStringList() << QLatin1String("solaris") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("tru64-"))) + return QStringList() << QLatin1String("tru64") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("unixware-"))) + return QStringList() << QLatin1String("unixware") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("vxworks-"))) + return QStringList() << QLatin1String("vxworks") << QLatin1String("unix"); + if (mkspec.startsWith(QLatin1String("win32-"))) + return QStringList() << QLatin1String("windows"); + if (mkspec.startsWith(QLatin1String("wince"))) + return QStringList() << QLatin1String("windowsce") << QLatin1String("windows"); + if (mkspec.startsWith(QLatin1String("winphone-"))) + return QStringList() << QLatin1String("windowsphone") << QLatin1String("winrt") << QLatin1String("windows"); + if (mkspec.startsWith(QLatin1String("winrt-"))) + return QStringList() << QLatin1String("winrt") << QLatin1String("windows"); + return QStringList(); +} + +static QStringList qbsToolchainFromQtMkspec(const QString &mkspec) +{ + if (mkspec.contains(QLatin1String("-msvc"))) + return QStringList() << QLatin1String("msvc"); + if (mkspec == QLatin1String("win32-g++")) + return QStringList() << QLatin1String("mingw") << QLatin1String("gcc"); + + if (mkspec.contains(QLatin1String("-clang"))) + return QStringList() << QLatin1String("clang") << QLatin1String("llvm") + << QLatin1String("gcc"); + if (mkspec.contains(QLatin1String("-llvm"))) + return QStringList() << QLatin1String("llvm") << QLatin1String("gcc"); + if (mkspec.contains(QLatin1String("-g++"))) + return QStringList() << QLatin1String("gcc"); + + // Worry about other, less common toolchains (ICC, QCC, etc.) later... + return QStringList(); +} + +static const QString msvcPrefix = QLatin1String("win32-msvc"); + +static bool isMsvcQt(const QtEnvironment &env) +{ + return env.mkspecName.startsWith(msvcPrefix); +} + +static Version msvcCompilerVersionForYear(int year) +{ + switch (year) + { + case 2005: + return Version(14); + case 2008: + return Version(15); + case 2010: + return Version(16); + case 2012: + return Version(17); + case 2013: + return Version(18); + case 2015: + return Version(19); + case 2017: + return Version(19, 1); + default: + return Version(); + } +} + +static Version msvcCompilerVersionForYear(const QtEnvironment &env) +{ + return isMsvcQt(env) + ? msvcCompilerVersionForYear(env.mkspecName.mid(msvcPrefix.size()).toInt()) + : Version(); +} + +enum Match { MatchFull, MatchPartial, MatchNone }; + +static Match compatibility(const QtEnvironment &env, const Profile &toolchainProfile, + const Version &msvcCompilerVersion) +{ + Match match = MatchFull; + + const QSet toolchainNames = toolchainProfile.value(QLatin1String("qbs.toolchain")) + .toStringList().toSet(); + const QSet mkspecToolchainNames = qbsToolchainFromQtMkspec(env.mkspecName).toSet(); + if (areProfilePropertiesIncompatible(toolchainNames, mkspecToolchainNames)) { + QSet intersection = toolchainNames; + intersection.intersect(mkspecToolchainNames); + if (!intersection.isEmpty()) + match = MatchPartial; + else + return MatchNone; + } + + const QSet targetOsNames = toolchainProfile.value(QLatin1String("qbs.targetOS")) + .toStringList().toSet(); + if (areProfilePropertiesIncompatible(qbsTargetOsFromQtMkspec(env.mkspecName).toSet(), + targetOsNames)) + return MatchNone; + + const QString toolchainArchitecture = toolchainProfile.value(QLatin1String("qbs.architecture")) + .toString(); + if (areProfilePropertiesIncompatible(canonicalArchitecture(env.architecture), + canonicalArchitecture(toolchainArchitecture))) + return MatchNone; + + if (msvcCompilerVersion.isValid()) { + // We want to know for sure that MSVC compiler versions match, + // because it's especially important for this toolchain + const Version fullCompilerVersion = Version::fromString( + toolchainProfile.value(QLatin1String("cpp.compilerVersion")).toString()); + const Version shortCompilerVersion = Version(fullCompilerVersion.majorVersion(), + fullCompilerVersion.minorVersion()); + if (msvcCompilerVersion != shortCompilerVersion) + return MatchNone; + } + + return match; +} + +static bool isMsvcCrossCompilerProfile(const QString &profileName) +{ + return profileName.contains(QLatin1Char('_')); +} + +static void compressMsvcProfiles(QStringList &profiles) +{ + if (std::all_of(profiles.constBegin(), profiles.constEnd(), isMsvcCrossCompilerProfile)) + return; + + profiles.erase(std::remove_if(profiles.begin(), profiles.end(), isMsvcCrossCompilerProfile), + profiles.end()); +} + +void SetupQt::saveToQbsSettings(const QString &qtVersionName, const QtEnvironment &qtEnvironment, + Settings *settings) +{ + const QString cleanQtVersionName = Profile::cleanName(qtVersionName); + QString msg = QCoreApplication::translate("SetupQt", "Creating profile '%1'.") + .arg(cleanQtVersionName); + printf("%s\n", qPrintable(msg)); + + const ErrorInfo errorInfo = setupQtProfile(cleanQtVersionName, settings, qtEnvironment); + if (errorInfo.hasError()) + throw errorInfo; + + // If this profile does not specify a toolchain and we find exactly one profile that looks + // like it might have been added by qbs-setup-toolchains, let's use that one as our + // base profile. + Profile profile(cleanQtVersionName, settings); + if (!profile.baseProfile().isEmpty()) + return; + if (isToolchainProfile(profile)) + return; + + QStringList fullMatches; + QStringList partialMatches; + const Version msvcCompilerVersion = msvcCompilerVersionForYear(qtEnvironment); + foreach (const QString &profileName, settings->profiles()) { + const Profile otherProfile(profileName, settings); + if (profileName == profile.name() + || !isToolchainProfile(otherProfile) + || isQtProfile(otherProfile)) + continue; + + switch (compatibility(qtEnvironment, otherProfile, msvcCompilerVersion)) { + case MatchFull: + fullMatches << profileName; + break; + case MatchPartial: + partialMatches << profileName; + break; + default: + break; + } + } + + if (msvcCompilerVersion.isValid() && fullMatches.size() > 1) + compressMsvcProfiles(fullMatches); + + QString bestMatch; + if (fullMatches.count() == 1) + bestMatch = fullMatches.first(); + else if (fullMatches.isEmpty() && partialMatches.count() == 1) + bestMatch = partialMatches.first(); + if (bestMatch.isEmpty()) { + QString message = Tr::tr("You need to set up toolchain information before you can " + "use this Qt version for building. "); + if (fullMatches.isEmpty() && partialMatches.isEmpty()) { + message += Tr::tr("However, no toolchain profile was found. Either create one " + "using qbs-setup-toolchains and set it as this profile's " + "base profile or add the toolchain settings manually " + "to this profile."); + } else { + message += Tr::tr("Consider setting one of these profiles as this profile's base " + "profile: %1.").arg((fullMatches + partialMatches) + .join(QLatin1String(", "))); + } + qbsWarning() << message; + } else { + profile.setBaseProfile(bestMatch); + qbsInfo() << Tr::tr("Setting profile '%1' as the base profile for this profile.") + .arg(bestMatch); + } +} + +bool SetupQt::checkIfMoreThanOneQtWithTheSameVersion(const QString &qtVersion, + const QList &qtEnvironments) +{ + bool foundOneVersion = false; + foreach (const QtEnvironment &qtEnvironment, qtEnvironments) { + if (qtEnvironment.qtVersion == qtVersion) { + if (foundOneVersion) + return true; + foundOneVersion = true; + } + } + + return false; +} + +} // namespace qbs diff --git a/src/app/qbs-setup-qt/setupqt.h b/src/app/qbs-setup-qt/setupqt.h new file mode 100644 index 00000000..fc611736 --- /dev/null +++ b/src/app/qbs-setup-qt/setupqt.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SETUPQT_H +#define QBS_SETUPQT_H + +#include +#include +#include + +namespace qbs { +class QtEnvironment; +class Settings; + +class SetupQt +{ + Q_DECLARE_TR_FUNCTIONS(SetupQt) +public: + static bool isQMakePathValid(const QString &qmakePath); + static QList fetchEnvironments(); + static void addQtBuildVariant(QtEnvironment *env, const QString &buildVariantName); + static QtEnvironment fetchEnvironment(const QString &qmakePath); + static void saveToQbsSettings(const QString &qtVersionName, const QtEnvironment & qtEnvironment, + Settings *settings); + static bool checkIfMoreThanOneQtWithTheSameVersion(const QString &qtVersion, + const QList &qtEnvironments); +}; + +} // namespace qbs + +#endif // QBS_SETUPQT_H diff --git a/src/app/qbs-setup-toolchains/commandlineparser.cpp b/src/app/qbs-setup-toolchains/commandlineparser.cpp new file mode 100644 index 00000000..84f7abe9 --- /dev/null +++ b/src/app/qbs-setup-toolchains/commandlineparser.cpp @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandlineparser.h" + +#include +#include + +#include + +using qbs::Internal::Tr; + +static QString helpOptionShort() { return QLatin1String("-h"); } +static QString helpOptionLong() { return QLatin1String("--help"); } +static QString detectOption() { return QLatin1String("--detect"); } +static QString typeOption() { return QLatin1String("--type"); } +static QString settingsDirOption() { return QLatin1String("--settings-dir"); } + +void CommandLineParser::parse(const QStringList &commandLine) +{ + m_commandLine = commandLine; + Q_ASSERT(!m_commandLine.isEmpty()); + m_command = QFileInfo(m_commandLine.takeFirst()).fileName(); + m_helpRequested = false; + m_autoDetectionMode = false; + m_compilerPath.clear(); + m_toolchainType.clear(); + m_profileName.clear(); + m_settingsDir.clear(); + + if (m_commandLine.isEmpty()) + throwError(Tr::tr("No command-line arguments provided.")); + + while (!m_commandLine.isEmpty()) { + const QString arg = m_commandLine.first(); + if (!arg.startsWith(QLatin1String("--"))) + break; + m_commandLine.removeFirst(); + if (arg == helpOptionShort() || arg == helpOptionLong()) + m_helpRequested = true; + else if (arg == detectOption()) + m_autoDetectionMode = true; + else if (arg == typeOption()) + assignOptionArgument(typeOption(), m_toolchainType); + else if (arg == settingsDirOption()) + assignOptionArgument(settingsDirOption(), m_settingsDir); + } + + if (m_helpRequested || m_autoDetectionMode) { + if (!m_commandLine.isEmpty()) + complainAboutExtraArguments(); + return; + } + + switch (m_commandLine.count()) { + case 0: + case 1: + throwError(Tr::tr("Not enough command-line arguments provided.")); + case 2: + m_compilerPath = m_commandLine.at(0); + m_profileName = m_commandLine.at(1); + break; + default: + complainAboutExtraArguments(); + } +} + +void CommandLineParser::throwError(const QString &message) +{ + qbs::ErrorInfo error(Tr::tr("Syntax error: %1").arg(message)); + error.append(usageString()); + throw error; +} + +QString CommandLineParser::usageString() const +{ + QString s = Tr::tr("This tool creates qbs profiles from toolchains.\n"); + s += Tr::tr("Usage:\n"); + s += Tr::tr(" %1 [%2 ] %3\n") + .arg(m_command, settingsDirOption(), detectOption()); + s += Tr::tr(" %1 [%3 ] [%2 ] " + " \n") + .arg(m_command, typeOption(), settingsDirOption()); + s += Tr::tr(" %1 %2|%3\n").arg(m_command, helpOptionShort(), helpOptionLong()); + s += Tr::tr("The first form tries to auto-detect all known toolchains, looking them up " + "via the PATH environment variable.\n"); + s += Tr::tr("The second form creates one profile for one toolchain. It will attempt " + "to find out the toolchain type automatically.\nIn case the compiler has " + "an unusual file name, you may need to provide the '--type' option."); + return s; +} + +void CommandLineParser::assignOptionArgument(const QString &option, QString &argument) +{ + if (m_commandLine.isEmpty()) + throwError(Tr::tr("Option '%1' needs an argument.").arg(option)); + argument = m_commandLine.takeFirst(); + if (argument.isEmpty()) + throwError(Tr::tr("Argument for option '%1' must not be empty.").arg(option)); +} + +void CommandLineParser::complainAboutExtraArguments() +{ + throwError(Tr::tr("Extraneous command-line arguments '%1'.") + .arg(m_commandLine.join(QLatin1Char(' ')))); +} diff --git a/src/app/qbs-setup-toolchains/commandlineparser.h b/src/app/qbs-setup-toolchains/commandlineparser.h new file mode 100644 index 00000000..f7821666 --- /dev/null +++ b/src/app/qbs-setup-toolchains/commandlineparser.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H +#define QBS_SETUPTOOLCHAINS_COMMANDLINEPARSER_H + +#include + +class CommandLineParser +{ +public: + void parse(const QStringList &commandLine); + + bool helpRequested() const { return m_helpRequested; } + bool autoDetectionMode() const { return m_autoDetectionMode; } + + QString compilerPath() const { return m_compilerPath; } + QString toolchainType() const { return m_toolchainType; } + QString profileName() const { return m_profileName; } + QString settingsDir() const { return m_settingsDir; } + + QString usageString() const; + +private: + void throwError(const QString &message); + void assignOptionArgument(const QString &option, QString &argument); + void complainAboutExtraArguments(); + + bool m_helpRequested; + bool m_autoDetectionMode; + QString m_compilerPath; + QString m_toolchainType; + QString m_profileName; + QString m_settingsDir; + QStringList m_commandLine; + QString m_command; +}; + +#endif // Include guard diff --git a/src/app/qbs-setup-toolchains/main.cpp b/src/app/qbs-setup-toolchains/main.cpp new file mode 100644 index 00000000..55e806f9 --- /dev/null +++ b/src/app/qbs-setup-toolchains/main.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "commandlineparser.h" +#include "probe.h" + +#include +#include +#include + +#include + +#include +#include + +using qbs::Internal::Tr; +using qbs::Settings; + +static void printUsage(const QString &usageString) +{ + std::cout << qPrintable(usageString) << std::endl; +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + CommandLineParser clParser; + try { + clParser.parse(app.arguments()); + if (clParser.helpRequested()) { + printUsage(clParser.usageString()); + return EXIT_SUCCESS; + } + Settings settings(clParser.settingsDir()); + if (clParser.autoDetectionMode()) { + probe(&settings); + return EXIT_SUCCESS; + } + createProfile(clParser.profileName(), clParser.toolchainType(), clParser.compilerPath(), + &settings); + } catch (const qbs::ErrorInfo &e) { + std::cerr << qPrintable(e.toString()) << std::endl; + return EXIT_FAILURE; + } +} diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp new file mode 100644 index 00000000..bb1838f2 --- /dev/null +++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvcprobe.h" + +#include "probe.h" +#include "../shared/logging/consolelogger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace qbs; +using namespace qbs::Internal; +using Internal::Tr; + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(WinSDK, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(MSVC, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +// Not necessary but helps setup-qt automatically associate base profiles +static void setQtHelperProperties(Profile &p, const MSVC *msvc) +{ + QString targetArch = msvc->architecture.split(QLatin1Char('_')).last(); + if (targetArch.isEmpty()) + targetArch = QStringLiteral("x86"); + if (targetArch == QStringLiteral("arm")) + targetArch = QStringLiteral("armv7"); + + p.setValue(QStringLiteral("qbs.architecture"), canonicalArchitecture(targetArch)); + p.setValue(QStringLiteral("cpp.compilerVersion"), msvc->compilerVersion.toString()); +} + +static void addMSVCPlatform(Settings *settings, QList &profiles, QString name, MSVC *msvc) +{ + qbsInfo() << Tr::tr("Setting up profile '%1'.").arg(name); + Profile p(name, settings); + p.removeProfile(); + p.setValue(QLatin1String("qbs.targetOS"), QStringList(QLatin1String("windows"))); + p.setValue(QLatin1String("qbs.toolchain"), QStringList(QLatin1String("msvc"))); + p.setValue(QLatin1String("cpp.toolchainInstallPath"), msvc->binPath); + setQtHelperProperties(p, msvc); + profiles << p; +} + +struct MSVCArchInfo +{ + QString arch; + QString binPath; +}; + +static QVector findSupportedArchitectures(const MSVC &msvc) +{ + QVector result; + if (msvc.internalVsVersion.majorVersion() < 15) { + static const QStringList knownArchitectures = QStringList() + << QStringLiteral("x86") + << QStringLiteral("amd64_x86") + << QStringLiteral("amd64") + << QStringLiteral("x86_amd64") + << QStringLiteral("ia64") + << QStringLiteral("x86_ia64") + << QStringLiteral("x86_arm") + << QStringLiteral("amd64_arm"); + for (const QString &knownArchitecture : knownArchitectures) { + MSVCArchInfo ai; + ai.arch = knownArchitecture; + ai.binPath = msvc.binPathForArchitecture(knownArchitecture); + if (QFile::exists(ai.binPath + QLatin1String("/cl.exe"))) + result += ai; + } + } else { + QDir vcInstallDir(msvc.vcInstallPath); + foreach (QString hostArch, vcInstallDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + QDir subdir = vcInstallDir; + if (!subdir.cd(hostArch)) + continue; + const QString shortHostArch = hostArch.mid(4).toLower(); + foreach (const QString &arch, subdir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + MSVCArchInfo ai; + ai.binPath = subdir.absoluteFilePath(arch); + if (shortHostArch == arch) + ai.arch = arch; + else + ai.arch = shortHostArch + QLatin1Char('_') + arch; + result += ai; + } + } + } + return result; +} + +static QString wow6432Key() +{ +#ifdef Q_OS_WIN64 + return QLatin1String("\\Wow6432Node"); +#else + return QString(); +#endif +} + +static QVector installedMSVCs() +{ + QVector msvcs; + const QSettings vsRegistry( + QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key() + + QLatin1String("\\Microsoft\\VisualStudio\\SxS\\VS7"), + QSettings::NativeFormat); + foreach (const QString &vsName, vsRegistry.childKeys()) { + MSVC msvc; + msvc.internalVsVersion = Version::fromString(vsName); + if (!msvc.internalVsVersion.isValid()) + continue; + + QDir vsInstallDir(vsRegistry.value(vsName).toString()); + msvc.vsInstallPath = vsInstallDir.absolutePath(); + if (!vsInstallDir.cd(QStringLiteral("VC"))) + continue; + + msvc.version = QString::number(Internal::VisualStudioVersionInfo( + Internal::Version::fromString(vsName)).marketingVersion()); + if (msvc.version.isEmpty()) { + qbsWarning() << Tr::tr("Unknown MSVC version %1 found.").arg(vsName); + continue; + } + + if (msvc.internalVsVersion.majorVersion() < 15) { + QDir vcInstallDir = vsInstallDir; + if (!vcInstallDir.cd(QStringLiteral("bin"))) + continue; + msvc.vcInstallPath = vcInstallDir.absolutePath(); + msvcs.append(msvc); + } else { + QDir vcInstallDir = vsInstallDir; + vcInstallDir.cd(QStringLiteral("Tools/MSVC")); + foreach (const QString &vcVersionStr, + vcInstallDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + const Version vcVersion = Version::fromString(vcVersionStr); + if (!vcVersion.isValid()) + continue; + QDir specificVcInstallDir = vcInstallDir; + if (!specificVcInstallDir.cd(vcVersionStr) + || !specificVcInstallDir.cd(QStringLiteral("bin"))) { + continue; + } + msvc.vcInstallPath = specificVcInstallDir.absolutePath(); + msvcs.append(msvc); + } + } + } + return msvcs; +} + +void msvcProbe(Settings *settings, QList &profiles) +{ + qbsInfo() << Tr::tr("Detecting MSVC toolchains..."); + + // 1) Installed SDKs preferred over standalone Visual studio + QVector winSDKs; + WinSDK defaultWinSDK; + + const QSettings sdkRegistry(QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key() + + QLatin1String("\\Microsoft\\Microsoft SDKs\\Windows"), + QSettings::NativeFormat); + const QString defaultSdkPath = sdkRegistry.value(QLatin1String("CurrentInstallFolder")).toString(); + if (!defaultSdkPath.isEmpty()) { + foreach (const QString &sdkKey, sdkRegistry.childGroups()) { + WinSDK sdk; + sdk.version = sdkKey; + sdk.vcInstallPath = sdkRegistry.value(sdkKey + QLatin1String("/InstallationFolder")).toString(); + sdk.isDefault = (sdk.vcInstallPath == defaultSdkPath); + if (sdk.vcInstallPath.isEmpty()) + continue; + if (sdk.vcInstallPath.endsWith(QLatin1Char('\\'))) + sdk.vcInstallPath.chop(1); + if (sdk.isDefault) + defaultWinSDK = sdk; + foreach (const MSVCArchInfo &ai, findSupportedArchitectures(sdk)) { + WinSDK specificSDK = sdk; + specificSDK.architecture = ai.arch; + specificSDK.binPath = ai.binPath; + winSDKs += specificSDK; + } + } + } + + foreach (const WinSDK &sdk, winSDKs) { + qbsInfo() << Tr::tr(" Windows SDK %1 detected:\n" + " installed in %2").arg(sdk.version, sdk.vcInstallPath); + if (sdk.isDefault) + qbsInfo() << Tr::tr(" This is the default SDK on this machine."); + } + + // 2) Installed MSVCs + QVector msvcs; + foreach (MSVC msvc, installedMSVCs()) { + if (msvc.internalVsVersion.majorVersion() < 15) { + // Check existence of various install scripts + const QString vcvars32bat = msvc.vcInstallPath + QLatin1String("/vcvars32.bat"); + if (!QFileInfo(vcvars32bat).isFile()) + continue; + } + + foreach (const MSVCArchInfo &ai, findSupportedArchitectures(msvc)) { + MSVC specificMSVC = msvc; + specificMSVC.architecture = ai.arch; + specificMSVC.binPath = ai.binPath; + msvcs += specificMSVC; + } + } + + foreach (const MSVC &msvc, msvcs) { + qbsInfo() << Tr::tr(" MSVC %1 (%2) detected in\n" + " %3").arg(msvc.version, msvc.architecture, + QDir::toNativeSeparators(msvc.binPath)); + } + + if (winSDKs.isEmpty() && msvcs.isEmpty()) { + qbsInfo() << Tr::tr("Could not detect an installation of " + "the Windows SDK or Visual Studio."); + return; + } + + qbsInfo() << Tr::tr("Detecting build environment..."); + QVector msvcPtrs; + msvcPtrs.resize(winSDKs.size() + msvcs.size()); + std::transform(winSDKs.begin(), winSDKs.end(), msvcPtrs.begin(), + [] (WinSDK &sdk) -> MSVC * { return &sdk; }); + std::transform(msvcs.begin(), msvcs.end(), msvcPtrs.begin() + winSDKs.size(), + [] (MSVC &msvc) -> MSVC * { return &msvc; }); + + VsEnvironmentDetector envDetector; + envDetector.start(msvcPtrs); + + for (int i = 0; i < winSDKs.count(); ++i) { + WinSDK &sdk = winSDKs[i]; + const QString name = QLatin1String("WinSDK") + sdk.version + QLatin1Char('-') + + sdk.architecture; + try { + sdk.init(); + addMSVCPlatform(settings, profiles, name, &sdk); + } catch (const ErrorInfo &error) { + qbsWarning() << Tr::tr("Failed to set up %1: %2").arg(name, error.toString()); + } + } + + for (int i = 0; i < msvcs.count(); ++i) { + MSVC &msvc = msvcs[i]; + const QString name = QLatin1String("MSVC") + msvc.version + QLatin1Char('-') + + msvc.architecture; + try { + msvc.init(); + addMSVCPlatform(settings, profiles, name, &msvc); + } catch (const ErrorInfo &error) { + qbsWarning() << Tr::tr("Failed to set up %1: %2").arg(name, error.toString()); + } + } +} + +void createMsvcProfile(const QString &profileName, const QString &compilerFilePath, + Settings *settings) +{ + MSVC msvc(compilerFilePath); + QList dummy; + addMSVCPlatform(settings, dummy, profileName, &msvc); + qbsInfo() << Tr::tr("Profile '%1' created for '%2'.") + .arg(profileName, QDir::toNativeSeparators(compilerFilePath)); +} diff --git a/src/app/qbs-setup-toolchains/msvcprobe.h b/src/app/qbs-setup-toolchains/msvcprobe.h new file mode 100644 index 00000000..38889b2e --- /dev/null +++ b/src/app/qbs-setup-toolchains/msvcprobe.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MSVCPROBE_H +#define MSVCPROBE_H + +#include + +namespace qbs { +class Profile; +class Settings; +} + +void createMsvcProfile(const QString &profileName, const QString &compilerFilePath, + qbs::Settings *settings); + +void msvcProbe(qbs::Settings *settings, QList &profiles); + +#endif // MSVCPROBE_H diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp new file mode 100644 index 00000000..de4e9276 --- /dev/null +++ b/src/app/qbs-setup-toolchains/probe.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "probe.h" + +#include "msvcprobe.h" +#include "xcodeprobe.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace qbs; +using Internal::HostOsInfo; +using Internal::Tr; + +static QTextStream qStdout(stdout); +static QTextStream qStderr(stderr); + +static QString findExecutable(const QString &fileName) +{ + QString fullFileName = fileName; + if (HostOsInfo::isWindowsHost() + && !fileName.endsWith(QLatin1String(".exe"), Qt::CaseInsensitive)) { + fullFileName += QLatin1String(".exe"); + } + const QString path = QString::fromLocal8Bit(qgetenv("PATH")); + foreach (const QString &ppath, path.split(HostOsInfo::pathListSeparator())) { + const QString fullPath = ppath + QLatin1Char('/') + fullFileName; + if (QFileInfo::exists(fullPath)) + return QDir::cleanPath(fullPath); + } + return QString(); +} + +static QString qsystem(const QString &exe, const QStringList &args = QStringList()) +{ + QProcess p; + p.setProcessChannelMode(QProcess::MergedChannels); + p.start(exe, args); + if (!p.waitForStarted()) { + throw qbs::ErrorInfo(Tr::tr("Failed to start compiler '%1': %2") + .arg(exe, p.errorString())); + } + if (!p.waitForFinished() || p.exitCode() != 0) + throw qbs::ErrorInfo(Tr::tr("Failed to run compiler '%1': %2").arg(exe, p.errorString())); + return QString::fromLocal8Bit(p.readAll()); +} + +static QStringList validMinGWMachines() +{ + // List of MinGW machine names (gcc -dumpmachine) recognized by Qbs + return QStringList() + << QLatin1String("mingw32") << QLatin1String("mingw64") + << QLatin1String("i686-w64-mingw32") << QLatin1String("x86_64-w64-mingw32") + << QLatin1String("i686-w64-mingw32.shared") << QLatin1String("x86_64-w64-mingw32.shared") + << QLatin1String("i686-w64-mingw32.static") << QLatin1String("x86_64-w64-mingw32.static") + << QLatin1String("i586-mingw32msvc") << QLatin1String("amd64-mingw32msvc"); +} + +static QStringList toolchainTypeFromCompilerName(const QString &compilerName) +{ + if (compilerName == QLatin1String("cl.exe")) + return canonicalToolchain(QLatin1String("msvc")); + foreach (const QString &type, (QStringList() << QLatin1String("clang") << QLatin1String("llvm") + << QLatin1String("mingw") << QLatin1String("gcc"))) + if (compilerName.contains(type)) + return canonicalToolchain(type); + if (compilerName == QLatin1String("g++")) + return canonicalToolchain(QLatin1String("gcc")); + return QStringList(); +} + +static QString gccMachineName(const QString &compilerFilePath) +{ + return qsystem(compilerFilePath, QStringList() << QLatin1String("-dumpmachine")).trimmed(); +} + +static QStringList standardCompilerFileNames() +{ + return QStringList() + << HostOsInfo::appendExecutableSuffix(QStringLiteral("gcc")) + << HostOsInfo::appendExecutableSuffix(QStringLiteral("g++")) + << HostOsInfo::appendExecutableSuffix(QStringLiteral("clang")) + << HostOsInfo::appendExecutableSuffix(QStringLiteral("clang++")); +} + +static void setCommonProperties(Profile &profile, const QString &compilerFilePath, + const QStringList &toolchainTypes) +{ + const QFileInfo cfi(compilerFilePath); + const QString compilerName = cfi.fileName(); + + if (toolchainTypes.contains(QStringLiteral("mingw"))) + profile.setValue(QStringLiteral("qbs.targetOS"), QStringList(QStringLiteral("windows"))); + + const QString prefix = compilerName.left(compilerName.lastIndexOf(QLatin1Char('-')) + 1); + if (!prefix.isEmpty()) + profile.setValue(QLatin1String("cpp.toolchainPrefix"), prefix); + + profile.setValue(QLatin1String("cpp.toolchainInstallPath"), cfi.absolutePath()); + profile.setValue(QLatin1String("qbs.toolchain"), toolchainTypes); + + const QString suffix = compilerName.right(compilerName.size() - prefix.size()); + if (!standardCompilerFileNames().contains(suffix)) + qWarning("%s", qPrintable( + QString::fromLatin1("'%1' is not a standard compiler file name; " + "you must set the cpp.cCompilerName and " + "cpp.cxxCompilerName properties of this profile " + "manually").arg(compilerName))); +} + +class ToolPathSetup +{ +public: + ToolPathSetup(Profile *profile, const QString &path, const QString &toolchainPrefix) + : m_profile(profile), m_compilerDirPath(path), m_toolchainPrefix(toolchainPrefix) + { + } + + void apply(const QString &toolName, const QString &propertyName) const + { + QString toolFileName = m_toolchainPrefix + HostOsInfo::appendExecutableSuffix(toolName); + if (QFile::exists(m_compilerDirPath + QLatin1Char('/') + toolFileName)) + return; + const QString toolFilePath = findExecutable(toolFileName); + if (toolFilePath.isEmpty()) { + qWarning("%s", qPrintable( + QString(QLatin1String("'%1' exists neither in '%2' nor in PATH.")) + .arg(toolFileName, m_compilerDirPath))); + } + m_profile->setValue(propertyName, toolFilePath); + } + +private: + Profile * const m_profile; + QString m_compilerDirPath; + QString m_toolchainPrefix; +}; + +static Profile createGccProfile(const QString &compilerFilePath, Settings *settings, + const QStringList &toolchainTypes, + const QString &profileName = QString()) +{ + const QString machineName = gccMachineName(compilerFilePath); + + if (toolchainTypes.contains(QLatin1String("mingw"))) { + if (!validMinGWMachines().contains(machineName)) { + throw ErrorInfo(Tr::tr("Detected gcc platform '%1' is not supported.") + .arg(machineName)); + } + } + + Profile profile(!profileName.isEmpty() ? profileName : machineName, settings); + profile.removeProfile(); + setCommonProperties(profile, compilerFilePath, toolchainTypes); + + // Check whether auxiliary tools reside within the toolchain's install path. + // This might not be the case when using icecc or another compiler wrapper. + const QString compilerDirPath = QFileInfo(compilerFilePath).absolutePath(); + const ToolPathSetup toolPathSetup(&profile, compilerDirPath, + profile.value(QStringLiteral("cpp.toolchainPrefix")) + .toString()); + toolPathSetup.apply(QLatin1String("ar"), QLatin1String("cpp.archiverPath")); + toolPathSetup.apply(QLatin1String("as"), QLatin1String("cpp.assemblerPath")); + toolPathSetup.apply(QLatin1String("nm"), QLatin1String("cpp.nmPath")); + if (profile.value(QStringLiteral("qbs.targetOS")) + .toStringList().contains(QStringLiteral("darwin"))) + toolPathSetup.apply(QLatin1String("dsymutil"), QLatin1String("cpp.dsymutilPath")); + else + toolPathSetup.apply(QLatin1String("objcopy"), QLatin1String("cpp.objcopyPath")); + toolPathSetup.apply(QLatin1String("strip"), QLatin1String("cpp.stripPath")); + + qStdout << Tr::tr("Profile '%1' created for '%2'.").arg(profile.name(), compilerFilePath) + << endl; + return profile; +} + +static void gccProbe(Settings *settings, QList &profiles, const QString &compilerName) +{ + qStdout << Tr::tr("Trying to detect %1...").arg(compilerName) << endl; + + const QString crossCompilePrefix = QString::fromLocal8Bit(qgetenv("CROSS_COMPILE")); + const QString compilerFilePath = findExecutable(crossCompilePrefix + compilerName); + QFileInfo cfi(compilerFilePath); + if (!cfi.exists()) { + qStderr << Tr::tr("%1 not found.").arg(compilerName) << endl; + return; + } + const QString profileName = cfi.completeBaseName(); + const QStringList toolchainTypes = toolchainTypeFromCompilerName(compilerName); + profiles << createGccProfile(compilerFilePath, settings, toolchainTypes, profileName); +} + +static void mingwProbe(Settings *settings, QList &profiles) +{ + // List of possible compiler binary names for this platform + QStringList compilerNames; + if (HostOsInfo::isWindowsHost()) { + compilerNames << QLatin1String("gcc"); + } else { + foreach (const QString &machineName, validMinGWMachines()) { + compilerNames << machineName + QLatin1String("-gcc"); + } + } + + foreach (const QString &compilerName, compilerNames) { + const QString gccPath + = findExecutable(HostOsInfo::appendExecutableSuffix(compilerName)); + if (!gccPath.isEmpty()) + profiles << createGccProfile(gccPath, settings, + canonicalToolchain(QLatin1String("mingw"))); + } +} + +void probe(Settings *settings) +{ + QList profiles; + if (HostOsInfo::isWindowsHost()) { + msvcProbe(settings, profiles); + } else { + gccProbe(settings, profiles, QLatin1String("gcc")); + gccProbe(settings, profiles, QLatin1String("clang")); + + if (HostOsInfo::isMacosHost()) { + xcodeProbe(settings, profiles); + } + } + + mingwProbe(settings, profiles); + + if (profiles.isEmpty()) { + qStderr << Tr::tr("Could not detect any toolchains. No profile created.") << endl; + } else if (profiles.count() == 1 && settings->defaultProfile().isEmpty()) { + const QString profileName = profiles.first().name(); + qStdout << Tr::tr("Making profile '%1' the default.").arg(profileName) << endl; + settings->setValue(QLatin1String("defaultProfile"), profileName); + } +} + +void createProfile(const QString &profileName, const QString &toolchainType, + const QString &compilerFilePath, Settings *settings) +{ + QFileInfo compiler(compilerFilePath); + if (compilerFilePath == compiler.fileName() && !compiler.exists()) + compiler = QFileInfo(findExecutable(compilerFilePath)); + + if (!compiler.exists()) { + throw qbs::ErrorInfo(Tr::tr("Compiler '%1' not found") + .arg(compilerFilePath)); + } + + QStringList toolchainTypes; + if (toolchainType.isEmpty()) + toolchainTypes = toolchainTypeFromCompilerName(compiler.fileName()); + else + toolchainTypes = canonicalToolchain(toolchainType); + + if (toolchainTypes.contains(QLatin1String("msvc"))) + createMsvcProfile(profileName, compiler.absoluteFilePath(), settings); + else if (toolchainTypes.contains(QLatin1String("gcc"))) + createGccProfile(compiler.absoluteFilePath(), settings, toolchainTypes, profileName); + else + throw qbs::ErrorInfo(Tr::tr("Cannot create profile: Unknown toolchain type.")); +} diff --git a/src/app/qbs-setup-toolchains/probe.h b/src/app/qbs-setup-toolchains/probe.h new file mode 100644 index 00000000..8f2ca358 --- /dev/null +++ b/src/app/qbs-setup-toolchains/probe.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROBE_H +#define QBS_PROBE_H + +#include + +QT_BEGIN_NAMESPACE +class QString; +class QStringList; +QT_END_NAMESPACE + +namespace qbs { class Settings; } + +void createProfile(const QString &profileName, const QString &toolchainType, + const QString &compilerFilePath, qbs::Settings *settings); + +void probe(qbs::Settings *settings); + +#endif // Header guard diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.exe.manifest b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.exe.manifest new file mode 100644 index 00000000..c2114cc8 --- /dev/null +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.exe.manifest @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro new file mode 100644 index 00000000..f395c545 --- /dev/null +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro @@ -0,0 +1,20 @@ +include(../app.pri) + +TARGET = qbs-setup-toolchains + +HEADERS += \ + commandlineparser.h \ + msvcprobe.h \ + probe.h \ + xcodeprobe.h + +SOURCES += \ + commandlineparser.cpp \ + main.cpp \ + msvcprobe.cpp \ + probe.cpp \ + xcodeprobe.cpp + +mingw { + RC_FILE = qbs-setup-toolchains.rc +} diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs new file mode 100644 index 00000000..3536b51d --- /dev/null +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs @@ -0,0 +1,17 @@ +import qbs 1.0 + +QbsApp { + name: "qbs-setup-toolchains" + cpp.dynamicLibraries: qbs.targetOS.contains("windows") ? base.concat("shell32") : base + files: [ + "commandlineparser.cpp", + "commandlineparser.h", + "main.cpp", + "msvcprobe.cpp", + "msvcprobe.h", + "probe.cpp", + "probe.h", + "xcodeprobe.cpp", + "xcodeprobe.h", + ] +} diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.rc b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.rc new file mode 100644 index 00000000..0f53403f --- /dev/null +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.rc @@ -0,0 +1,4 @@ +#define RT_MANIFEST 24 +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "qbs-setup-toolchains.exe.manifest" diff --git a/src/app/qbs-setup-toolchains/xcodeprobe.cpp b/src/app/qbs-setup-toolchains/xcodeprobe.cpp new file mode 100644 index 00000000..0f2ebd1b --- /dev/null +++ b/src/app/qbs-setup-toolchains/xcodeprobe.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "probe.h" +#include "xcodeprobe.h" + +#include "../shared/logging/consolelogger.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace qbs; +using Internal::Tr; + +namespace { +static const QString defaultDeveloperPath = + QStringLiteral("/Applications/Xcode.app/Contents/Developer"); +static const QRegularExpression defaultDeveloperPathRegex( + QStringLiteral("^/Applications/Xcode([a-zA-Z0-9 _-]+)\\.app/Contents/Developer$")); + +class XcodeProbe +{ +public: + XcodeProbe(qbs::Settings *settings, QList &profiles) + : settings(settings), profiles(profiles) + { } + + bool addDeveloperPath(const QString &path); + void detectDeveloperPaths(); + void setupDefaultToolchains(const QString &devPath, const QString &xCodeName); + void detectAll(); +private: + qbs::Settings *settings; + QList &profiles; + QStringList developerPaths; +}; + +bool XcodeProbe::addDeveloperPath(const QString &path) +{ + if (path.isEmpty()) + return false; + QFileInfo pInfo(path); + if (!pInfo.exists() || !pInfo.isDir()) + return false; + if (developerPaths.contains(path)) + return false; + developerPaths.append(path); + qbsInfo() << Tr::tr("Added developer path %1").arg(path); + return true; +} + +void XcodeProbe::detectDeveloperPaths() +{ + QProcess selectedXcode; + QString program = QLatin1String("/usr/bin/xcode-select"); + QStringList arguments(QLatin1String("--print-path")); + selectedXcode.start(program, arguments, QProcess::ReadOnly); + if (!selectedXcode.waitForFinished() || selectedXcode.exitCode()) { + qbsInfo() << Tr::tr("Could not detect selected Xcode with /usr/bin/xcode-select"); + } else { + QString path = QString::fromLocal8Bit(selectedXcode.readAllStandardOutput()); + addDeveloperPath(path); + } + addDeveloperPath(defaultDeveloperPath); + + QProcess launchServices; + program = QLatin1String("/usr/bin/mdfind"); + arguments = QStringList(QLatin1String("kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'")); + launchServices.start(program, arguments, QProcess::ReadOnly); + if (!launchServices.waitForFinished() || launchServices.exitCode()) { + qbsInfo() << Tr::tr("Could not detect additional Xcode installations with /usr/bin/mdfind"); + } else { + for (const QString &path : QString::fromLocal8Bit(launchServices.readAllStandardOutput()) + .split(QLatin1Char('\n'), QString::SkipEmptyParts)) + addDeveloperPath(path + QStringLiteral("/Contents/Developer")); + } +} + +static QStringList targetOSList(const QString &applePlatformName) +{ + QStringList targetOS; + if (applePlatformName == QStringLiteral("macosx")) { + targetOS << QStringLiteral("macos"); + } else if (applePlatformName == QStringLiteral("iphoneos")) { + targetOS << QStringLiteral("ios"); + } else if (applePlatformName == QStringLiteral("iphonesimulator")) { + targetOS << QStringLiteral("ios") << QStringLiteral("ios-simulator"); + } else if (applePlatformName == QStringLiteral("appletvos")) { + targetOS << QStringLiteral("tvos"); + } else if (applePlatformName == QStringLiteral("appletvsimulator")) { + targetOS << QStringLiteral("tvos") << QStringLiteral("tvos-simulator"); + } else if (applePlatformName == QStringLiteral("watchos")) { + targetOS << QStringLiteral("watchos"); + } else if (applePlatformName == QStringLiteral("watchsimulator")) { + targetOS << QStringLiteral("watchos") << QStringLiteral("watchos-simulator"); + } + + targetOS << QStringLiteral("darwin") << QStringLiteral("bsd") << QStringLiteral("unix"); + return targetOS; +} + +static QStringList archList(const QString &applePlatformName) +{ + QStringList archs; + if (applePlatformName == QStringLiteral("macosx") + || applePlatformName == QStringLiteral("iphonesimulator") + || applePlatformName == QStringLiteral("appletvsimulator") + || applePlatformName == QStringLiteral("watchsimulator")) { + if (applePlatformName != QStringLiteral("appletvsimulator")) + archs << QStringLiteral("x86"); + if (applePlatformName != QStringLiteral("watchsimulator")) + archs << QStringLiteral("x86_64"); + } else if (applePlatformName == QStringLiteral("iphoneos") + || applePlatformName == QStringLiteral("appletvos")) { + if (applePlatformName != QStringLiteral("appletvos")) + archs << QStringLiteral("armv7"); + archs << QStringLiteral("arm64"); + } else if (applePlatformName == QStringLiteral("watchos")) { + archs << QStringLiteral("armv7k"); + } + + return archs; +} + +void XcodeProbe::setupDefaultToolchains(const QString &devPath, const QString &xcodeName) +{ + qbsInfo() << Tr::tr("Profile '%1' created for '%2'.").arg(xcodeName).arg(devPath); + + Profile installationProfile(xcodeName, settings); + installationProfile.removeProfile(); + installationProfile.setValue(QStringLiteral("qbs.toolchain"), QStringList() + << QLatin1String("xcode") + << QLatin1String("clang") + << QLatin1String("llvm") + << QLatin1String("gcc")); + if (devPath != defaultDeveloperPath) + installationProfile.setValue(QStringLiteral("xcode.developerPath"), devPath); + profiles << installationProfile; + + QStringList platforms; + platforms << QStringLiteral("macosx") + << QStringLiteral("iphoneos") + << QStringLiteral("iphonesimulator") + << QStringLiteral("appletvos") + << QStringLiteral("appletvsimulator") + << QStringLiteral("watchos") + << QStringLiteral("watchsimulator"); + for (const QString &platform : platforms) { + Profile platformProfile(xcodeName + QLatin1Char('-') + platform, settings); + platformProfile.removeProfile(); + platformProfile.setBaseProfile(installationProfile.name()); + platformProfile.setValue(QStringLiteral("qbs.targetOS"), targetOSList(platform)); + profiles << platformProfile; + + for (const QString &arch : archList(platform)) { + Profile archProfile(xcodeName + QLatin1Char('-') + platform + QLatin1Char('-') + arch, + settings); + archProfile.removeProfile(); + archProfile.setBaseProfile(platformProfile.name()); + archProfile.setValue(QStringLiteral("qbs.architecture"), + qbs::canonicalArchitecture(arch)); + profiles << archProfile; + } + } +} + +void XcodeProbe::detectAll() +{ + int i = 1; + detectDeveloperPaths(); + for (const QString &developerPath : developerPaths) { + QRegularExpressionMatch match(defaultDeveloperPathRegex.match(developerPath)); + QString profileName = QLatin1String("xcode"); + if (developerPath != defaultDeveloperPath) { + profileName += match.hasMatch() + ? match.capturedTexts().value(1).toLower().replace(QLatin1Char(' '), + QLatin1Char('-')) + : QString::number(i++); + } + setupDefaultToolchains(developerPath, profileName); + } +} +} // end anonymous namespace + +void xcodeProbe(qbs::Settings *settings, QList &profiles) +{ + XcodeProbe probe(settings, profiles); + probe.detectAll(); +} diff --git a/src/app/qbs-setup-toolchains/xcodeprobe.h b/src/app/qbs-setup-toolchains/xcodeprobe.h new file mode 100644 index 00000000..84c35166 --- /dev/null +++ b/src/app/qbs-setup-toolchains/xcodeprobe.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XCODEPROBE_H +#define XCODEPROBE_H + +#include + +namespace qbs { +class Profile; +class Settings; +} + +void xcodeProbe(qbs::Settings *settings, QList &profiles); + +#endif // XCODEPROBE_H diff --git a/src/app/qbs/application.cpp b/src/app/qbs/application.cpp new file mode 100644 index 00000000..33fab439 --- /dev/null +++ b/src/app/qbs/application.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "application.h" + +#include "commandlinefrontend.h" +#include "ctrlchandler.h" + +namespace qbs { + +Application::Application(int &argc, char **argv) + : QCoreApplication(argc, argv), m_clFrontend(0), m_canceled(false) +{ + setApplicationName(QLatin1String("qbs")); + setOrganizationName(QLatin1String("QtProject")); + setOrganizationDomain(QLatin1String("qt-project.org")); +} + +Application *Application::instance() +{ + return qobject_cast(QCoreApplication::instance()); +} + +void Application::setCommandLineFrontend(CommandLineFrontend *clFrontend) +{ + installCtrlCHandler(); + m_clFrontend = clFrontend; +} + +/** + * Interrupt the application. This is directly called from a signal handler. + */ +void Application::userInterrupt() +{ + if (m_canceled) + return; + Q_ASSERT(m_clFrontend); + m_canceled = true; + m_clFrontend->cancel(); +} + +} // namespace qbs diff --git a/src/app/qbs/application.h b/src/app/qbs/application.h new file mode 100644 index 00000000..a38212df --- /dev/null +++ b/src/app/qbs/application.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef APPLICATION_H +#define APPLICATION_H + +#include + +namespace qbs { +class CommandLineFrontend; + +class Application : public QCoreApplication +{ + Q_OBJECT +public: + Application(int &argc, char **argv); + + static Application *instance(); + + void setCommandLineFrontend(CommandLineFrontend *clFrontend); + void userInterrupt(); + +private: + CommandLineFrontend *m_clFrontend; + bool m_canceled; +}; + +} // namespace qbs + +#endif // APPLICATION_H diff --git a/src/app/qbs/commandlinefrontend.cpp b/src/app/qbs/commandlinefrontend.cpp new file mode 100644 index 00000000..0b712df1 --- /dev/null +++ b/src/app/qbs/commandlinefrontend.cpp @@ -0,0 +1,641 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandlinefrontend.h" + +#include "application.h" +#include "consoleprogressobserver.h" +#include "status.h" +#include "parser/commandlineoption.h" +#include "../shared/logging/consolelogger.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace qbs { +using namespace Internal; + +CommandLineFrontend::CommandLineFrontend(const CommandLineParser &parser, Settings *settings, + QObject *parent) + : QObject(parent) + , m_parser(parser) + , m_settings(settings) + , m_observer(0) + , m_cancelStatus(CancelStatusNone) + , m_cancelTimer(new QTimer(this)) +{ +} + +CommandLineFrontend::~CommandLineFrontend() +{ + m_cancelTimer->stop(); +} + +// Called from interrupt handler. Don't do anything non-trivial here. +void CommandLineFrontend::cancel() +{ + m_cancelStatus = CancelStatusRequested; +} + +void CommandLineFrontend::checkCancelStatus() +{ + switch (m_cancelStatus) { + case CancelStatusNone: + break; + case CancelStatusRequested: + m_cancelStatus = CancelStatusCanceling; + m_cancelTimer->stop(); + if (m_resolveJobs.isEmpty() && m_buildJobs.isEmpty()) + std::exit(EXIT_FAILURE); + foreach (AbstractJob * const job, m_resolveJobs) + job->cancel(); + foreach (AbstractJob * const job, m_buildJobs) + job->cancel(); + break; + case CancelStatusCanceling: + QBS_ASSERT(false, return); + break; + } +} + +void CommandLineFrontend::start() +{ + try { + switch (m_parser.command()) { + case RunCommandType: + case ShellCommandType: + if (m_parser.products().count() > 1) { + throw ErrorInfo(Tr::tr("Invalid use of command '%1': Cannot use more than one " + "product.\nUsage: %2") + .arg(m_parser.commandName(), m_parser.commandDescription())); + } + // Fall-through intended. + case StatusCommandType: + case InstallCommandType: + case DumpNodesTreeCommandType: + if (m_parser.buildConfigurations().count() > 1) { + QString error = Tr::tr("Invalid use of command '%1': There can be only one " + "build configuration.\n").arg(m_parser.commandName()); + error += Tr::tr("Usage: %1").arg(m_parser.commandDescription()); + throw ErrorInfo(error); + } + break; + default: + break; + } + + if (m_parser.showVersion()) { + puts(QBS_VERSION); + qApp->exit(EXIT_SUCCESS); + return; + } + if (m_parser.showProgress()) + m_observer = new ConsoleProgressObserver; + SetupProjectParameters params; + params.setProjectFilePath(m_parser.projectFilePath()); + params.setIgnoreDifferentProjectFilePath(m_parser.force()); + params.setDryRun(m_parser.dryRun()); + params.setForceProbeExecution(m_parser.forceProbesExecution()); + params.setWaitLockBuildGraph(m_parser.waitLockBuildGraph()); + params.setLogElapsedTime(m_parser.logTime()); + params.setSettingsDirectory(m_settings->baseDirectory()); + if (!m_parser.buildBeforeInstalling() || m_parser.command() == DumpNodesTreeCommandType) + params.setRestoreBehavior(SetupProjectParameters::RestoreOnly); + foreach (const QVariantMap &buildConfig, m_parser.buildConfigurations()) { + QVariantMap userConfig = buildConfig; + const QString configurationKey = QLatin1String("qbs.configurationName"); + const QString profileKey = QLatin1String("qbs.profile"); + const QString installRootKey = QLatin1String("qbs.installRoot"); + QString installRoot = userConfig.value(installRootKey).toString(); + if (!installRoot.isEmpty() && QFileInfo(installRoot).isRelative()) { + installRoot.prepend(QLatin1Char('/')).prepend(QDir::currentPath()); + userConfig.insert(installRootKey, installRoot); + } + const QString configurationName = userConfig.take(configurationKey).toString(); + QString profileName = userConfig.take(profileKey).toString(); + if (profileName.isEmpty()) + profileName = m_settings->defaultProfile(); + if (profileName.isEmpty()) { + ErrorInfo error(Tr::tr("No profile specified and no default profile exists.")); + error.append(Tr::tr("To set a default profile, run " + "'qbs config defaultProfile '.")); + throw error; + } + const Preferences prefs(m_settings); + params.setSearchPaths(prefs.searchPaths(QDir::cleanPath(QCoreApplication::applicationDirPath() + + QLatin1String("/" QBS_RELATIVE_SEARCH_PATH)))); + params.setPluginPaths(prefs.pluginPaths(QDir::cleanPath(QCoreApplication::applicationDirPath() + + QLatin1String("/" QBS_RELATIVE_PLUGINS_PATH)))); + params.setLibexecPath(QDir::cleanPath(QCoreApplication::applicationDirPath() + + QLatin1String("/" QBS_RELATIVE_LIBEXEC_PATH))); + params.setTopLevelProfile(profileName); + params.setConfigurationName(configurationName); + params.setBuildRoot(buildDirectory(profileName)); + params.setOverriddenValues(userConfig); + SetupProjectJob * const job = Project().setupProject(params, + ConsoleLogger::instance().logSink(), this); + connectJob(job); + m_resolveJobs << job; + } + + /* + * Progress reporting on the terminal gets a bit tricky when resolving several projects + * concurrently, since we cannot show multiple progress bars at the same time. Instead, + * we just set the total effort to the number of projects and increase the progress + * every time one of them finishes, ingoring the progress reports from the jobs themselves. + * (Yes, that does mean it will take disproportionately long for the first progress + * notification to arrive.) + */ + if (m_parser.showProgress() && resolvingMultipleProjects()) + m_observer->initialize(tr("Setting up projects"), m_resolveJobs.count()); + + // Check every two seconds whether we received a cancel request. This value has been + // experimentally found to be acceptable. + // Note that this polling approach is not problematic here, since we are doing work anyway, + // so there's no danger of waking up the processor for no reason. + connect(m_cancelTimer, &QTimer::timeout, this, &CommandLineFrontend::checkCancelStatus); + m_cancelTimer->start(2000); + } catch (const ErrorInfo &error) { + qbsError() << error.toString(); + if (m_buildJobs.isEmpty() && m_resolveJobs.isEmpty()) { + qApp->exit(EXIT_FAILURE); + } else { + cancel(); + checkCancelStatus(); + } + } +} + +void CommandLineFrontend::handleCommandDescriptionReport(const QString &highlight, + const QString &message) +{ + qbsInfo() << MessageTag(highlight) << message; +} + +void CommandLineFrontend::handleJobFinished(bool success, AbstractJob *job) +{ + try { + job->deleteLater(); + if (!success) { + qbsError() << job->error().toString(); + m_resolveJobs.removeOne(job); + m_buildJobs.removeOne(job); + if (m_resolveJobs.isEmpty() && m_buildJobs.isEmpty()) { + qApp->exit(EXIT_FAILURE); + return; + } + cancel(); + } else if (SetupProjectJob * const setupJob = qobject_cast(job)) { + m_resolveJobs.removeOne(job); + m_projects << setupJob->project(); + if (m_observer && resolvingMultipleProjects()) + m_observer->incrementProgressValue(); + if (m_resolveJobs.isEmpty()) + handleProjectsResolved(); + } else if (qobject_cast(job)) { + if (m_parser.command() == RunCommandType) + qApp->exit(runTarget()); + else + qApp->quit(); + } else { // Build or clean. + m_buildJobs.removeOne(job); + if (m_buildJobs.isEmpty()) { + switch (m_parser.command()) { + case RunCommandType: + case InstallCommandType: + install(); + break; + case GenerateCommandType: + generate(); + // fall through + case BuildCommandType: + case CleanCommandType: + qApp->quit(); + break; + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Missing case in switch statement"); + } + } + } + } catch (const ErrorInfo &error) { + qbsError() << error.toString(); + qApp->exit(EXIT_FAILURE); + } +} + +void CommandLineFrontend::handleNewTaskStarted(const QString &description, int totalEffort) +{ + // If the user does not want a progress bar, we just print the current activity. + if (!m_parser.showProgress()) { + if (!m_parser.logTime()) + qbsInfo() << description; + return; + } + if (isBuilding()) { + m_totalBuildEffort += totalEffort; + if (++m_buildEffortsRetrieved == m_buildEffortsNeeded) { + m_observer->initialize(tr("Building"), m_totalBuildEffort); + if (m_currentBuildEffort > 0) + m_observer->setProgressValue(m_currentBuildEffort); + } + } else if (!resolvingMultipleProjects()) { + m_observer->initialize(description, totalEffort); + } +} + +void CommandLineFrontend::handleTotalEffortChanged(int totalEffort) +{ + // Can only happen when resolving. + if (m_parser.showProgress() && !isBuilding() && !resolvingMultipleProjects()) + m_observer->setMaximum(totalEffort); +} + +void CommandLineFrontend::handleTaskProgress(int value, AbstractJob *job) +{ + if (isBuilding()) { + int ¤tJobEffort = m_buildEfforts[job]; + m_currentBuildEffort += value - currentJobEffort; + currentJobEffort = value; + if (m_buildEffortsRetrieved == m_buildEffortsNeeded) + m_observer->setProgressValue(m_currentBuildEffort); + } else if (!resolvingMultipleProjects()) { + m_observer->setProgressValue(value); + } +} + +void CommandLineFrontend::handleProcessResultReport(const qbs::ProcessResult &result) +{ + bool hasOutput = !result.stdOut().isEmpty() || !result.stdErr().isEmpty(); + if (!hasOutput && result.success()) + return; + + (result.success() ? qbsInfo() : qbsError()) + << shellQuote(QDir::toNativeSeparators(result.executableFilePath()), result.arguments()) + << (hasOutput ? QString::fromLatin1("\n") : QString()) + << (result.stdOut().isEmpty() ? QString() : result.stdOut().join(QLatin1Char('\n'))) + << (result.stdErr().isEmpty() ? QString() : result.stdErr().join(QLatin1Char('\n'))); +} + +bool CommandLineFrontend::resolvingMultipleProjects() const +{ + return isResolving() && m_resolveJobs.count() + m_projects.count() > 1; +} + +bool CommandLineFrontend::isResolving() const +{ + return !m_resolveJobs.isEmpty(); +} + +bool CommandLineFrontend::isBuilding() const +{ + return !m_buildJobs.isEmpty(); +} + +CommandLineFrontend::ProductMap CommandLineFrontend::productsToUse() const +{ + ProductMap products; + QStringList productNames; + const bool useAll = m_parser.products().isEmpty(); + foreach (const Project &project, m_projects) { + QList &productList = products[project]; + const ProjectData projectData = project.projectData(); + foreach (const ProductData &product, projectData.allProducts()) { + if (useAll || m_parser.products().contains(product.name())) { + productList << product; + productNames << product.name(); + } + } + } + + foreach (const QString &productName, m_parser.products()) { + if (!productNames.contains(productName)) + throw ErrorInfo(Tr::tr("No such product '%1'.").arg(productName)); + } + + return products; +} + +void CommandLineFrontend::handleProjectsResolved() +{ + if (m_cancelStatus != CancelStatusNone) + throw ErrorInfo(Tr::tr("Execution canceled.")); + switch (m_parser.command()) { + case ResolveCommandType: + qApp->quit(); + break; + case CleanCommandType: + makeClean(); + break; + case ShellCommandType: + qApp->exit(runShell()); + break; + case StatusCommandType: + qApp->exit(printStatus(m_projects.first().projectData())); + break; + case BuildCommandType: + case GenerateCommandType: + build(); + break; + case InstallCommandType: + case RunCommandType: + if (m_parser.buildBeforeInstalling()) + build(); + else + install(); + break; + case UpdateTimestampsCommandType: + updateTimestamps(); + qApp->quit(); + break; + case DumpNodesTreeCommandType: + dumpNodesTree(); + qApp->quit(); + break; + case HelpCommandType: + Q_ASSERT_X(false, Q_FUNC_INFO, "Impossible."); + } +} + +void CommandLineFrontend::makeClean() +{ + if (m_parser.products().isEmpty()) { + foreach (const Project &project, m_projects) { + m_buildJobs << project.cleanAllProducts(m_parser.cleanOptions(project.profile()), this); + } + } else { + const ProductMap &products = productsToUse(); + for (ProductMap::ConstIterator it = products.begin(); it != products.end(); ++it) { + m_buildJobs << it.key().cleanSomeProducts(it.value(), + m_parser.cleanOptions(it.key().profile()), + this); + } + } + connectBuildJobs(); +} + +int CommandLineFrontend::runShell() +{ + const ProductData productToRun = getTheOneRunnableProduct(); + RunEnvironment runEnvironment = m_projects.first().getRunEnvironment(productToRun, + m_parser.installOptions(m_projects.first().profile()), + QProcessEnvironment::systemEnvironment(), m_settings); + return runEnvironment.doRunShell(); +} + +BuildOptions CommandLineFrontend::buildOptions(const Project &project) const +{ + BuildOptions options = m_parser.buildOptions(m_projects.first().profile()); + if (options.maxJobCount() <= 0) { + const QString profileName = project.profile(); + QBS_CHECK(!profileName.isEmpty()); + options.setMaxJobCount(Preferences(m_settings, profileName).jobs()); + } + return options; +} + +QString CommandLineFrontend::buildDirectory(const QString &profileName) const +{ + QString buildDir = m_parser.projectBuildDirectory(); + if (buildDir.isEmpty()) { + buildDir = Preferences(m_settings, profileName).defaultBuildDirectory(); + if (buildDir.isEmpty()) { + qbsDebug() << "No project build directory given; using current directory."; + buildDir = QDir::currentPath(); + } else { + qbsDebug() << "No project build directory given; using directory from preferences."; + } + } + + QString projectName(QFileInfo(m_parser.projectFilePath()).baseName()); + buildDir.replace(BuildDirectoryOption::magicProjectString(), projectName); + QString projectDir(QFileInfo(m_parser.projectFilePath()).path()); + buildDir.replace(BuildDirectoryOption::magicProjectDirString(), projectDir); + if (!QFileInfo(buildDir).isAbsolute()) + buildDir = QDir::currentPath() + QLatin1Char('/') + buildDir; + buildDir = QDir::cleanPath(buildDir); + return buildDir; +} + +void CommandLineFrontend::build() +{ + if (m_parser.products().isEmpty()) { + const Project::ProductSelection productSelection = m_parser.withNonDefaultProducts() + ? Project::ProductSelectionWithNonDefault : Project::ProductSelectionDefaultOnly; + foreach (const Project &project, m_projects) + m_buildJobs << project.buildAllProducts(buildOptions(project), productSelection, this); + } else { + const ProductMap &products = productsToUse(); + for (ProductMap::ConstIterator it = products.begin(); it != products.end(); ++it) + m_buildJobs << it.key().buildSomeProducts(it.value(), buildOptions(it.key()), this); + } + connectBuildJobs(); + + /* + * Progress reporting for the build jobs works as follows: We know that for every job, + * the newTaskStarted() signal is emitted exactly once (unless there's an error). So we add up + * the respective total efforts as they come in. Once all jobs have reported their total + * efforts, we can start the overall progress report. + */ + m_buildEffortsNeeded = m_buildJobs.count(); + m_buildEffortsRetrieved = 0; + m_totalBuildEffort = 0; + m_currentBuildEffort = 0; +} + +void CommandLineFrontend::generate() +{ + const QString generatorName = m_parser.generateOptions().generatorName(); + QSharedPointer generator(ProjectGeneratorManager::findGenerator(generatorName)); + if (!generator) { + const QString generatorNames = ProjectGeneratorManager::loadedGeneratorNames() + .join(QLatin1String("\n\t")); + if (generatorName.isEmpty()) { + throw ErrorInfo(Tr::tr("No generator specified. Available generators:\n\t%1") + .arg(generatorNames)); + } + + throw ErrorInfo(Tr::tr("No generator named '%1'. Available generators:\n\t%2") + .arg(generatorName) + .arg(generatorNames)); + } + + generator->generate(m_projects, + m_parser.buildConfigurations(), + m_parser.installOptions(QString())); +} + +int CommandLineFrontend::runTarget() +{ + const ProductData productToRun = getTheOneRunnableProduct(); + const QString executableFilePath = productToRun.targetExecutable(); + if (executableFilePath.isEmpty()) { + throw ErrorInfo(Tr::tr("Cannot run: Product '%1' is not an application.") + .arg(productToRun.name())); + } + RunEnvironment runEnvironment = m_projects.first().getRunEnvironment(productToRun, + m_parser.installOptions(m_projects.first().profile()), + QProcessEnvironment::systemEnvironment(), m_settings); + return runEnvironment.doRunTarget(executableFilePath, m_parser.runArgs()); +} + +void CommandLineFrontend::updateTimestamps() +{ + const ProductMap &products = productsToUse(); + for (ProductMap::ConstIterator it = products.constBegin(); it != products.constEnd(); ++it) { + Project p = it.key(); + p.updateTimestamps(it.value()); + } +} + +void CommandLineFrontend::dumpNodesTree() +{ + QFile stdOut; + stdOut.open(stdout, QIODevice::WriteOnly); + const ErrorInfo error = m_projects.first().dumpNodesTree(stdOut, productsToUse() + .value(m_projects.first())); + if (error.hasError()) + throw error; +} + +void CommandLineFrontend::connectBuildJobs() +{ + foreach (AbstractJob * const job, m_buildJobs) + connectBuildJob(job); +} + +void CommandLineFrontend::connectBuildJob(AbstractJob *job) +{ + connectJob(job); + + BuildJob *bjob = qobject_cast(job); + if (!bjob) + return; + + connect(bjob, &BuildJob::reportCommandDescription, + this, &CommandLineFrontend::handleCommandDescriptionReport); + connect(bjob, &BuildJob::reportProcessResult, + this, &CommandLineFrontend::handleProcessResultReport); +} + +void CommandLineFrontend::connectJob(AbstractJob *job) +{ + connect(job, &AbstractJob::finished, + this, &CommandLineFrontend::handleJobFinished); + connect(job, &AbstractJob::taskStarted, + this, &CommandLineFrontend::handleNewTaskStarted); + connect(job, &AbstractJob::totalEffortChanged, + this, &CommandLineFrontend::handleTotalEffortChanged); + if (m_parser.showProgress()) { + connect(job, &AbstractJob::taskProgress, + this, &CommandLineFrontend::handleTaskProgress); + } +} + +ProductData CommandLineFrontend::getTheOneRunnableProduct() +{ + QBS_CHECK(m_projects.count() == 1); // Has been checked earlier. + + if (m_parser.products().count() == 1) { + foreach (const ProductData &p, m_projects.first().projectData().allProducts()) { + if (p.name() == m_parser.products().first()) + return p; + } + QBS_CHECK(false); + } + QBS_CHECK(m_parser.products().count() == 0); + + QList runnableProducts; + foreach (const ProductData &p, m_projects.first().projectData().allProducts()) { + if (p.isRunnable()) + runnableProducts << p; + } + + if (runnableProducts.count() == 1) + return runnableProducts.first(); + + if (runnableProducts.isEmpty()) { + throw ErrorInfo(Tr::tr("Cannot execute command '%1': Project has no runnable product.") + .arg(m_parser.commandName())); + } + + ErrorInfo error(Tr::tr("Ambiguous use of command '%1': No product given, but project " + "has more than one runnable product.").arg(m_parser.commandName())); + error.append(Tr::tr("Use the '--products' option with one of the following products:")); + foreach (const ProductData &p, runnableProducts) { + QString productRepr = QLatin1String("\t") + p.name(); + if (p.profile() != m_projects.first().profile()) { + productRepr.append(QLatin1String(" [")).append(p.profile()) + .append(QLatin1Char(']')); + } + error.append(productRepr); + } + throw error; +} + +void CommandLineFrontend::install() +{ + Q_ASSERT(m_projects.count() == 1); + const Project project = m_projects.first(); + InstallJob *installJob; + if (m_parser.products().isEmpty()) { + const Project::ProductSelection productSelection = m_parser.withNonDefaultProducts() + ? Project::ProductSelectionWithNonDefault : Project::ProductSelectionDefaultOnly; + installJob = project.installAllProducts(m_parser.installOptions(project.profile()), + productSelection); + } else { + const Project project = m_projects.first(); + const ProductMap products = productsToUse(); + installJob = project.installSomeProducts(products.value(project), + m_parser.installOptions(project.profile())); + } + connectJob(installJob); +} + +} // namespace qbs diff --git a/src/app/qbs/commandlinefrontend.h b/src/app/qbs/commandlinefrontend.h new file mode 100644 index 00000000..aa4e288f --- /dev/null +++ b/src/app/qbs/commandlinefrontend.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef COMMANDLINEFRONTEND_H +#define COMMANDLINEFRONTEND_H + +#include "parser/commandlineparser.h" +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + +namespace qbs { +class AbstractJob; +class ConsoleProgressObserver; +class ErrorInfo; +class ProcessResult; +class Settings; + +class CommandLineFrontend : public QObject +{ + Q_OBJECT +public: + explicit CommandLineFrontend(const CommandLineParser &parser, Settings *settings, + QObject *parent = 0); + ~CommandLineFrontend(); + + void cancel(); + void start(); + +private: + void handleCommandDescriptionReport(const QString &highlight, const QString &message); + void handleJobFinished(bool success, qbs::AbstractJob *job); + void handleNewTaskStarted(const QString &description, int totalEffort); + void handleTotalEffortChanged(int totalEffort); + void handleTaskProgress(int value, qbs::AbstractJob *job); + void handleProcessResultReport(const qbs::ProcessResult &result); + void checkCancelStatus(); + + typedef QHash > ProductMap; + ProductMap productsToUse() const; + + bool resolvingMultipleProjects() const; + bool isResolving() const; + bool isBuilding() const; + void handleProjectsResolved(); + void makeClean(); + int runShell(); + void build(); + void generate(); + int runTarget(); + void updateTimestamps(); + void dumpNodesTree(); + void connectBuildJobs(); + void connectBuildJob(AbstractJob *job); + void connectJob(AbstractJob *job); + ProductData getTheOneRunnableProduct(); + void install(); + BuildOptions buildOptions(const Project &project) const; + QString buildDirectory(const QString &profileName) const; + + const CommandLineParser &m_parser; + Settings * const m_settings; + QList m_resolveJobs; + QList m_buildJobs; + QList m_projects; + + ConsoleProgressObserver *m_observer; + + enum CancelStatus { CancelStatusNone, CancelStatusRequested, CancelStatusCanceling }; + CancelStatus m_cancelStatus; + QTimer * const m_cancelTimer; + + int m_buildEffortsNeeded; + int m_buildEffortsRetrieved; + int m_totalBuildEffort; + int m_currentBuildEffort; + QHash m_buildEfforts; +}; + +} // namespace qbs + +#endif // COMMANDLINEFRONTEND_H diff --git a/src/app/qbs/consoleprogressobserver.cpp b/src/app/qbs/consoleprogressobserver.cpp new file mode 100644 index 00000000..191e3184 --- /dev/null +++ b/src/app/qbs/consoleprogressobserver.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "consoleprogressobserver.h" + +#include +#include + +#include + +namespace qbs { + +void ConsoleProgressObserver::initialize(const QString &task, int max) +{ + m_maximum = max; + m_value = 0; + m_percentage = 0; + m_hashesPrinted = 0; + std::cout << task.toLocal8Bit().constData() << ": 0%" << std::flush; + setMaximum(max); +} + +void ConsoleProgressObserver::setMaximum(int maximum) +{ + m_maximum = maximum; + if (maximum == 0) { + m_percentage = 100; + updateProgressBarIfNecessary(); + writePercentageString(); + std::cout << std::endl; + } +} + +void ConsoleProgressObserver::setProgressValue(int value) +{ + if (value > m_maximum || value <= m_value) + return; // TODO: Should be an assertion, but the executor currently breaks it. + m_value = value; + const int newPercentage = (100 * m_value) / m_maximum; + if (newPercentage == m_percentage) + return; + eraseCurrentPercentageString(); + m_percentage = newPercentage; + updateProgressBarIfNecessary(); + writePercentageString(); + if (m_value == m_maximum) + std::cout << std::endl; + else + std::cout << std::flush; +} + +void ConsoleProgressObserver::eraseCurrentPercentageString() +{ + const int charsToErase = m_percentage == 0 ? 2 : m_percentage < 10 ? 3 : 4; + + // (1) Move cursor before the old percentage string. + // (2) Erase current line content to the right of the cursor. + std::cout << QString::fromLatin1("\x1b[%1D").arg(charsToErase).toLocal8Bit().constData(); + std::cout << "\x1b[K"; +} + +void ConsoleProgressObserver::updateProgressBarIfNecessary() +{ + static const int TotalHashCount = 50; // Should fit on most terminals without a line break. + const int hashesNeeded = (m_percentage * TotalHashCount) / 100; + if (m_hashesPrinted < hashesNeeded) { + std::cout << QByteArray(hashesNeeded - m_hashesPrinted, '#').constData(); + m_hashesPrinted = hashesNeeded; + } +} + +void ConsoleProgressObserver::writePercentageString() +{ + std::cout << QString::fromLatin1(" %1%").arg(m_percentage).toLocal8Bit().constData(); +} + +} // namespace qbs diff --git a/src/app/qbs/consoleprogressobserver.h b/src/app/qbs/consoleprogressobserver.h new file mode 100644 index 00000000..2f6cbb97 --- /dev/null +++ b/src/app/qbs/consoleprogressobserver.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CONSOLEPROGRESSOBSERVER_H +#define CONSOLEPROGRESSOBSERVER_H + +#include + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +namespace qbs { + +class ConsoleProgressObserver +{ +public: + void initialize(const QString &task, int max); + void setMaximum(int maximum); + void setProgressValue(int value); + void incrementProgressValue() { setProgressValue(m_value + 1); } + +private: + void eraseCurrentPercentageString(); + void updateProgressBarIfNecessary(); + void writePercentageString(); + + int m_maximum; + int m_value; + int m_percentage; + int m_hashesPrinted; +}; + +} // namespace qbs + +#endif // CONSOLEPROGRESSOBSERVER_H diff --git a/src/app/qbs/ctrlchandler.cpp b/src/app/qbs/ctrlchandler.cpp new file mode 100644 index 00000000..e1f67c4a --- /dev/null +++ b/src/app/qbs/ctrlchandler.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "application.h" + +static void cancel() +{ + qbs::Application * const app = qbs::Application::instance(); + if (app) + app->userInterrupt(); +} + +#if defined(Q_OS_WIN) && defined(Q_CC_MSVC) + +#include + +static BOOL WINAPI consoleCtrlHandlerRoutine(__in DWORD dwCtrlType) +{ + Q_UNUSED(dwCtrlType); + cancel(); + return TRUE; +} + +void installCtrlCHandler() +{ + SetConsoleCtrlHandler(&consoleCtrlHandlerRoutine, TRUE); +} + +#else + +#include + +static void sigIntHandler(int sig) +{ + Q_UNUSED(sig); + cancel(); +} + +void installCtrlCHandler() +{ + signal(SIGINT, sigIntHandler); +} + +#endif diff --git a/src/app/qbs/ctrlchandler.h b/src/app/qbs/ctrlchandler.h new file mode 100644 index 00000000..463b6f45 --- /dev/null +++ b/src/app/qbs/ctrlchandler.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CTRLCHANDLER_H +#define CTRLCHANDLER_H + +void installCtrlCHandler(); + +#endif // CTRLCHANDLER_H diff --git a/src/app/qbs/main.cpp b/src/app/qbs/main.cpp new file mode 100644 index 00000000..739c9bd6 --- /dev/null +++ b/src/app/qbs/main.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "application.h" +#include "commandlinefrontend.h" +#include "qbstool.h" +#include "parser/commandlineparser.h" +#include "../shared/logging/consolelogger.h" + +#include + +#include +#include + +using namespace qbs; + +static bool tryToRunTool(const QStringList &arguments, int &exitCode) +{ + if (arguments.isEmpty()) + return false; + QStringList toolArgs = arguments; + const QString toolName = toolArgs.takeFirst(); + if (toolName.startsWith(QLatin1Char('-'))) + return false; + return QbsTool::tryToRunTool(toolName, toolArgs, &exitCode); +} + +int main(int argc, char *argv[]) +{ + ConsoleLogger::instance(); + + try { + Application app(argc, argv); + QStringList arguments = app.arguments(); + arguments.removeFirst(); + + int toolExitCode = 0; + if (tryToRunTool(arguments, toolExitCode)) + return toolExitCode; + + CommandLineParser parser; + if (!parser.parseCommandLine(arguments)) + return EXIT_FAILURE; + + if (parser.command() == HelpCommandType) { + parser.printHelp(); + return 0; + } + + Settings settings(parser.settingsDir()); + ConsoleLogger::instance().setSettings(&settings); + CommandLineFrontend clFrontend(parser, &settings); + app.setCommandLineFrontend(&clFrontend); + QTimer::singleShot(0, &clFrontend, &CommandLineFrontend::start); + return app.exec(); + } catch (const ErrorInfo &error) { + qbsError() << error.toString(); + return EXIT_FAILURE; + } +} diff --git a/src/app/qbs/parser/command.cpp b/src/app/qbs/parser/command.cpp new file mode 100644 index 00000000..0c221473 --- /dev/null +++ b/src/app/qbs/parser/command.cpp @@ -0,0 +1,539 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "command.h" + +#include "commandlineoption.h" +#include "commandlineoptionpool.h" + +#include +#include +#include + +#include +#include + +namespace qbs { +using namespace Internal; + +Command::~Command() +{ +} + +void Command::parse(QStringList &input) +{ + parseOptions(input); + parseMore(input); + if (!input.isEmpty()) { + throw ErrorInfo(Tr::tr("Invalid use of command '%1': Extraneous input '%2'.\nUsage: %3") + .arg(representation(), input.join(QLatin1Char(' ')), longDescription())); + } +} + +void Command::addAllToAdditionalArguments(QStringList &input) +{ + while (!input.isEmpty()) + addOneToAdditionalArguments(input.takeFirst()); +} + +// TODO: Stricter checking for build variants and properties. +void Command::addOneToAdditionalArguments(const QString &argument) +{ + if (argument.startsWith(QLatin1Char('-'))) { + throw ErrorInfo(Tr::tr("Invalid use of command '%1': Encountered option '%2', expected a " + "build variant or property.\nUsage: %3") + .arg(representation(), argument, longDescription())); + } + m_additionalArguments << argument; +} + +QList Command::actualSupportedOptions() const +{ + QList options = supportedOptions(); + if (!HostOsInfo::isAnyUnixHost()) + options.removeOne(CommandLineOption::ShowProgressOptionType); + if (type() != HelpCommandType) + options << CommandLineOption::SettingsDirOptionType; // Valid for almost all commands. + return options; +} + +void Command::parseOptions(QStringList &input) +{ + QSet usedOptions; + while (!input.isEmpty()) { + const QString optionString = input.first(); + if (!optionString.startsWith(QLatin1Char('-'))) + break; + if (optionString == QLatin1String("--")) + break; + input.removeFirst(); + if (optionString.count() == 1) { + throw ErrorInfo(Tr::tr("Invalid use of command '%1': Empty options are not allowed.\n" + "Usage: %2").arg(representation(), longDescription())); + } + + // Split up grouped short options. + if (optionString.at(1) != QLatin1Char('-') && optionString.count() > 2) { + for (int i = optionString.count(); --i > 0;) + input.prepend(QLatin1Char('-') + optionString.at(i)); + continue; + } + + bool matchFound = false; + foreach (const CommandLineOption::Type optionType, actualSupportedOptions()) { + CommandLineOption * const option = optionPool().getOption(optionType); + if (option->shortRepresentation() != optionString + && option->longRepresentation() != optionString) { + continue; + } + if (usedOptions.contains(option) && !option->canAppearMoreThanOnce()) { + throw ErrorInfo(Tr::tr("Invalid use of command '%1': Option '%2' cannot appear " + "more than once.\nUsage: %3") + .arg(representation(), optionString, longDescription())); + } + option->parse(type(), optionString, input); + usedOptions << option; + matchFound = true; + break; + } + if (!matchFound) { + throw ErrorInfo(Tr::tr("Invalid use of command '%1': Unknown option '%2'.\nUsage: %3") + .arg(representation(), optionString, longDescription())); + } + } +} + +QString Command::supportedOptionsDescription() const +{ + // Sorting the options by name is nicer for the user. + QMap optionMap; + foreach (const CommandLineOption::Type opType, actualSupportedOptions()) { + const CommandLineOption * const option = optionPool().getOption(opType); + optionMap.insert(option->longRepresentation(), option); + } + + QString s = Tr::tr("The possible options are:\n"); + foreach (const CommandLineOption *option, optionMap) + s += option->description(type()); + return s; +} + +void Command::parseMore(QStringList &input) +{ + addAllToAdditionalArguments(input); +} + + +QString ResolveCommand::shortDescription() const +{ + return Tr::tr("Resolve a project without building it."); +} + +QString ResolveCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [[variant] [property:value] ...] ...\n") + .arg(representation()); + description += Tr::tr("Resolves a project in one or more configuration(s).\n"); + return description += supportedOptionsDescription(); +} + +QString ResolveCommand::representation() const +{ + return QLatin1String("resolve"); +} + +static QList resolveOptions() +{ + return QList() + << CommandLineOption::FileOptionType + << CommandLineOption::BuildDirectoryOptionType + << CommandLineOption::LogLevelOptionType + << CommandLineOption::VerboseOptionType + << CommandLineOption::QuietOptionType + << CommandLineOption::ShowProgressOptionType + << CommandLineOption::JobsOptionType + << CommandLineOption::DryRunOptionType + << CommandLineOption::ForceProbesOptionType + << CommandLineOption::LogTimeOptionType + << CommandLineOption::ForceOptionType; +} + +QList ResolveCommand::supportedOptions() const +{ + return resolveOptions(); +} + +QString GenerateCommand::shortDescription() const +{ + return Tr::tr("Generate project files for another build tool."); +} + +QString GenerateCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [[variant] [property:value] ...] ...\n") + .arg(representation()); + description += Tr::tr("Generates files to build the project using another build tool.\n"); + return description += supportedOptionsDescription(); +} + +QString GenerateCommand::representation() const +{ + return QLatin1String("generate"); +} + +QList GenerateCommand::supportedOptions() const +{ + return QList() + << CommandLineOption::FileOptionType + << CommandLineOption::BuildDirectoryOptionType + << CommandLineOption::LogLevelOptionType + << CommandLineOption::VerboseOptionType + << CommandLineOption::QuietOptionType + << CommandLineOption::ShowProgressOptionType + << CommandLineOption::InstallRootOptionType + << CommandLineOption::LogTimeOptionType + << CommandLineOption::GeneratorOptionType; +} + +QString BuildCommand::shortDescription() const +{ + return Tr::tr("Build (parts of) a project. This is the default command."); +} + +QString BuildCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [[variant] [property:value] ...] ...\n") + .arg(representation()); + description += Tr::tr("Builds a project in one or more configuration(s).\n"); + return description += supportedOptionsDescription(); +} + +static QString buildCommandRepresentation() { return QLatin1String("build"); } + +QString BuildCommand::representation() const +{ + return buildCommandRepresentation(); +} + +static QList buildOptions() +{ + QList options = resolveOptions(); + return options << CommandLineOption::KeepGoingOptionType + << CommandLineOption::ProductsOptionType + << CommandLineOption::ChangedFilesOptionType + << CommandLineOption::ForceTimestampCheckOptionType + << CommandLineOption::ForceOutputCheckOptionType + << CommandLineOption::BuildNonDefaultOptionType + << CommandLineOption::VersionOptionType + << CommandLineOption::CommandEchoModeOptionType + << CommandLineOption::NoInstallOptionType + << CommandLineOption::RemoveFirstOptionType + << CommandLineOption::WaitLockOptionType; +} + +QList BuildCommand::supportedOptions() const +{ + return buildOptions(); +} + +QString CleanCommand::shortDescription() const +{ + return Tr::tr("Remove the files generated during a build."); +} + +QString CleanCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [[variant] [property:value] ...] ...\n") + .arg(representation()); + description += Tr::tr("Removes build artifacts for the given project and configuration(s).\n"); + return description += supportedOptionsDescription(); +} + +QString CleanCommand::representation() const +{ + return QLatin1String("clean"); +} + +QList CleanCommand::supportedOptions() const +{ + QList options = buildOptions(); + options.removeOne(CommandLineOption::ChangedFilesOptionType); + options.removeOne(CommandLineOption::JobsOptionType); + options.removeOne(CommandLineOption::BuildNonDefaultOptionType); + options.removeOne(CommandLineOption::CommandEchoModeOptionType); + return options; +} + +QString InstallCommand::shortDescription() const +{ + return Tr::tr("Install (parts of) a project."); +} + +QString InstallCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [[variant] [property:value] ...]\n") + .arg(representation()); + description += Tr::tr("Install all files marked as installable " + "to their respective destinations.\n" + "The project is built first, if necessary, unless the '%1' option " + "is given.\n").arg(optionPool().noBuildOption()->longRepresentation()); + return description += supportedOptionsDescription(); +} + +QString InstallCommand::representation() const +{ + return QLatin1String("install"); +} + +QList installOptions() +{ + QList options = buildOptions() + << CommandLineOption::InstallRootOptionType + << CommandLineOption::NoBuildOptionType; + options.removeOne(CommandLineOption::NoInstallOptionType); + return options; +} + +QList InstallCommand::supportedOptions() const +{ + return installOptions(); +} + +QString RunCommand::shortDescription() const +{ + return QLatin1String("Run an executable generated by building a project."); +} + +QString RunCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [variant] [property:value] ... " + "[ -- ]\n").arg(representation()); + description += Tr::tr("Run the specified product's executable with the specified arguments.\n"); + description += Tr::tr("If the project has only one product, the '%1' option may be omitted.\n") + .arg(optionPool().productsOption()->longRepresentation()); + description += Tr::tr("The product will be built if it is not up to date; " + "see the '%2' command.\n").arg(buildCommandRepresentation()); + return description += supportedOptionsDescription(); +} + +QString RunCommand::representation() const +{ + return QLatin1String("run"); +} + +QList RunCommand::supportedOptions() const +{ + return installOptions(); +} + +void RunCommand::parseMore(QStringList &input) +{ + // Build variants and properties + while (!input.isEmpty()) { + const QString arg = input.takeFirst(); + if (arg == QLatin1String("--")) + break; + addOneToAdditionalArguments(arg); + } + + m_targetParameters = input; + input.clear(); +} + +QString ShellCommand::shortDescription() const +{ + return Tr::tr("Open a shell with a product's environment."); +} + +QString ShellCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [variant] [property:value] ...\n") + .arg(representation()); + description += Tr::tr("Opens a shell in the same environment that a build with the given " + "parameters would use.\n"); + const ProductsOption * const option = optionPool().productsOption(); + description += Tr::tr("The '%1' option may be omitted if and only if the project has " + "exactly one product.").arg(option->longRepresentation()); + return description += supportedOptionsDescription(); +} + +QString ShellCommand::representation() const +{ + return QLatin1String("shell"); +} + +QList ShellCommand::supportedOptions() const +{ + return QList() + << CommandLineOption::FileOptionType + << CommandLineOption::BuildDirectoryOptionType + << CommandLineOption::LogLevelOptionType + << CommandLineOption::VerboseOptionType + << CommandLineOption::QuietOptionType + << CommandLineOption::ShowProgressOptionType + << CommandLineOption::ProductsOptionType; +} + +QString StatusCommand::shortDescription() const +{ + return Tr::tr("Show the status of files in the project directory."); +} + +QString StatusCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [variant] [property:value] ...\n") + .arg(representation()); + description += Tr::tr("Lists all the files in the project directory and shows whether " + "they are known to qbs in the respective configuration.\n"); + return description += supportedOptionsDescription(); +} + +QString StatusCommand::representation() const +{ + return QLatin1String("status"); +} + +QList StatusCommand::supportedOptions() const +{ + return QList() + << CommandLineOption::FileOptionType + << CommandLineOption::BuildDirectoryOptionType + << CommandLineOption::LogLevelOptionType + << CommandLineOption::VerboseOptionType + << CommandLineOption::QuietOptionType + << CommandLineOption::ShowProgressOptionType; +} + +QString UpdateTimestampsCommand::shortDescription() const +{ + return Tr::tr("Mark the build as up to date."); +} + +QString UpdateTimestampsCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [[variant] [property:value] ...] ...\n") + .arg(representation()); + description += Tr::tr("Update the timestamps of all build artifacts, causing the next " + "builds of the project to do nothing if no updates to source files happen in between.\n" + "This functionality is useful if you know that the current changes to source files " + "are irrelevant to the build.\n" + "NOTE: Doing this causes a discrepancy between the \"real world\" and the information " + "in the build graph, so use with care.\n"); + return description += supportedOptionsDescription(); +} + +QString UpdateTimestampsCommand::representation() const +{ + return QLatin1String("update-timestamps"); +} + +QList UpdateTimestampsCommand::supportedOptions() const +{ + return QList() + << CommandLineOption::FileOptionType + << CommandLineOption::BuildDirectoryOptionType + << CommandLineOption::LogLevelOptionType + << CommandLineOption::VerboseOptionType + << CommandLineOption::QuietOptionType + << CommandLineOption::ProductsOptionType; +} + +QString DumpNodesTreeCommand::shortDescription() const +{ + return Tr::tr("Dumps the nodes in the build graph to stdout."); +} + +QString DumpNodesTreeCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 [options] [[variant] [property:value] ...] ...\n") + .arg(representation()); + description += Tr::tr("Internal command; for debugging purposes only.\n"); + return description += supportedOptionsDescription(); +} + +QString DumpNodesTreeCommand::representation() const +{ + return QLatin1String("dump-nodes-tree"); +} + +QList DumpNodesTreeCommand::supportedOptions() const +{ + return QList() + << CommandLineOption::LogLevelOptionType + << CommandLineOption::VerboseOptionType + << CommandLineOption::QuietOptionType + << CommandLineOption::FileOptionType + << CommandLineOption::BuildDirectoryOptionType + << CommandLineOption::ProductsOptionType; +} + +QString HelpCommand::shortDescription() const +{ + return Tr::tr("Show general or command-specific help."); +} + +QString HelpCommand::longDescription() const +{ + QString description = Tr::tr("qbs %1 []\n").arg(representation()); + return description += Tr::tr("Shows either the general help or a description of " + "the given command.\n"); +} + +QString HelpCommand::representation() const +{ + return QLatin1String("help"); +} + +QList HelpCommand::supportedOptions() const +{ + return QList(); +} + +void HelpCommand::parseMore(QStringList &input) +{ + if (input.isEmpty()) + return; + if (input.count() > 1) { + throw ErrorInfo(Tr::tr("Invalid use of command '%1': Cannot describe more than one command.\n" + "Usage: %2").arg(representation(), longDescription())); + } + m_command = input.takeFirst(); + Q_ASSERT(input.isEmpty()); +} + +} // namespace qbs diff --git a/src/app/qbs/parser/command.h b/src/app/qbs/parser/command.h new file mode 100644 index 00000000..3a3d6f98 --- /dev/null +++ b/src/app/qbs/parser/command.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PARSER_COMMAND_H +#define QBS_PARSER_COMMAND_H + +#include "commandlineoption.h" +#include "commandtype.h" + +namespace qbs { +class CommandLineOptionPool; + +class Command +{ +public: + virtual ~Command(); + + virtual CommandType type() const = 0; + virtual QString shortDescription() const = 0; + virtual QString longDescription() const = 0; + virtual QString representation() const = 0; + + void parse(QStringList &input); + QStringList additionalArguments() const { return m_additionalArguments; } + +protected: + Command(CommandLineOptionPool &optionPool) : m_optionPool(optionPool) {} + + const CommandLineOptionPool &optionPool() const { return m_optionPool; } + void addAllToAdditionalArguments(QStringList &input); + void addOneToAdditionalArguments(const QString &argument); + QString supportedOptionsDescription() const; + +private: + QList actualSupportedOptions() const; + void parseOptions(QStringList &input); + + virtual void parseMore(QStringList &input); + virtual QList supportedOptions() const = 0; + + QStringList m_additionalArguments; + const CommandLineOptionPool &m_optionPool; +}; + +class ResolveCommand : public Command +{ +public: + ResolveCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + +private: + CommandType type() const { return ResolveCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; +}; + +class GenerateCommand : public Command +{ +public: + GenerateCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + +private: + CommandType type() const { return GenerateCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; +}; + +class BuildCommand : public Command +{ +public: + BuildCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + +private: + CommandType type() const { return BuildCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; +}; + +class CleanCommand : public Command +{ +public: + CleanCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + +private: + CommandType type() const { return CleanCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; +}; + +class InstallCommand : public Command +{ +public: + InstallCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + +private: + CommandType type() const { return InstallCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; +}; + +class RunCommand : public Command +{ +public: + RunCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + QStringList targetParameters() const { return m_targetParameters; } + +private: + CommandType type() const { return RunCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; + void parseMore(QStringList &input); + + QStringList m_targetParameters; +}; + +class ShellCommand : public Command +{ +public: + ShellCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + +private: + CommandType type() const { return ShellCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; +}; + +// TODO: It seems wrong that a configuration has to be given here. Ideally, this command would just track *all* files regardless of conditions. Is that possible? +class StatusCommand : public Command +{ +public: + StatusCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + +private: + CommandType type() const { return StatusCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; +}; + +class UpdateTimestampsCommand : public Command +{ +public: + UpdateTimestampsCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + +private: + CommandType type() const { return UpdateTimestampsCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; +}; + +class DumpNodesTreeCommand : public Command +{ +public: + DumpNodesTreeCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + +private: + CommandType type() const Q_DECL_OVERRIDE { return DumpNodesTreeCommandType; } + QString shortDescription() const Q_DECL_OVERRIDE; + QString longDescription() const Q_DECL_OVERRIDE; + QString representation() const Q_DECL_OVERRIDE; + QList supportedOptions() const Q_DECL_OVERRIDE; +}; + +class HelpCommand : public Command +{ +public: + HelpCommand(CommandLineOptionPool &optionPool) : Command(optionPool) {} + QString commandToDescribe() const { return m_command; } + +private: + CommandType type() const { return HelpCommandType; } + QString shortDescription() const; + QString longDescription() const; + QString representation() const; + QList supportedOptions() const; + void parseMore(QStringList &input); + + QString m_command; +}; + +} // namespace qbs + +#endif // QBS_PARSER_COMMAND_H diff --git a/src/app/qbs/parser/commandlineoption.cpp b/src/app/qbs/parser/commandlineoption.cpp new file mode 100644 index 00000000..8689b203 --- /dev/null +++ b/src/app/qbs/parser/commandlineoption.cpp @@ -0,0 +1,661 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandlineoption.h" + +#include +#include +#include +#include + +namespace qbs { +using namespace Internal; + +CommandLineOption::~CommandLineOption() +{ +} + +void CommandLineOption::parse(CommandType command, const QString &representation, QStringList &input) +{ + m_command = command; + doParse(representation, input); +} + +CommandLineOption::CommandLineOption() + : m_command(static_cast(-1)) +{ +} + +QString CommandLineOption::getArgument(const QString &representation, QStringList &input) +{ + if (input.isEmpty()) { + throw ErrorInfo(Tr::tr("Invalid use of option '%1': Missing argument.\nUsage: %2") + .arg(representation, description(command()))); + } + return input.takeFirst(); +} + +QString FileOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1|%2 \n" + "\tUse as the project file.\n" + "\tIf is a directory and it contains a single file ending in '.qbs',\n" + "\tthat file will be used.\n" + "\tIf this option is not given at all, behavior is the same as for '-f .'.\n") + .arg(longRepresentation(), shortRepresentation()); +} + +QString FileOption::shortRepresentation() const +{ + return QLatin1String("-f"); +} + +QString FileOption::longRepresentation() const +{ + return QLatin1String("--file"); +} + +void FileOption::doParse(const QString &representation, QStringList &input) +{ + m_projectFilePath = getArgument(representation, input); +} + +QString BuildDirectoryOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1|%2 \n" + "\tBuild in the given directory. The default value is the current directory\n" + "\tunless preferences.defaultBuildDirectory is set.\n" + "\tRelative paths will be interpreted relative to the current directory.\n" + "\tIf the directory does not exist, it will be created. Use the following\n" + "\tspecial values as placeholders:\n" + "\t%3: name of the project file excluding the extension\n" + "\t%4: directory containing the project file\n") + .arg(longRepresentation(), shortRepresentation(), + magicProjectString(), magicProjectDirString()); +} + +QString BuildDirectoryOption::shortRepresentation() const +{ + return QLatin1String("-d"); +} + +QString BuildDirectoryOption::longRepresentation() const +{ + return QLatin1String("--build-directory"); +} + +QString BuildDirectoryOption::magicProjectString() +{ + return QLatin1String("@project"); +} + +QString BuildDirectoryOption::magicProjectDirString() +{ + return QLatin1String("@path"); +} + +void BuildDirectoryOption::doParse(const QString &representation, QStringList &input) +{ + m_projectBuildDirectory = getArgument(representation, input); +} + +QString GeneratorOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1|%2 \n" + "\tUse the given build system generator.\n") + .arg(longRepresentation(), shortRepresentation()); +} + +QString GeneratorOption::shortRepresentation() const +{ + return QLatin1String("-g"); +} + +QString GeneratorOption::longRepresentation() const +{ + return QLatin1String("--generator"); +} + +void GeneratorOption::doParse(const QString &representation, QStringList &input) +{ + m_generatorName = getArgument(representation, input); + if (m_generatorName.isEmpty()) { + throw ErrorInfo(Tr::tr("Invalid use of option '%1': No generator given.\nUsage: %2") + .arg(representation, description(command()))); + } +} + +static QString loglevelLongRepresentation() { return QLatin1String("--log-level"); } + +QString VerboseOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1|%2\n" + "\tBe more verbose. Increases the log level by one.\n" + "\tThis option can be given more than once.\n" + "\tExcessive occurrences have no effect.\n" + "\tIf option '%3' appears anywhere on the command line in addition\n" + "\tto this option, its value is taken as the base which to increase.\n") + .arg(longRepresentation(), shortRepresentation(), loglevelLongRepresentation()); +} + +QString VerboseOption::shortRepresentation() const +{ + return QLatin1String("-v"); +} + +QString VerboseOption::longRepresentation() const +{ + return QLatin1String("--more-verbose"); +} + +QString QuietOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1|%2\n" + "\tBe more quiet. Decreases the log level by one.\n" + "\tThis option can be given more than once.\n" + "\tExcessive occurrences have no effect.\n" + "\tIf option '%3' appears anywhere on the command line in addition\n" + "\tto this option, its value is taken as the base which to decrease.\n") + .arg(longRepresentation(), shortRepresentation(), loglevelLongRepresentation()); +} + +QString QuietOption::shortRepresentation() const +{ + return QLatin1String("-q"); +} + +QString QuietOption::longRepresentation() const +{ + return QLatin1String("--less-verbose"); +} + +QString JobsOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1|%2 \n" + "\tUse concurrent build jobs. must be an integer greater than zero.\n" + "\tThe default is the number of cores.\n") + .arg(longRepresentation(), shortRepresentation()); +} + +QString JobsOption::shortRepresentation() const +{ + return QLatin1String("-j"); +} + +QString JobsOption::longRepresentation() const +{ + return QLatin1String("--jobs"); +} + +void JobsOption::doParse(const QString &representation, QStringList &input) +{ + const QString jobCountString = getArgument(representation, input); + bool stringOk; + m_jobCount = jobCountString.toInt(&stringOk); + if (!stringOk || m_jobCount <= 0) + throw ErrorInfo(Tr::tr("Invalid use of option '%1': Illegal job count '%2'.\nUsage: %3") + .arg(representation, jobCountString, description(command()))); +} + +QString KeepGoingOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1|%2\n" + "\tKeep going when errors occur (if at all possible).\n") + .arg(longRepresentation(), shortRepresentation()); +} + +QString KeepGoingOption::shortRepresentation() const +{ + return QLatin1String("-k"); +} + +QString KeepGoingOption::longRepresentation() const +{ + return QLatin1String("--keep-going"); +} + +QString DryRunOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1|%2\n" + "\tDry run. No commands will be executed and no permanent changes to the\n" + "\tbuild graph will be done.\n") + .arg(longRepresentation(), shortRepresentation()); +} + +QString DryRunOption::shortRepresentation() const +{ + return QLatin1String("-n"); +} + +QString DryRunOption::longRepresentation() const +{ + return QLatin1String("--dry-run"); +} + +QString ForceProbesOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n" + "\tForce re-execution of all Probe items' configure scripts, rather than using the\n" + "\tcached data.\n") + .arg(longRepresentation()); +} + +QString ForceProbesOption::longRepresentation() const +{ + return QLatin1String("--force-probe-execution"); +} + +QString NoInstallOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n" + "\tDo not install any artifacts as part of the build process.\n") + .arg(longRepresentation()); +} + +QString NoInstallOption::longRepresentation() const +{ + return QLatin1String("--no-install"); +} + + +static QString logTimeRepresentation() +{ + return QLatin1String("--log-time"); +} + +QString ShowProgressOption::description(CommandType command) const +{ + Q_UNUSED(command); + QString desc = Tr::tr("%1\n" + "\tShow a progress bar. Implies '%2=%3'.\n").arg(longRepresentation(), + loglevelLongRepresentation(), logLevelName(LoggerMinLevel)); + return desc += Tr::tr("\tThis option is mutually exclusive with '%1'.\n") + .arg(logTimeRepresentation()); +} + +static QString showProgressRepresentation() +{ + return QLatin1String("--show-progress"); +} + +QString ShowProgressOption::longRepresentation() const +{ + return showProgressRepresentation(); +} + +void StringListOption::doParse(const QString &representation, QStringList &input) +{ + m_arguments = getArgument(representation, input).split(QLatin1Char(',')); + if (m_arguments.isEmpty()) { + throw ErrorInfo(Tr::tr("Invalid use of option '%1': Argument list must not be empty.\n" + "Usage: %2").arg(representation, description(command()))); + } + foreach (const QString &element, m_arguments) { + if (element.isEmpty()) { + throw ErrorInfo(Tr::tr("Invalid use of option '%1': Argument list must not contain " + "empty elements.\nUsage: %2") + .arg(representation, description(command()))); + } + } +} + +QString ChangedFilesOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1 [,...]\n" + "\tAssume these and only these files have changed.\n").arg(longRepresentation()); +} + +QString ChangedFilesOption::longRepresentation() const +{ + return QLatin1String("--changed-files"); +} + +QString ProductsOption::description(CommandType command) const +{ + const QString prefix = Tr::tr("%1|%2").arg(longRepresentation(), shortRepresentation()); + switch (command) { + case InstallCommandType: + case RunCommandType: + case ShellCommandType: + return Tr::tr("%1 \n\tUse the specified product.\n").arg(prefix); + default: + return Tr::tr("%1 [,...]\n" + "\tTake only the specified products into account.\n").arg(prefix); + } +} + +QString ProductsOption::shortRepresentation() const +{ + return QLatin1String("-p"); +} + +QString ProductsOption::longRepresentation() const +{ + return QLatin1String("--products"); +} + +static QStringList allLogLevelStrings() +{ + QStringList result; + for (int i = static_cast(LoggerMinLevel); i <= static_cast(LoggerMaxLevel); ++i) + result << logLevelName(static_cast(i)); + return result; +} + +LogLevelOption::LogLevelOption() : m_logLevel(defaultLogLevel()) +{ +} + +QString LogLevelOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1 \n" + "\tUse the specified log level.\n" + "\tPossible values are '%2'.\n" + "\tThe default is '%3'.\n").arg(longRepresentation(), + allLogLevelStrings().join(QLatin1String("', '")), logLevelName(defaultLogLevel())); +} + +QString LogLevelOption::longRepresentation() const +{ + return loglevelLongRepresentation(); +} + +void LogLevelOption::doParse(const QString &representation, QStringList &input) +{ + const QString levelString = getArgument(representation, input); + const QList levels = QList() << LoggerError << LoggerWarning + << LoggerInfo << LoggerDebug << LoggerTrace; + foreach (LoggerLevel l, levels) { + if (logLevelName(l) == levelString) { + m_logLevel = l; + return; + } + } + throw ErrorInfo(Tr::tr("Invalid use of option '%1': Unknown log level '%2'.\nUsage: %3") + .arg(representation, levelString, description(command()))); +} + +QString ForceOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n\tDisregard objections.\n" + "\tqbs might refuse to execute a given command because certain\n" + "\tcircumstances make it seem dubious. This option switches the\n" + "\trespective checks off.\n").arg(longRepresentation()); +} + +QString ForceOption::longRepresentation() const +{ + return QLatin1String("--force"); +} + +QString ForceTimeStampCheckOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n\tForce timestamp checks.\n" + "\tInstead of using the file timestamps that are stored in the build graph,\n" + "\tretrieve the timestamps from the file system.\n").arg(longRepresentation()); +} + +QString ForceTimeStampCheckOption::longRepresentation() const +{ + return QLatin1String("--check-timestamps"); +} + +QString ForceOutputCheckOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n\tForce transformer output artifact checks.\n" + "\tVerify that the output artifacts declared by rules in the\n" + "\tproject are actually created.\n").arg(longRepresentation()); +} + +QString ForceOutputCheckOption::longRepresentation() const +{ + return QLatin1String("--check-outputs"); +} + +QString BuildNonDefaultOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n\tBuild all products, even if their builtByDefault property is false.\n") + .arg(longRepresentation()); +} + +QString BuildNonDefaultOption::longRepresentation() const +{ + return QLatin1String("--all-products"); +} + +QString VersionOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n" + "\tDisplay the qbs version and exit.\n").arg(longRepresentation()); +} + +QString VersionOption::shortRepresentation() const +{ + return QString(); +} + +QString VersionOption::longRepresentation() const +{ + return QStringLiteral("--version"); +} + + +InstallRootOption::InstallRootOption() : m_useSysroot(false) +{ +} + +static QString magicSysrootString() { return QLatin1String("@sysroot"); } + +QString InstallRootOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1 \n" + "\tInstall into the given directory.\n" + "\tThe default value is '/%2'.\n" + "\tIf the directory does not exist, it will be created. Use the special\n" + "\tvalue '%3' to install into the sysroot (i.e. the value of the\n" + "\tproperty qbs.sysroot).\n") + .arg(longRepresentation(), InstallOptions::defaultInstallRoot(), magicSysrootString()); +} + +QString InstallRootOption::longRepresentation() const +{ + return QLatin1String("--install-root"); +} + +void InstallRootOption::doParse(const QString &representation, QStringList &input) +{ + if (input.isEmpty()) { + throw ErrorInfo(Tr::tr("Invalid use of option '%1: Argument expected.\n" + "Usage: %2").arg(representation, description(command()))); + } + const QString installRoot = input.takeFirst(); + if (installRoot == magicSysrootString()) + m_useSysroot = true; + else + m_installRoot = installRoot; +} + +QString RemoveFirstOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n\tRemove the installation base directory before installing.\n") + .arg(longRepresentation()); +} + +QString RemoveFirstOption::longRepresentation() const +{ + return QLatin1String("--clean-install-root"); +} + + +QString NoBuildOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n\tDo not build before installing.\n") + .arg(longRepresentation()); +} + +QString NoBuildOption::longRepresentation() const +{ + return QLatin1String("--no-build"); +} + + +QString LogTimeOption::description(CommandType command) const +{ + Q_UNUSED(command); + QString desc = Tr::tr("%1\n\tLog the time that the operations involved in this command take.\n") + .arg(longRepresentation()); + desc += Tr::tr("\tThis option is implied in log levels '%1' and higher.\n") + .arg(logLevelName(LoggerDebug)); + return desc += Tr::tr("\tThis option is mutually exclusive with '%1'.\n") + .arg(showProgressRepresentation()); +} + +QString LogTimeOption::shortRepresentation() const +{ + return QLatin1String("-t"); +} + +QString LogTimeOption::longRepresentation() const +{ + return logTimeRepresentation(); +} + + +SettingsDirOption::SettingsDirOption() +{ +} + +QString SettingsDirOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1 \n" + "\tRead all settings (such as profile information) from the given directory.\n" + "\tThe default value is system-specific (see the QSettings documentation).\n" + "\tIf the directory does not exist, it will be created.\n") + .arg(longRepresentation()); +} + +QString SettingsDirOption::longRepresentation() const +{ + return QLatin1String("--settings-dir"); +} + +void SettingsDirOption::doParse(const QString &representation, QStringList &input) +{ + if (input.isEmpty()) { + throw ErrorInfo(Tr::tr("Invalid use of option '%1: Argument expected.\n" + "Usage: %2").arg(representation, description(command()))); + } + m_settingsDir = input.takeFirst(); +} + +CommandEchoModeOption::CommandEchoModeOption() +{ +} + +QString CommandEchoModeOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1 \n" + "\tKind of output to show when executing commands.\n" + "\tPossible values are '%2'.\n" + "\tThe default is '%3'.\n") + .arg(longRepresentation(), allCommandEchoModeStrings().join(QLatin1String("', '")), + commandEchoModeName(defaultCommandEchoMode())); +} + +QString CommandEchoModeOption::longRepresentation() const +{ + return QLatin1String("--command-echo-mode"); +} + +CommandEchoMode CommandEchoModeOption::commandEchoMode() const +{ + return m_echoMode; +} + +void CommandEchoModeOption::doParse(const QString &representation, QStringList &input) +{ + const QString mode = getArgument(representation, input); + if (mode.isEmpty()) { + throw ErrorInfo(Tr::tr("Invalid use of option '%1': No command echo mode given.\nUsage: %2") + .arg(representation, description(command()))); + } + + if (!allCommandEchoModeStrings().contains(mode)) { + throw ErrorInfo(Tr::tr("Invalid use of option '%1': " + "Invalid command echo mode '%2' given.\nUsage: %3") + .arg(representation, mode, description(command()))); + } + + m_echoMode = commandEchoModeFromName(mode); +} + +QString WaitLockOption::description(CommandType command) const +{ + Q_UNUSED(command); + return Tr::tr("%1\n\tWait indefinitely for other processes to release the build graph lock.\n") + .arg(longRepresentation()); +} + +QString WaitLockOption::longRepresentation() const +{ + return QLatin1String("--wait-lock"); +} + +} // namespace qbs diff --git a/src/app/qbs/parser/commandlineoption.h b/src/app/qbs/parser/commandlineoption.h new file mode 100644 index 00000000..9a9efd7e --- /dev/null +++ b/src/app/qbs/parser/commandlineoption.h @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_COMMANDLINEOPTION_H +#define QBS_COMMANDLINEOPTION_H + +#include "commandtype.h" + +#include + +#include + +namespace qbs { + +class CommandLineOption +{ +public: + enum Type { + FileOptionType, + BuildDirectoryOptionType, + LogLevelOptionType, VerboseOptionType, QuietOptionType, + JobsOptionType, + KeepGoingOptionType, + DryRunOptionType, + ForceProbesOptionType, + ShowProgressOptionType, + ChangedFilesOptionType, + ProductsOptionType, + NoInstallOptionType, + InstallRootOptionType, RemoveFirstOptionType, NoBuildOptionType, + ForceOptionType, + ForceTimestampCheckOptionType, + ForceOutputCheckOptionType, + BuildNonDefaultOptionType, + VersionOptionType, + LogTimeOptionType, + CommandEchoModeOptionType, + SettingsDirOptionType, + GeneratorOptionType, + WaitLockOptionType + }; + + virtual ~CommandLineOption(); + virtual QString description(CommandType command) const = 0; + virtual QString shortRepresentation() const = 0; + virtual QString longRepresentation() const = 0; + virtual bool canAppearMoreThanOnce() const { return false; } + + void parse(CommandType command, const QString &representation, QStringList &input); + +protected: + CommandLineOption(); + QString getArgument(const QString &representation, QStringList &input); + CommandType command() const { return m_command; } + +private: + virtual void doParse(const QString &representation, QStringList &input) = 0; + + CommandType m_command; +}; + +class FileOption : public CommandLineOption +{ +public: + QString projectFilePath() const { return m_projectFilePath; } + +private: + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; + void doParse(const QString &representation, QStringList &input); + +private: + QString m_projectFilePath; +}; + +class BuildDirectoryOption : public CommandLineOption +{ +public: + QString projectBuildDirectory() const { return m_projectBuildDirectory; } + static QString magicProjectString(); + static QString magicProjectDirString(); + +private: + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; + void doParse(const QString &representation, QStringList &input); + +private: + QString m_projectBuildDirectory; +}; + +class GeneratorOption : public CommandLineOption +{ +public: + QString generatorName() const { return m_generatorName; } + +private: + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; + void doParse(const QString &representation, QStringList &input); + +private: + QString m_generatorName; +}; + +class CountingOption : public CommandLineOption +{ +public: + int count() const { return m_count; } + +protected: + CountingOption() : m_count(0) {} + +private: + bool canAppearMoreThanOnce() const { return true; } + void doParse(const QString & /* representation */, QStringList & /* input */) { ++m_count; } + + int m_count; +}; + +class VerboseOption : public CountingOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; +}; + +class QuietOption : public CountingOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; +}; + +class JobsOption : public CommandLineOption +{ +public: + JobsOption() : m_jobCount(0) {} + int jobCount() const { return m_jobCount; } + +private: + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; + void doParse(const QString &representation, QStringList &input); + + int m_jobCount; +}; + +class OnOffOption : public CommandLineOption +{ +public: + bool enabled() const { return m_enabled; } + +protected: + OnOffOption() : m_enabled(false) {} + +private: + void doParse(const QString & /* representation */, QStringList & /* input */) { m_enabled = true; } + + bool m_enabled; +}; + +class KeepGoingOption : public OnOffOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; +}; + +class DryRunOption : public OnOffOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; +}; + +class ForceProbesOption : public OnOffOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class NoInstallOption : public OnOffOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class ShowProgressOption : public OnOffOption +{ +public: + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class ForceOption : public OnOffOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class ForceTimeStampCheckOption : public OnOffOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class ForceOutputCheckOption : public OnOffOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class BuildNonDefaultOption : public OnOffOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class VersionOption : public OnOffOption +{ +private: + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; + +private: + QString m_projectFilePath; +}; + + +class StringListOption : public CommandLineOption +{ +public: + QStringList arguments() const { return m_arguments; } + +private: + void doParse(const QString &representation, QStringList &input); + + QStringList m_arguments; +}; + +class ChangedFilesOption : public StringListOption +{ + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class ProductsOption : public StringListOption +{ +public: + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; +}; + +class LogLevelOption : public CommandLineOption +{ +public: + LogLevelOption(); + int logLevel() const { return m_logLevel; } + +private: + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; + void doParse(const QString &representation, QStringList &input); + + int m_logLevel; +}; + +class InstallRootOption : public CommandLineOption +{ +public: + InstallRootOption(); + + QString installRoot() const { return m_installRoot; } + bool useSysroot() const { return m_useSysroot; } + + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; + +private: + void doParse(const QString &representation, QStringList &input); + + QString m_installRoot; + bool m_useSysroot; +}; + +class RemoveFirstOption : public OnOffOption +{ +public: + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class NoBuildOption : public OnOffOption +{ +public: + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +class LogTimeOption : public OnOffOption +{ +public: + QString description(CommandType command) const; + QString shortRepresentation() const; + QString longRepresentation() const; +}; + +class CommandEchoModeOption : public CommandLineOption +{ +public: + CommandEchoModeOption(); + + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; + CommandEchoMode commandEchoMode() const; + +private: + void doParse(const QString &representation, QStringList &input); + + CommandEchoMode m_echoMode = CommandEchoModeInvalid; +}; + +class SettingsDirOption : public CommandLineOption +{ +public: + SettingsDirOption(); + + QString settingsDir() const { return m_settingsDir; } + + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; + +private: + void doParse(const QString &representation, QStringList &input); + + QString m_settingsDir; +}; + +class WaitLockOption : public OnOffOption +{ +public: + QString description(CommandType command) const; + QString shortRepresentation() const { return QString(); } + QString longRepresentation() const; +}; + +} // namespace qbs + +#endif // QBS_COMMANDLINEOPTION_H diff --git a/src/app/qbs/parser/commandlineoptionpool.cpp b/src/app/qbs/parser/commandlineoptionpool.cpp new file mode 100644 index 00000000..2fc3e0e1 --- /dev/null +++ b/src/app/qbs/parser/commandlineoptionpool.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandlineoptionpool.h" + +namespace qbs { + +CommandLineOptionPool::~CommandLineOptionPool() +{ + qDeleteAll(m_options); +} + +CommandLineOption *CommandLineOptionPool::getOption(CommandLineOption::Type type) const +{ + CommandLineOption *& option = m_options[type]; + if (!option) { + switch (type) { + case CommandLineOption::FileOptionType: + option = new FileOption; + break; + case CommandLineOption::BuildDirectoryOptionType: + option = new BuildDirectoryOption; + break; + case CommandLineOption::LogLevelOptionType: + option = new LogLevelOption; + break; + case CommandLineOption::VerboseOptionType: + option = new VerboseOption; + break; + case CommandLineOption::QuietOptionType: + option = new QuietOption; + break; + case CommandLineOption::JobsOptionType: + option = new JobsOption; + break; + case CommandLineOption::KeepGoingOptionType: + option = new KeepGoingOption; + break; + case CommandLineOption::DryRunOptionType: + option = new DryRunOption; + break; + case CommandLineOption::ForceProbesOptionType: + option = new ForceProbesOption; + break; + case CommandLineOption::ShowProgressOptionType: + option = new ShowProgressOption; + break; + case CommandLineOption::ChangedFilesOptionType: + option = new ChangedFilesOption; + break; + case CommandLineOption::ProductsOptionType: + option = new ProductsOption; + break; + case CommandLineOption::NoInstallOptionType: + option = new NoInstallOption; + break; + case CommandLineOption::InstallRootOptionType: + option = new InstallRootOption; + break; + case CommandLineOption::RemoveFirstOptionType: + option = new RemoveFirstOption; + break; + case CommandLineOption::NoBuildOptionType: + option = new NoBuildOption; + break; + case CommandLineOption::ForceOptionType: + option = new ForceOption; + break; + case CommandLineOption::ForceTimestampCheckOptionType: + option = new ForceTimeStampCheckOption; + break; + case CommandLineOption::ForceOutputCheckOptionType: + option = new ForceOutputCheckOption; + break; + case CommandLineOption::BuildNonDefaultOptionType: + option = new BuildNonDefaultOption; + break; + case CommandLineOption::VersionOptionType: + option = new VersionOption; + break; + case CommandLineOption::LogTimeOptionType: + option = new LogTimeOption; + break; + case CommandLineOption::CommandEchoModeOptionType: + option = new CommandEchoModeOption; + break; + case CommandLineOption::SettingsDirOptionType: + option = new SettingsDirOption; + break; + case CommandLineOption::GeneratorOptionType: + option = new GeneratorOption; + break; + case CommandLineOption::WaitLockOptionType: + option = new WaitLockOption; + break; + default: + qFatal("Unknown option type %d", type); + } + } + return option; +} + +FileOption *CommandLineOptionPool::fileOption() const +{ + return static_cast(getOption(CommandLineOption::FileOptionType)); +} + +BuildDirectoryOption *CommandLineOptionPool::buildDirectoryOption() const +{ + return static_cast(getOption(CommandLineOption::BuildDirectoryOptionType)); +} + +LogLevelOption *CommandLineOptionPool::logLevelOption() const +{ + return static_cast(getOption(CommandLineOption::LogLevelOptionType)); +} + +VerboseOption *CommandLineOptionPool::verboseOption() const +{ + return static_cast(getOption(CommandLineOption::VerboseOptionType)); +} + +QuietOption *CommandLineOptionPool::quietOption() const +{ + return static_cast(getOption(CommandLineOption::QuietOptionType)); +} + +ShowProgressOption *CommandLineOptionPool::showProgressOption() const +{ + return static_cast(getOption(CommandLineOption::ShowProgressOptionType)); +} + +DryRunOption *CommandLineOptionPool::dryRunOption() const +{ + return static_cast(getOption(CommandLineOption::DryRunOptionType)); +} + +ForceProbesOption *CommandLineOptionPool::forceProbesOption() const +{ + return static_cast(getOption(CommandLineOption::ForceProbesOptionType)); +} + +ChangedFilesOption *CommandLineOptionPool::changedFilesOption() const +{ + return static_cast(getOption(CommandLineOption::ChangedFilesOptionType)); +} + +KeepGoingOption *CommandLineOptionPool::keepGoingOption() const +{ + return static_cast(getOption(CommandLineOption::KeepGoingOptionType)); +} + +JobsOption *CommandLineOptionPool::jobsOption() const +{ + return static_cast(getOption(CommandLineOption::JobsOptionType)); +} + +ProductsOption *CommandLineOptionPool::productsOption() const +{ + return static_cast(getOption(CommandLineOption::ProductsOptionType)); +} + +NoInstallOption *CommandLineOptionPool::noInstallOption() const +{ + return static_cast(getOption(CommandLineOption::NoInstallOptionType)); +} + +InstallRootOption *CommandLineOptionPool::installRootOption() const +{ + return static_cast(getOption(CommandLineOption::InstallRootOptionType)); +} + +RemoveFirstOption *CommandLineOptionPool::removeFirstoption() const +{ + return static_cast(getOption(CommandLineOption::RemoveFirstOptionType)); +} + +NoBuildOption *CommandLineOptionPool::noBuildOption() const +{ + return static_cast(getOption(CommandLineOption::NoBuildOptionType)); +} + +ForceOption *CommandLineOptionPool::forceOption() const +{ + return static_cast(getOption(CommandLineOption::ForceOptionType)); +} + +ForceTimeStampCheckOption *CommandLineOptionPool::forceTimestampCheckOption() const +{ + return static_cast( + getOption(CommandLineOption::ForceTimestampCheckOptionType)); +} + +ForceOutputCheckOption *CommandLineOptionPool::forceOutputCheckOption() const +{ + return static_cast( + getOption(CommandLineOption::ForceOutputCheckOptionType)); +} + +BuildNonDefaultOption *CommandLineOptionPool::buildNonDefaultOption() const +{ + return static_cast( + getOption(CommandLineOption::BuildNonDefaultOptionType)); +} + +VersionOption *CommandLineOptionPool::versionOption() const +{ + return static_cast( + getOption(CommandLineOption::VersionOptionType)); +} + +LogTimeOption *CommandLineOptionPool::logTimeOption() const +{ + return static_cast(getOption(CommandLineOption::LogTimeOptionType)); +} + +CommandEchoModeOption *CommandLineOptionPool::commandEchoModeOption() const +{ + return static_cast( + getOption(CommandLineOption::CommandEchoModeOptionType)); +} + +SettingsDirOption *CommandLineOptionPool::settingsDirOption() const +{ + return static_cast(getOption(CommandLineOption::SettingsDirOptionType)); +} + +GeneratorOption *CommandLineOptionPool::generatorOption() const +{ + return static_cast(getOption(CommandLineOption::GeneratorOptionType)); +} + +WaitLockOption *CommandLineOptionPool::waitLockOption() const +{ + return static_cast(getOption(CommandLineOption::WaitLockOptionType)); +} + +} // namespace qbs diff --git a/src/app/qbs/parser/commandlineoptionpool.h b/src/app/qbs/parser/commandlineoptionpool.h new file mode 100644 index 00000000..b878317a --- /dev/null +++ b/src/app/qbs/parser/commandlineoptionpool.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_COMMANDLINEOPTIONPOOL_H +#define QBS_COMMANDLINEOPTIONPOOL_H + +#include "commandlineoption.h" + +#include + +namespace qbs { + +class CommandLineOptionPool +{ +public: + ~CommandLineOptionPool(); + + CommandLineOption *getOption(CommandLineOption::Type type) const; + FileOption *fileOption() const; + BuildDirectoryOption *buildDirectoryOption() const; + LogLevelOption *logLevelOption() const; + VerboseOption *verboseOption() const; + QuietOption *quietOption() const; + ShowProgressOption *showProgressOption() const; + DryRunOption *dryRunOption() const; + ForceProbesOption *forceProbesOption() const; + ChangedFilesOption *changedFilesOption() const; + KeepGoingOption *keepGoingOption() const; + JobsOption *jobsOption() const; + ProductsOption *productsOption() const; + NoInstallOption *noInstallOption() const; + InstallRootOption *installRootOption() const; + RemoveFirstOption *removeFirstoption() const; + NoBuildOption *noBuildOption() const; + ForceOption *forceOption() const; + ForceTimeStampCheckOption *forceTimestampCheckOption() const; + ForceOutputCheckOption *forceOutputCheckOption() const; + BuildNonDefaultOption *buildNonDefaultOption() const; + VersionOption *versionOption() const; + LogTimeOption *logTimeOption() const; + CommandEchoModeOption *commandEchoModeOption() const; + SettingsDirOption *settingsDirOption() const; + GeneratorOption *generatorOption() const; + WaitLockOption *waitLockOption() const; + +private: + mutable QHash m_options; +}; + +} // namespace qbs + +#endif // QBS_COMMANDLINEOPTIONPOOL_H diff --git a/src/app/qbs/parser/commandlineparser.cpp b/src/app/qbs/parser/commandlineparser.cpp new file mode 100644 index 00000000..6448d04a --- /dev/null +++ b/src/app/qbs/parser/commandlineparser.cpp @@ -0,0 +1,648 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "commandlineparser.h" + +#include "command.h" +#include "commandlineoption.h" +#include "commandlineoptionpool.h" +#include "commandpool.h" +#include "../qbstool.h" +#include "../../shared/logging/consolelogger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef Q_OS_UNIX +#include +#endif + +namespace qbs { +using Internal::Tr; + +class CommandLineParser::CommandLineParserPrivate +{ +public: + CommandLineParserPrivate(); + + void doParse(); + Command *commandFromString(const QString &commandString) const; + QList allCommands() const; + QString generalHelp() const; + + void setupProjectFile(); + void setupBuildDirectory(); + void setupProgress(); + void setupLogLevel(); + void setupBuildOptions(); + bool checkForExistingBuildConfiguration(const QList &buildConfigs, + const QString &configurationName); + bool isSameProfile(const QString &profile1, const QString &profile2) const; + bool withNonDefaultProducts() const; + bool dryRun() const; + QString settingsDir() const { return optionPool.settingsDirOption()->settingsDir(); } + + CommandEchoMode echoMode() const; + + QString propertyName(const QString &aCommandLineName) const; + + QStringList commandLine; + Command *command; + QString projectFilePath; + QString projectBuildDirectory; + BuildOptions buildOptions; + CommandLineOptionPool optionPool; + CommandPool commandPool; + bool showProgress; + bool logTime; +}; + +CommandLineParser::CommandLineParser() : d(0) +{ +} + +CommandLineParser::~CommandLineParser() +{ + delete d; +} + +void CommandLineParser::printHelp() const +{ + QTextStream stream(stdout); + + Q_ASSERT(d->command == d->commandPool.getCommand(HelpCommandType)); + const HelpCommand * const helpCommand = static_cast(d->command); + if (helpCommand->commandToDescribe().isEmpty()) { + stream << "qbs " QBS_VERSION "\n"; + stream << d->generalHelp(); + } else { + const Command * const commandToDescribe + = d->commandFromString(helpCommand->commandToDescribe()); + if (commandToDescribe) { + stream << commandToDescribe->longDescription(); + } else if (!QbsTool::tryToRunTool(helpCommand->commandToDescribe(), + QStringList(QLatin1String("--help")))) { + throw ErrorInfo(Tr::tr("No such command '%1'.\n%2") + .arg(helpCommand->commandToDescribe(), d->generalHelp())); + } + } +} + +CommandType CommandLineParser::command() const +{ + return d->command->type(); +} + +QString CommandLineParser::projectFilePath() const +{ + return d->projectFilePath; +} + +QString CommandLineParser::projectBuildDirectory() const +{ + return d->projectBuildDirectory; +} + +BuildOptions CommandLineParser::buildOptions(const QString &profile) const +{ + Settings settings(settingsDir()); + Preferences preferences(&settings, profile); + + if (d->buildOptions.maxJobCount() <= 0) { + d->buildOptions.setMaxJobCount(preferences.jobs()); + } + + if (d->buildOptions.echoMode() < 0) { + d->buildOptions.setEchoMode(preferences.defaultEchoMode()); + } + + return d->buildOptions; +} + +CleanOptions CommandLineParser::cleanOptions(const QString &profile) const +{ + CleanOptions options; + options.setDryRun(buildOptions(profile).dryRun()); + options.setKeepGoing(buildOptions(profile).keepGoing()); + options.setLogElapsedTime(logTime()); + return options; +} + +GenerateOptions CommandLineParser::generateOptions() const +{ + GenerateOptions options; + options.setGeneratorName(d->optionPool.generatorOption()->generatorName()); + return options; +} + +InstallOptions CommandLineParser::installOptions(const QString &profile) const +{ + InstallOptions options; + options.setRemoveExistingInstallation(d->optionPool.removeFirstoption()->enabled()); + options.setInstallRoot(d->optionPool.installRootOption()->installRoot()); + options.setInstallIntoSysroot(d->optionPool.installRootOption()->useSysroot()); + if (!options.installRoot().isEmpty()) { + QFileInfo fi(options.installRoot()); + if (!fi.isAbsolute()) + options.setInstallRoot(fi.absoluteFilePath()); + } + options.setDryRun(buildOptions(profile).dryRun()); + options.setKeepGoing(buildOptions(profile).keepGoing()); + options.setLogElapsedTime(logTime()); + return options; +} + +bool CommandLineParser::force() const +{ + return d->optionPool.forceOption()->enabled(); +} + +bool CommandLineParser::forceTimestampCheck() const +{ + return d->optionPool.forceTimestampCheckOption()->enabled(); +} + +bool CommandLineParser::forceOutputCheck() const +{ + return d->optionPool.forceOutputCheckOption()->enabled(); +} + +bool CommandLineParser::dryRun() const +{ + return d->dryRun(); +} + +bool CommandLineParser::forceProbesExecution() const +{ + return d->optionPool.forceProbesOption()->enabled(); +} + +bool CommandLineParser::waitLockBuildGraph() const +{ + return d->optionPool.waitLockOption()->enabled(); +} + +bool CommandLineParser::logTime() const +{ + return d->logTime; +} + +bool CommandLineParser::withNonDefaultProducts() const +{ + return d->withNonDefaultProducts(); +} + +bool CommandLineParser::buildBeforeInstalling() const +{ + return !d->optionPool.noBuildOption()->enabled(); +} + +QStringList CommandLineParser::runArgs() const +{ + Q_ASSERT(d->command->type() == RunCommandType); + return static_cast(d->command)->targetParameters(); +} + +QStringList CommandLineParser::products() const +{ + return d->optionPool.productsOption()->arguments(); +} + +bool CommandLineParser::showProgress() const +{ + return d->showProgress; +} + +bool CommandLineParser::showVersion() const +{ + return d->optionPool.versionOption()->enabled(); +} + +QString CommandLineParser::settingsDir() const +{ + return d->settingsDir(); +} + +QString CommandLineParser::commandName() const +{ + return d->command->representation(); +} + +QString CommandLineParser::commandDescription() const +{ + return d->command->longDescription(); +} + +static QString getBuildConfigurationName(const QVariantMap &buildConfig) +{ + return buildConfig.value(QLatin1String("qbs.configurationName")).toString(); +} + +QList CommandLineParser::buildConfigurations() const +{ + // first: configuration name, second: properties. + // Empty configuration name used for global properties. + typedef QPair PropertyListItem; + QList propertiesPerConfiguration; + + const QString configurationNameKey = QLatin1String("qbs.configurationName"); + QString currentConfigurationName; + QVariantMap currentProperties; + foreach (const QString &arg, d->command->additionalArguments()) { + const int sepPos = arg.indexOf(QLatin1Char(':')); + if (sepPos == -1) { // New build configuration found. + propertiesPerConfiguration << qMakePair(currentConfigurationName, currentProperties); + currentConfigurationName = arg; + currentProperties.clear(); + continue; + } + const QString property = d->propertyName(arg.left(sepPos)); + if (property.isEmpty()) { + qbsWarning() << Tr::tr("Ignoring empty property."); + } else if (property == configurationNameKey) { + qbsWarning() << Tr::tr("Refusing to overwrite special property '%1'.") + .arg(configurationNameKey); + } else { + const QString rawString = arg.mid(sepPos + 1); + currentProperties.insert(property, representationToSettingsValue(rawString)); + } + } + propertiesPerConfiguration << qMakePair(currentConfigurationName, currentProperties); + + if (propertiesPerConfiguration.count() == 1) // No configuration name specified on command line. + propertiesPerConfiguration << PropertyListItem(QStringLiteral("default"), QVariantMap()); + + const QVariantMap globalProperties = propertiesPerConfiguration.takeFirst().second; + QList buildConfigs; + foreach (const PropertyListItem &item, propertiesPerConfiguration) { + QVariantMap properties = item.second; + for (QVariantMap::ConstIterator globalPropIt = globalProperties.constBegin(); + globalPropIt != globalProperties.constEnd(); ++globalPropIt) { + if (!properties.contains(globalPropIt.key())) + properties.insert(globalPropIt.key(), globalPropIt.value()); + } + + const QString configurationName = item.first; + if (d->checkForExistingBuildConfiguration(buildConfigs, configurationName)) { + qbsWarning() << Tr::tr("Ignoring redundant request to build for configuration '%1'.") + .arg(configurationName); + continue; + } + + properties.insert(configurationNameKey, configurationName); + buildConfigs << properties; + } + + return buildConfigs; +} + +bool CommandLineParser::parseCommandLine(const QStringList &args) +{ + delete d; + d = new CommandLineParserPrivate; + d->commandLine = args; + try { + d->doParse(); + return true; + } catch (const ErrorInfo &error) { + qbsError() << error.toString(); + return false; + } +} + + +CommandLineParser::CommandLineParserPrivate::CommandLineParserPrivate() + : command(0), commandPool(optionPool), showProgress(false), logTime(false) +{ +} + +void CommandLineParser::CommandLineParserPrivate::doParse() +{ + if (commandLine.isEmpty()) { // No command given, use default. + command = commandPool.getCommand(BuildCommandType); + } else { + command = commandFromString(commandLine.first()); + if (command) { + commandLine.removeFirst(); + } else { // No command given. + // As an exception to the command-based syntax, we allow -h or --help as the + // sole contents of the command line, because people are used to this working. + if (commandLine.count() == 1 && (commandLine.first() == QLatin1String("-h") + || commandLine.first() == QLatin1String("--help"))) { + command = commandPool.getCommand(HelpCommandType); + commandLine.clear(); + } else { + command = commandPool.getCommand(BuildCommandType); + } + } + } + command->parse(commandLine); + + if (command->type() == HelpCommandType) + return; + + if (command->type() == BuildCommandType && optionPool.versionOption()->enabled()) + return; + + setupProjectFile(); + setupBuildDirectory(); + setupProgress(); + setupLogLevel(); + setupBuildOptions(); +} + +Command *CommandLineParser::CommandLineParserPrivate::commandFromString(const QString &commandString) const +{ + foreach (Command * const command, allCommands()) { + if (command->representation() == commandString) + return command; + } + return 0; +} + +QList CommandLineParser::CommandLineParserPrivate::allCommands() const +{ + return QList() + << commandPool.getCommand(GenerateCommandType) + << commandPool.getCommand(ResolveCommandType) + << commandPool.getCommand(BuildCommandType) + << commandPool.getCommand(CleanCommandType) + << commandPool.getCommand(RunCommandType) + << commandPool.getCommand(ShellCommandType) + << commandPool.getCommand(StatusCommandType) + << commandPool.getCommand(UpdateTimestampsCommandType) + << commandPool.getCommand(InstallCommandType) + << commandPool.getCommand(DumpNodesTreeCommandType) + << commandPool.getCommand(HelpCommandType); +} + +QString CommandLineParser::CommandLineParserPrivate::generalHelp() const +{ + QString help = Tr::tr("Usage: qbs [command] [command parameters]\n"); + help += Tr::tr("Internal commands:\n"); + const int rhsIndentation = 30; + + // Sorting the commands by name is nicer for the user. + QMap commandMap; + foreach (const Command * command, allCommands()) + commandMap.insert(command->representation(), command); + + foreach (const Command * command, commandMap) { + help.append(QLatin1String(" ")).append(command->representation()); + const QString whitespace + = QString(rhsIndentation - 2 - command->representation().count(), QLatin1Char(' ')); + help.append(whitespace).append(command->shortDescription()).append(QLatin1Char('\n')); + } + + QStringList toolNames = QbsTool::allToolNames(); + toolNames.sort(); + if (!toolNames.isEmpty()) { + help.append(QLatin1Char('\n')).append(Tr::tr("Auxiliary commands:\n")); + foreach (const QString &toolName, toolNames) { + help.append(QLatin1String(" ")).append(toolName); + const QString whitespace = QString(rhsIndentation - 2 - toolName.count(), + QLatin1Char(' ')); + QbsTool tool; + tool.runTool(toolName, QStringList(QLatin1String("--help"))); + if (tool.exitCode() != 0) + continue; + const QString shortDescription + = tool.stdOut().left(tool.stdOut().indexOf(QLatin1Char('\n'))); + help.append(whitespace).append(shortDescription).append(QLatin1Char('\n')); + } + } + + return help; +} + +void CommandLineParser::CommandLineParserPrivate::setupProjectFile() +{ + projectFilePath = optionPool.fileOption()->projectFilePath(); + if (projectFilePath.isEmpty()) { + qbsDebug() << "No project file given; looking in current directory."; + projectFilePath = QDir::currentPath(); + } + + const QFileInfo projectFileInfo(projectFilePath); + if (!projectFileInfo.exists()) + throw ErrorInfo(Tr::tr("Project file '%1' cannot be found.").arg(projectFilePath)); + if (projectFileInfo.isRelative()) + projectFilePath = projectFileInfo.absoluteFilePath(); + if (projectFileInfo.isFile()) + return; + if (!projectFileInfo.isDir()) + throw ErrorInfo(Tr::tr("Project file '%1' has invalid type.").arg(projectFilePath)); + + const QStringList namePatterns = QStringList() + << QLatin1String("*.qbs"); + + const QStringList &actualFileNames + = QDir(projectFilePath).entryList(namePatterns, QDir::Files); + if (actualFileNames.isEmpty()) { + QString error; + if (optionPool.fileOption()->projectFilePath().isEmpty()) + error = Tr::tr("No project file given and none found in current directory.\n"); + else + error = Tr::tr("No project file found in directory '%1'.").arg(projectFilePath); + throw ErrorInfo(error); + } + if (actualFileNames.count() > 1) { + throw ErrorInfo(Tr::tr("More than one project file found in directory '%1'.") + .arg(projectFilePath)); + } + projectFilePath.append(QLatin1Char('/')).append(actualFileNames.first()); + + projectFilePath = QDir::current().filePath(projectFilePath); + projectFilePath = QDir::cleanPath(projectFilePath); + + qbsDebug() << "Using project file '" << QDir::toNativeSeparators(projectFilePath) << "'."; +} + +void CommandLineParser::CommandLineParserPrivate::setupBuildDirectory() +{ + projectBuildDirectory = optionPool.buildDirectoryOption()->projectBuildDirectory(); +} + +void CommandLineParser::CommandLineParserPrivate::setupBuildOptions() +{ + buildOptions.setDryRun(dryRun()); + QStringList changedFiles = optionPool.changedFilesOption()->arguments(); + QDir currentDir; + for (int i = 0; i < changedFiles.count(); ++i) { + QString &file = changedFiles[i]; + file = QDir::fromNativeSeparators(currentDir.absoluteFilePath(file)); + } + buildOptions.setChangedFiles(changedFiles); + buildOptions.setKeepGoing(optionPool.keepGoingOption()->enabled()); + buildOptions.setForceTimestampCheck(optionPool.forceTimestampCheckOption()->enabled()); + buildOptions.setForceOutputCheck(optionPool.forceOutputCheckOption()->enabled()); + const JobsOption * jobsOption = optionPool.jobsOption(); + buildOptions.setMaxJobCount(jobsOption->jobCount()); + buildOptions.setLogElapsedTime(logTime); + buildOptions.setEchoMode(echoMode()); + buildOptions.setInstall(!optionPool.noInstallOption()->enabled()); + buildOptions.setRemoveExistingInstallation(optionPool.removeFirstoption()->enabled()); +} + +void CommandLineParser::CommandLineParserPrivate::setupProgress() +{ + const ShowProgressOption * const option = optionPool.showProgressOption(); + showProgress = option->enabled(); +#ifdef Q_OS_UNIX + if (showProgress && !isatty(STDOUT_FILENO)) { + showProgress = false; + qbsWarning() << Tr::tr("Ignoring option '%1', because standard output is " + "not connected to a terminal.").arg(option->longRepresentation()); + } +#endif +} + +void CommandLineParser::CommandLineParserPrivate::setupLogLevel() +{ + const LogLevelOption * const logLevelOption = optionPool.logLevelOption(); + const VerboseOption * const verboseOption = optionPool.verboseOption(); + const QuietOption * const quietOption = optionPool.quietOption(); + int logLevel = logLevelOption->logLevel(); + logLevel += verboseOption->count(); + logLevel -= quietOption->count(); + + if (showProgress && logLevel != LoggerMinLevel) { + const bool logLevelWasSetByUser + = logLevelOption->logLevel() != defaultLogLevel() + || verboseOption->count() > 0 || quietOption->count() > 0; + if (logLevelWasSetByUser) { + qbsInfo() << Tr::tr("Setting log level to '%1', because option '%2'" + " has been given.").arg(logLevelName(LoggerMinLevel), + optionPool.showProgressOption()->longRepresentation()); + } + logLevel = LoggerMinLevel; + } + if (logLevel < LoggerMinLevel) { + qbsWarning() << Tr::tr("Cannot decrease log level as much as specified; using '%1'.") + .arg(logLevelName(LoggerMinLevel)); + logLevel = LoggerMinLevel; + } else if (logLevel > LoggerMaxLevel) { + qbsWarning() << Tr::tr("Cannot increase log level as much as specified; using '%1'.") + .arg(logLevelName(LoggerMaxLevel)); + logLevel = LoggerMaxLevel; + } + + logTime = optionPool.logTimeOption()->enabled(); + if (showProgress && logTime) { + qbsWarning() << Tr::tr("Options '%1' and '%2' are incompatible. Ignoring '%2'.") + .arg(optionPool.showProgressOption()->longRepresentation(), + optionPool.logTimeOption()->longRepresentation()); + logTime = false; + } + + ConsoleLogger::instance().logSink()->setLogLevel(static_cast(logLevel)); +} + +QString CommandLineParser::CommandLineParserPrivate::propertyName(const QString &aCommandLineName) const +{ + // Make fully-qualified, ie "platform" -> "qbs.platform" + if (aCommandLineName.contains(QLatin1Char('.'))) + return aCommandLineName; + else + return QLatin1String("qbs.") + aCommandLineName; +} + +bool CommandLineParser::CommandLineParserPrivate::checkForExistingBuildConfiguration( + const QList &buildConfigs, const QString &configurationName) +{ + foreach (const QVariantMap &buildConfig, buildConfigs) { + if (configurationName == getBuildConfigurationName(buildConfig)) + return true; + } + return false; +} + +bool CommandLineParser::CommandLineParserPrivate::isSameProfile(const QString &profile1, + const QString &profile2) const +{ + if (profile1 == profile2) + return true; + Settings settings(settingsDir()); + if (profile1.isEmpty()) + return profile2.isEmpty() || profile2 == settings.defaultProfile(); + if (profile2.isEmpty()) + return profile1 == settings.defaultProfile(); + return false; +} + +bool CommandLineParser::CommandLineParserPrivate::withNonDefaultProducts() const +{ + if (command->type() == GenerateCommandType) + return true; + return optionPool.buildNonDefaultOption()->enabled(); +} + +bool CommandLineParser::CommandLineParserPrivate::dryRun() const +{ + if (command->type() == GenerateCommandType) + return true; + return optionPool.dryRunOption()->enabled(); +} + +CommandEchoMode CommandLineParser::CommandLineParserPrivate::echoMode() const +{ + if (command->type() == GenerateCommandType) + return CommandEchoModeSilent; + + if (optionPool.commandEchoModeOption()->commandEchoMode() < CommandEchoModeInvalid) + return optionPool.commandEchoModeOption()->commandEchoMode(); + + return defaultCommandEchoMode(); +} + +} // namespace qbs diff --git a/src/app/qbs/parser/commandlineparser.h b/src/app/qbs/parser/commandlineparser.h new file mode 100644 index 00000000..dd9c30f4 --- /dev/null +++ b/src/app/qbs/parser/commandlineparser.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_COMMANDLINEPARSER_H +#define QBS_COMMANDLINEPARSER_H + +#include "commandtype.h" + +#include +#include + +namespace qbs { +class BuildOptions; +class CleanOptions; +class GenerateOptions; +class InstallOptions; +class Settings; + +class CommandLineParser +{ + Q_DISABLE_COPY(CommandLineParser) +public: + CommandLineParser(); + ~CommandLineParser(); + + bool parseCommandLine(const QStringList &args); + void printHelp() const; + + CommandType command() const; + QString commandName() const; + QString commandDescription() const; + QString projectFilePath() const; + QString projectBuildDirectory() const; + BuildOptions buildOptions(const QString &profile) const; + CleanOptions cleanOptions(const QString &profile) const; + GenerateOptions generateOptions() const; + InstallOptions installOptions(const QString &profile) const; + bool force() const; + bool forceTimestampCheck() const; + bool forceOutputCheck() const; + bool dryRun() const; + bool forceProbesExecution() const; + bool waitLockBuildGraph() const; + bool logTime() const; + bool withNonDefaultProducts() const; + bool buildBeforeInstalling() const; + QStringList runArgs() const; + QStringList products() const; + QList buildConfigurations() const; + bool showProgress() const; + bool showVersion() const; + QString settingsDir() const; + +private: + class CommandLineParserPrivate; + CommandLineParserPrivate *d; +}; + +} // namespace qbs + +#endif // QBS_COMMANDLINEPARSER_H diff --git a/src/app/qbs/parser/commandpool.cpp b/src/app/qbs/parser/commandpool.cpp new file mode 100644 index 00000000..e0898047 --- /dev/null +++ b/src/app/qbs/parser/commandpool.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandpool.h" + +#include "command.h" + +namespace qbs { + +CommandPool::CommandPool(CommandLineOptionPool &optionPool) : m_optionPool(optionPool) +{ +} + +CommandPool::~CommandPool() +{ + qDeleteAll(m_commands); +} + +qbs::Command *CommandPool::getCommand(CommandType type) const +{ + Command *& command = m_commands[type]; + if (!command) { + switch (type) { + case ResolveCommandType: + command = new ResolveCommand(m_optionPool); + break; + case GenerateCommandType: + command = new GenerateCommand(m_optionPool); + break; + case BuildCommandType: + command = new BuildCommand(m_optionPool); + break; + case CleanCommandType: + command = new CleanCommand(m_optionPool); + break; + case RunCommandType: + command = new RunCommand(m_optionPool); + break; + case ShellCommandType: + command = new ShellCommand(m_optionPool); + break; + case StatusCommandType: + command = new StatusCommand(m_optionPool); + break; + case UpdateTimestampsCommandType: + command = new UpdateTimestampsCommand(m_optionPool); + break; + case InstallCommandType: + command = new InstallCommand(m_optionPool); + break; + case DumpNodesTreeCommandType: + command = new DumpNodesTreeCommand(m_optionPool); + break; + case HelpCommandType: + command = new HelpCommand(m_optionPool); + break; + } + } + return command; +} + +} // namespace qbs diff --git a/src/app/qbs/parser/commandpool.h b/src/app/qbs/parser/commandpool.h new file mode 100644 index 00000000..1184c9c2 --- /dev/null +++ b/src/app/qbs/parser/commandpool.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_COMMANDPOOL_H +#define QBS_COMMANDPOOL_H + +#include "commandtype.h" + +#include + +namespace qbs { +class Command; +class CommandLineOptionPool; + +class CommandPool +{ + Q_DISABLE_COPY(CommandPool) +public: + CommandPool(CommandLineOptionPool &optionPool); + ~CommandPool(); + + Command *getCommand(CommandType type) const; + +private: + CommandLineOptionPool &m_optionPool; + mutable QHash m_commands; +}; + +} // namespace qbs + +#endif // QBS_COMMANDPOOL_H diff --git a/src/app/qbs/parser/commandtype.h b/src/app/qbs/parser/commandtype.h new file mode 100644 index 00000000..0f704067 --- /dev/null +++ b/src/app/qbs/parser/commandtype.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef COMMANDTYPE_H +#define COMMANDTYPE_H + +namespace qbs { + +enum CommandType { + ResolveCommandType, BuildCommandType, CleanCommandType, RunCommandType, ShellCommandType, + StatusCommandType, UpdateTimestampsCommandType, DumpNodesTreeCommandType, + InstallCommandType, HelpCommandType, GenerateCommandType +}; + +} // namespace qbs + +#endif // COMMANDTYPE_H diff --git a/src/app/qbs/parser/parser.pri b/src/app/qbs/parser/parser.pri new file mode 100644 index 00000000..a45c7b3c --- /dev/null +++ b/src/app/qbs/parser/parser.pri @@ -0,0 +1,16 @@ +SOURCES += \ + $$PWD/commandlineparser.cpp \ + $$PWD/command.cpp \ + $$PWD/commandpool.cpp \ + $$PWD/commandlineoption.cpp \ + $$PWD/commandlineoptionpool.cpp + +HEADERS += \ + $$PWD/commandlineparser.h \ + $$PWD/command.h \ + $$PWD/commandpool.h \ + $$PWD/commandlineoption.h \ + $$PWD/commandlineoptionpool.h \ + $$PWD/commandtype.h + +include(../../../../qbs_version.pri) diff --git a/src/app/qbs/qbs.pro b/src/app/qbs/qbs.pro new file mode 100644 index 00000000..f2c3023b --- /dev/null +++ b/src/app/qbs/qbs.pro @@ -0,0 +1,28 @@ +include(../app.pri) +include(parser/parser.pri) + +TARGET = qbs + +SOURCES += main.cpp \ + ctrlchandler.cpp \ + application.cpp \ + status.cpp \ + consoleprogressobserver.cpp \ + commandlinefrontend.cpp \ + qbstool.cpp + +HEADERS += \ + ctrlchandler.h \ + application.h \ + status.h \ + consoleprogressobserver.h \ + commandlinefrontend.h \ + qbstool.h + +include(../../library_dirname.pri) +isEmpty(QBS_RELATIVE_LIBEXEC_PATH):QBS_RELATIVE_LIBEXEC_PATH=../libexec/qbs +isEmpty(QBS_RELATIVE_PLUGINS_PATH):QBS_RELATIVE_PLUGINS_PATH=../$${QBS_LIBRARY_DIRNAME} +isEmpty(QBS_RELATIVE_SEARCH_PATH):QBS_RELATIVE_SEARCH_PATH=.. +DEFINES += QBS_RELATIVE_LIBEXEC_PATH=\\\"$${QBS_RELATIVE_LIBEXEC_PATH}\\\" +DEFINES += QBS_RELATIVE_PLUGINS_PATH=\\\"$${QBS_RELATIVE_PLUGINS_PATH}\\\" +DEFINES += QBS_RELATIVE_SEARCH_PATH=\\\"$${QBS_RELATIVE_SEARCH_PATH}\\\" diff --git a/src/app/qbs/qbs.qbs b/src/app/qbs/qbs.qbs new file mode 100644 index 00000000..0f88f1dd --- /dev/null +++ b/src/app/qbs/qbs.qbs @@ -0,0 +1,47 @@ +import qbs 1.0 +import QbsFunctions + +QbsApp { + name: "qbs_app" + Depends { name: "qbs resources" } + targetName: "qbs" + cpp.defines: base.concat([ + 'QBS_VERSION="' + QbsFunctions.qbsVersion() + '"', + 'QBS_RELATIVE_LIBEXEC_PATH="' + qbsbuildconfig.relativeLibexecPath + '"', + 'QBS_RELATIVE_SEARCH_PATH="' + qbsbuildconfig.relativeSearchPath + '"', + 'QBS_RELATIVE_PLUGINS_PATH="' + qbsbuildconfig.relativePluginsPath + '"', + ]) + files: [ + "application.cpp", + "application.h", + "commandlinefrontend.cpp", + "commandlinefrontend.h", + "consoleprogressobserver.cpp", + "consoleprogressobserver.h", + "ctrlchandler.cpp", + "ctrlchandler.h", + "main.cpp", + "qbstool.cpp", + "qbstool.h", + "status.cpp", + "status.h", + ] + Group { + name: "parser" + prefix: name + '/' + files: [ + "command.cpp", + "command.h", + "commandlineoption.cpp", + "commandlineoption.h", + "commandlineoptionpool.cpp", + "commandlineoptionpool.h", + "commandlineparser.cpp", + "commandlineparser.h", + "commandpool.cpp", + "commandpool.h", + "commandtype.h", + ] + } +} + diff --git a/src/app/qbs/qbstool.cpp b/src/app/qbs/qbstool.cpp new file mode 100644 index 00000000..b3efc965 --- /dev/null +++ b/src/app/qbs/qbstool.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qbstool.h" + +#include + +#include +#include +#include +#include + +#include + +static QString toolPrefix() { return QLatin1String("qbs-"); } +static QString qbsBinDir() { return QCoreApplication::applicationDirPath(); } + +static QString qbsToolFilePath(const QString &toolName) +{ + return qbsBinDir() + QLatin1Char('/') + toolPrefix() + + qbs::Internal::HostOsInfo::appendExecutableSuffix(toolName); +} + +void QbsTool::runTool(const QString &toolName, const QStringList &arguments) +{ + m_failedToStart = false; + m_exitCode = -1; + const QString filePath = qbsToolFilePath(toolName); + const QFileInfo fi(filePath); + if (!fi.exists() || !fi.isFile() || !fi.isExecutable()) { + m_failedToStart = true; + return; + } + QProcess toolProc; + toolProc.start(filePath, arguments); + if (!toolProc.waitForStarted()) + m_failedToStart = true; + toolProc.waitForFinished(-1); + m_exitCode = toolProc.exitCode(); + m_stdout = QString::fromLocal8Bit(toolProc.readAllStandardOutput()); + m_stderr = QString::fromLocal8Bit(toolProc.readAllStandardError()); +} + +bool QbsTool::tryToRunTool(const QString &toolName, const QStringList &arguments, int *exitCode) +{ + QbsTool tool; + tool.runTool(toolName, arguments); + if (exitCode) + *exitCode = tool.exitCode(); + if (tool.failedToStart()) + return false; + std::cout << qPrintable(tool.stdOut()); + std::cerr << qPrintable(tool.stdErr()); + return true; +} + +QStringList QbsTool::allToolNames() +{ + const QString suffix = QLatin1String(QBS_HOST_EXE_SUFFIX); + QStringList toolFileNames = QDir(qbsBinDir()).entryList(QStringList(toolPrefix() + + QString::fromLatin1("*%1").arg(suffix)), QDir::Files, QDir::Name); + QStringList toolNames; + const int prefixLength = toolPrefix().count(); + foreach (const QString &toolFileName, toolFileNames) { + toolNames << toolFileName.mid(prefixLength, + toolFileName.count() - prefixLength - suffix.count()); + } + return toolNames; +} diff --git a/src/app/qbs/qbstool.h b/src/app/qbs/qbstool.h new file mode 100644 index 00000000..1cbe08a1 --- /dev/null +++ b/src/app/qbs/qbstool.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_QBSTOOL_H +#define QBS_QBSTOOL_H + +#include + +class QbsTool +{ +public: + void runTool(const QString &toolName, const QStringList &arguments); + + bool failedToStart() const { return m_failedToStart; } + int exitCode() const { return m_exitCode; } + QString stdOut() const { return m_stdout; } + QString stdErr() const { return m_stderr; } + + static QStringList allToolNames(); + static bool tryToRunTool(const QString &toolName, const QStringList &arguments, + int *exitCode = 0); + +private: + bool m_failedToStart; + int m_exitCode; + QString m_stdout; + QString m_stderr; +}; + +#endif // QBS_QBSTOOL_H diff --git a/src/app/qbs/status.cpp b/src/app/qbs/status.cpp new file mode 100644 index 00000000..9ef2f5da --- /dev/null +++ b/src/app/qbs/status.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "status.h" + +#include "../shared/logging/consolelogger.h" + +#include + +#include +#include +#include +#include +#include + +namespace qbs { + +static QList createIgnoreList(const QString &projectRootPath) +{ + QList ignoreRegularExpressionList; + ignoreRegularExpressionList.append(QRegExp(projectRootPath + QLatin1String("/build.*"))); + ignoreRegularExpressionList.append(QRegExp(QLatin1String("*.qbs"), + Qt::CaseSensitive, QRegExp::Wildcard)); + ignoreRegularExpressionList.append(QRegExp(QLatin1String("*.pro"), + Qt::CaseSensitive, QRegExp::Wildcard)); + ignoreRegularExpressionList.append(QRegExp(QLatin1String("*Makefile"), + Qt::CaseSensitive, QRegExp::Wildcard)); + ignoreRegularExpressionList.append(QRegExp(QLatin1String("*.so*"), + Qt::CaseSensitive, QRegExp::Wildcard)); + ignoreRegularExpressionList.append(QRegExp(QLatin1String("*.o"), + Qt::CaseSensitive, QRegExp::Wildcard)); + QString ignoreFilePath = projectRootPath + QLatin1String("/.qbsignore"); + + QFile ignoreFile(ignoreFilePath); + + if (ignoreFile.open(QFile::ReadOnly)) { + QList ignoreTokenList = ignoreFile.readAll().split('\n'); + foreach (const QByteArray &btoken, ignoreTokenList) { + const QString token = QString::fromLatin1(btoken); + if (token.left(1) == QLatin1String("/")) + ignoreRegularExpressionList.append(QRegExp(projectRootPath + + token + QLatin1String(".*"), + Qt::CaseSensitive, QRegExp::RegExp2)); + else if (!token.isEmpty()) + ignoreRegularExpressionList.append(QRegExp(token, Qt::CaseSensitive, QRegExp::RegExp2)); + + } + } + + return ignoreRegularExpressionList; +} + +static QStringList allFilesInDirectoryRecursive(const QDir &rootDirecory, const QList &ignoreRegularExpressionList) +{ + QStringList fileList; + + foreach (const QFileInfo &fileInfo, rootDirecory.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name)) { + QString absoluteFilePath = fileInfo.absoluteFilePath(); + bool inIgnoreList = false; + foreach (const QRegExp &ignoreRegularExpression, ignoreRegularExpressionList) { + if (ignoreRegularExpression.exactMatch(absoluteFilePath)) { + inIgnoreList = true; + break; + } + } + + if (!inIgnoreList) { + if (fileInfo.isFile()) + fileList.append(absoluteFilePath); + else if (fileInfo.isDir()) + fileList.append(allFilesInDirectoryRecursive(QDir(absoluteFilePath), ignoreRegularExpressionList)); + + } + } + + return fileList; +} + +static QStringList allFilesInProject(const QString &projectRootPath) +{ + QList ignoreRegularExpressionList = createIgnoreList(projectRootPath); + + return allFilesInDirectoryRecursive(QDir(projectRootPath), ignoreRegularExpressionList); +} + +QStringList allFiles(const ProductData &product) +{ + QStringList files; + foreach (const GroupData &group, product.groups()) + files += group.allFilePaths(); + return files; +} + +int printStatus(const ProjectData &project) +{ + const QString projectFilePath = project.location().filePath(); + QString projectDirectory = QFileInfo(projectFilePath).dir().path(); + int projectDirectoryPathLength = projectDirectory.length(); + + QStringList untrackedFilesInProject = allFilesInProject(projectDirectory); + QStringList missingFiles; + foreach (const ProductData &product, project.allProducts()) { + qbsInfo() << "\nProduct: " << product.name() + << " (" << product.location().filePath() << ":" + << product.location().line() << ")"; + foreach (const GroupData &group, product.groups()) { + qbsInfo() << " Group: " << group.name() + << " (" << group.location().filePath() << ":" + << group.location().line() << ")"; + QStringList sourceFiles = group.allFilePaths(); + qSort(sourceFiles); + foreach (const QString &sourceFile, sourceFiles) { + if (!QFileInfo(sourceFile).exists()) + missingFiles.append(sourceFile); + qbsInfo() << " " << sourceFile.mid(projectDirectoryPathLength + 1); + untrackedFilesInProject.removeOne(sourceFile); + } + } + } + + qbsInfo() << "\nMissing files:"; + foreach (const QString &untrackedFile, missingFiles) + qbsInfo() << untrackedFile.mid(projectDirectoryPathLength + 1); + + qbsInfo() << "\nUntracked files:"; + foreach (const QString &missingFile, untrackedFilesInProject) + qbsInfo() << missingFile.mid(projectDirectoryPathLength + 1); + + return 0; +} + +} // namespace qbs diff --git a/src/app/qbs/status.h b/src/app/qbs/status.h new file mode 100644 index 00000000..301363d7 --- /dev/null +++ b/src/app/qbs/status.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef STATUS_H +#define STATUS_H + +namespace qbs { +class ProjectData; + +int printStatus(const ProjectData &project); + +} // namespace qbs + +#endif // STATUS_H diff --git a/src/app/shared/logging/coloredoutput.cpp b/src/app/shared/logging/coloredoutput.cpp new file mode 100644 index 00000000..59743d56 --- /dev/null +++ b/src/app/shared/logging/coloredoutput.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "coloredoutput.h" +#include +#ifdef Q_OS_WIN32 +# include +#endif + +#include + +#ifdef Q_OS_UNIX +#include +#endif + +void printfColored(TextColor color, const char *str, va_list vl) +{ + fprintfColored(color, stdout, str, vl); +} + +void printfColored(TextColor color, const char *str, ...) +{ + va_list vl; + va_start(vl, str); + printfColored(color, str, vl); + va_end(vl); +} + +void fprintfColored(TextColor color, FILE *file, const char *str, va_list vl) +{ +#if defined(Q_OS_UNIX) + if (color != TextColorDefault && isatty(fileno(file))) { + unsigned char bright = (color & TextColorBright) >> 3; + fprintf(file, "\033[%d;%dm", bright, 30 + (color & ~TextColorBright)); + vfprintf(file, str, vl); + fprintf(stdout, "\033[0m"); + fprintf(stderr, "\033[0m"); + } else +#elif defined(Q_OS_WIN32) + HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + if (color != TextColorDefault + && hStdout != INVALID_HANDLE_VALUE + && GetConsoleScreenBufferInfo(hStdout, &csbiInfo)) + { + WORD bgrColor = ((color & 1) << 2) | (color & 2) | ((color & 4) >> 2); // BGR instead of RGB. + if (color & TextColorBright) + bgrColor += FOREGROUND_INTENSITY; + SetConsoleTextAttribute(hStdout, (csbiInfo.wAttributes & 0xf0) | bgrColor); + vfprintf(file, str, vl); + SetConsoleTextAttribute(hStdout, csbiInfo.wAttributes); + } else +#endif + { + vfprintf(file, str, vl); + } +} + +void fprintfColored(TextColor color, FILE *file, const char *str, ...) +{ + va_list vl; + va_start(vl, str); + fprintfColored(color, file, str, vl); + va_end(vl); +} diff --git a/src/app/shared/logging/coloredoutput.h b/src/app/shared/logging/coloredoutput.h new file mode 100644 index 00000000..182d96bb --- /dev/null +++ b/src/app/shared/logging/coloredoutput.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_COLOREDOUTPUT_H +#define QBS_COLOREDOUTPUT_H + +#include +#include + +// http://en.wikipedia.org/wiki/ANSI_escape_code#Colors +enum TextColor { + TextColorDefault = -1, + TextColorBlack = 0, + TextColorDarkRed = 1, + TextColorDarkGreen = 2, + TextColorDarkBlue = 4, + TextColorDarkCyan = TextColorDarkGreen | TextColorDarkBlue, + TextColorDarkMagenta = TextColorDarkRed | TextColorDarkBlue, + TextColorDarkYellow = TextColorDarkRed | TextColorDarkGreen, + TextColorGray = 7, + TextColorBright = 8, + TextColorRed = TextColorDarkRed | TextColorBright, + TextColorGreen = TextColorDarkGreen | TextColorBright, + TextColorBlue = TextColorDarkBlue | TextColorBright, + TextColorCyan = TextColorDarkCyan | TextColorBright, + TextColorMagenta = TextColorDarkMagenta | TextColorBright, + TextColorYellow = TextColorDarkYellow | TextColorBright, + TextColorWhite = TextColorGray | TextColorBright +}; + +void printfColored(TextColor color, const char *str, va_list vl); +void printfColored(TextColor color, const char *str, ...); +void fprintfColored(TextColor color, FILE *file, const char *str, va_list vl); +void fprintfColored(TextColor color, FILE *file, const char *str, ...); + +#endif // QBS_COLOREDOUTPUT_H diff --git a/src/app/shared/logging/consolelogger.cpp b/src/app/shared/logging/consolelogger.cpp new file mode 100644 index 00000000..4093f89c --- /dev/null +++ b/src/app/shared/logging/consolelogger.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "consolelogger.h" + +#include +#include + +#include + +static QHash setupColorTable() +{ + QHash colorTable; + colorTable[QLatin1String("compiler")] = TextColorDefault; + colorTable[QLatin1String("linker")] = TextColorDarkGreen; + colorTable[QLatin1String("codegen")] = TextColorDarkYellow; + colorTable[QLatin1String("filegen")] = TextColorDarkYellow; + return colorTable; +} + +ConsoleLogSink::ConsoleLogSink() : m_coloredOutputEnabled(true), m_enabled(true) +{ +} + +void ConsoleLogSink::doPrintMessage(qbs::LoggerLevel level, const QString &message, + const QString &tag) +{ + if (!m_enabled) + return; + + FILE * const file = level == qbs::LoggerInfo ? stdout : stderr; + + const QString levelTag = logLevelTag(level); + TextColor color = TextColorDefault; + switch (level) { + case qbs::LoggerError: + color = TextColorRed; + break; + case qbs::LoggerWarning: + color = TextColorYellow; + break; + default: + break; + } + + fprintfWrapper(color, file, levelTag.toLocal8Bit().constData()); + static QHash colorTable = setupColorTable(); + fprintfWrapper(colorTable.value(tag, TextColorDefault), file, "%s\n", + message.toLocal8Bit().constData()); + fflush(file); +} + +void ConsoleLogSink::fprintfWrapper(TextColor color, FILE *file, const char *str, ...) +{ + va_list vl; + va_start(vl, str); + if (m_coloredOutputEnabled) + fprintfColored(color, file, str, vl); + else + vfprintf(file, str, vl); + va_end(vl); +} + + +ConsoleLogger &ConsoleLogger::instance(qbs::Settings *settings) +{ + static ConsoleLogger logger(settings); + return logger; +} + +void ConsoleLogger::setSettings(qbs::Settings *settings) +{ + if (settings) + m_logSink.setColoredOutputEnabled(qbs::Preferences(settings).useColoredOutput()); +} + +ConsoleLogger::ConsoleLogger(qbs::Settings *settings) : Logger(&m_logSink) +{ + setSettings(settings); +} diff --git a/src/app/shared/logging/consolelogger.h b/src/app/shared/logging/consolelogger.h new file mode 100644 index 00000000..d362cfc2 --- /dev/null +++ b/src/app/shared/logging/consolelogger.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_LOGSINK_H +#define QBS_LOGSINK_H + +#include "coloredoutput.h" + +#include + +namespace qbs { class Settings; } + +class ConsoleLogSink : public qbs::ILogSink +{ +public: + ConsoleLogSink(); + + void setColoredOutputEnabled(bool enabled) { m_coloredOutputEnabled = enabled; } + void setEnabled(bool enabled) { m_enabled = enabled; } + +private: + void doPrintMessage(qbs::LoggerLevel level, const QString &message, const QString &tag); + void fprintfWrapper(TextColor color, FILE *file, const char *str, ...); + +private: + bool m_coloredOutputEnabled; + bool m_enabled; +}; + + +class ConsoleLogger : public qbs::Internal::Logger +{ +public: + static ConsoleLogger &instance(qbs::Settings *settings = 0); + ConsoleLogSink *logSink() { return &m_logSink; } + void setSettings(qbs::Settings *settings); + +private: + ConsoleLogger(qbs::Settings *settings); + + ConsoleLogSink m_logSink; +}; + +inline qbs::Internal::LogWriter qbsError() { + return ConsoleLogger::instance().qbsLog(qbs::LoggerError); +} +inline qbs::Internal::LogWriter qbsWarning() { return ConsoleLogger::instance().qbsWarning(); } +inline qbs::Internal::LogWriter qbsInfo() { return ConsoleLogger::instance().qbsInfo(); } +inline qbs::Internal::LogWriter qbsDebug() { return ConsoleLogger::instance().qbsDebug(); } +inline qbs::Internal::LogWriter qbsTrace() { return ConsoleLogger::instance().qbsTrace(); } + +#endif // QBS_LOGSINK_H diff --git a/src/app/shared/logging/logging.pri b/src/app/shared/logging/logging.pri new file mode 100644 index 00000000..e24f33e1 --- /dev/null +++ b/src/app/shared/logging/logging.pri @@ -0,0 +1,2 @@ +HEADERS += $$PWD/consolelogger.h $$PWD/coloredoutput.h +SOURCES += $$PWD/consolelogger.cpp $$PWD/coloredoutput.cpp diff --git a/src/install_prefix.pri b/src/install_prefix.pri new file mode 100644 index 00000000..34aef1d5 --- /dev/null +++ b/src/install_prefix.pri @@ -0,0 +1 @@ +unix: isEmpty(QBS_INSTALL_PREFIX): QBS_INSTALL_PREFIX = /usr/local diff --git a/src/lib/corelib/api/api.pri b/src/lib/corelib/api/api.pri new file mode 100644 index 00000000..e2f68f80 --- /dev/null +++ b/src/lib/corelib/api/api.pri @@ -0,0 +1,48 @@ +include(../../../install_prefix.pri) + +HEADERS += \ + $$PWD/internaljobs.h \ + $$PWD/projectdata.h \ + $$PWD/runenvironment.h \ + $$PWD/jobs.h \ + $$PWD/languageinfo.h \ + $$PWD/project.h \ + $$PWD/project_p.h \ + $$PWD/propertymap_p.h \ + $$PWD/projectdata_p.h \ + $$PWD/rulecommand.h \ + $$PWD/rulecommand_p.h + +SOURCES += \ + $$PWD/internaljobs.cpp \ + $$PWD/runenvironment.cpp \ + $$PWD/projectdata.cpp \ + $$PWD/jobs.cpp \ + $$PWD/languageinfo.cpp \ + $$PWD/project.cpp \ + $$PWD/rulecommand.cpp + +!qbs_no_dev_install { + api_headers.files = \ + $$PWD/jobs.h \ + $$PWD/languageinfo.h \ + $$PWD/project.h \ + $$PWD/projectdata.h \ + $$PWD/rulecommand.h \ + $$PWD/runenvironment.h + api_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/api + INSTALLS += api_headers +} + +qbs_enable_project_file_updates { + HEADERS += \ + $$PWD/changeset.h \ + $$PWD/projectfileupdater.h \ + $$PWD/qmljsrewriter.h + + SOURCES += \ + $$PWD/changeset.cpp \ + $$PWD/projectfileupdater.cpp \ + $$PWD/qmljsrewriter.cpp + DEFINES += QBS_ENABLE_PROJECT_FILE_UPDATES +} diff --git a/src/lib/corelib/api/changeset.cpp b/src/lib/corelib/api/changeset.cpp new file mode 100644 index 00000000..fddebc62 --- /dev/null +++ b/src/lib/corelib/api/changeset.cpp @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "changeset.h" + +#include + +namespace QbsQmlJS { + +ChangeSet::ChangeSet() + : m_string(0), m_cursor(0), m_error(false) +{ +} + +ChangeSet::ChangeSet(const QList &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 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::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 *replaceList) +{ + Q_ASSERT(replace_helper.type == EditOp::Replace); + + { + QMutableListIterator 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 *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 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 00000000..6a0593cb --- /dev/null +++ b/src/lib/corelib/api/changeset.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_CHANGESET_H +#define QBS_CHANGESET_H + +#include +#include + +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 &operations); + + bool isEmpty() const; + + QList 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 *replaceList); + void convertToReplace(const EditOp &op, QList *replaceList); + + void apply_helper(); + +private: + QString *m_string; + QTextCursor *m_cursor; + + QList 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 00000000..339a7fa0 --- /dev/null +++ b/src/lib/corelib/api/internaljobs.cpp @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "internaljobs.h" + +#include "jobs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +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); + if (m_job->timed()) + m_timedLogger = new TimedActivityLogger(m_job->logger(), task, true); + 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 TopLevelProjectPtr &project) +{ + try { + doSanityChecks(project, logger()); + 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, &InternalJob::finished, this, &InternalJobThreadWrapper::handleFinished); + connect(m_job, &InternalJob::newTaskStarted, + this, &InternalJob::newTaskStarted); + connect(m_job, &InternalJob::taskProgress, + this, &InternalJob::taskProgress); + connect(m_job, &InternalJob::totalEffortChanged, + this, &InternalJob::totalEffortChanged); + connect(this, &InternalJobThreadWrapper::startRequested, m_job, &InternalJob::start); +} + +InternalJobThreadWrapper::~InternalJobThreadWrapper() +{ + if (m_running) { + QEventLoop loop; + connect(m_job, &InternalJob::finished, &loop, &QEventLoop::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 TopLevelProjectPtr &existingProject, + const SetupProjectParameters ¶meters) +{ + m_existingProject = existingProject; + m_parameters = parameters; + setTimed(parameters.logElapsedTime()); +} + +void InternalSetupProjectJob::reportError(const ErrorInfo &error) +{ + setError(error); + emit finished(this); +} + +TopLevelProjectPtr InternalSetupProjectJob::project() const +{ + return m_newProject; +} + +void InternalSetupProjectJob::start() +{ + const TopLevelProjectConstPtr existingProject = m_existingProject; + BuildGraphLocker *bgLocker = m_existingProject ? m_existingProject->bgLocker : 0; + try { + const ErrorInfo err = m_parameters.expandBuildConfiguration(); + if (err.hasError()) + throw err; + const QString projectId = TopLevelProject::deriveId( + m_parameters.finalBuildConfigurationTree()); + const QString buildDir + = TopLevelProject::deriveBuildDirectory(m_parameters.buildRoot(), projectId); + if (m_existingProject && m_existingProject->buildDirectory != buildDir) + m_existingProject.clear(); + if (!m_existingProject) { + bgLocker = new BuildGraphLocker(ProjectBuildData::deriveBuildGraphFilePath(buildDir, + projectId), + logger(), m_parameters.waitLockBuildGraph(), observer()); + } + execute(); + if (m_existingProject) + m_existingProject->bgLocker = 0; + m_newProject->bgLocker = bgLocker; + } catch (const ErrorInfo &error) { + m_newProject.clear(); + setError(error); + + // Delete the build graph locker if and only if we allocated it here. + if (!existingProject) + delete bgLocker; + } + 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); + break; + case SetupProjectParameters::RestoreOnly: + m_newProject = restoreProject(evalContext).loadedProject; + break; + case SetupProjectParameters::RestoreAndTrackChanges: { + const BuildGraphLoadResult loadResult = restoreProject(evalContext); + m_newProject = loadResult.newlyResolvedProject; + if (!m_newProject) + m_newProject = loadResult.loadedProject; + if (!m_newProject) { + resolveProjectFromScratch(evalContext->engine()); + resolveBuildDataFromScratch(evalContext); + } else { + QBS_CHECK(m_newProject->buildData); + } + break; + } + } + + if (!m_parameters.dryRun()) + storeBuildGraph(m_newProject); + + // The evalutation context cannot be re-used for building, which runs in a different thread. + m_newProject->buildData->evaluationContext.clear(); +} + +void InternalSetupProjectJob::resolveProjectFromScratch(ScriptEngine *engine) +{ + Loader loader(engine, logger()); + loader.setSearchPaths(m_parameters.searchPaths()); + loader.setProgressObserver(observer()); + m_newProject = loader.loadProject(m_parameters); + QBS_CHECK(m_newProject); +} + +void InternalSetupProjectJob::resolveBuildDataFromScratch(const RulesEvaluationContextPtr &evalContext) +{ + TimedActivityLogger resolveLogger(logger(), QLatin1String("Resolving build project"), timed()); + BuildDataResolver(logger()).resolveBuildData(m_newProject, evalContext); +} + +BuildGraphLoadResult InternalSetupProjectJob::restoreProject(const RulesEvaluationContextPtr &evalContext) +{ + BuildGraphLoader bgLoader(m_parameters.adjustedEnvironment(), logger()); + const BuildGraphLoadResult loadResult + = bgLoader.load(m_existingProject, 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 &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 &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, &Executor::reportCommandDescription, + this, &BuildGraphTouchingJob::reportCommandDescription); + connect(m_executor, &Executor::reportProcessResult, + this, &BuildGraphTouchingJob::reportProcessResult); + + connect(executorThread, &QThread::started, m_executor, &Executor::build); + connect(m_executor, &Executor::finished, this, &InternalBuildJob::handleFinished); + connect(m_executor, &QObject::destroyed, executorThread, &QThread::quit); + connect(executorThread, &QThread::finished, this, &InternalBuildJob::emitFinished); + executorThread->start(); +} + +void InternalBuildJob::handleFinished() +{ + setError(m_executor->error()); + project()->buildData->evaluationContext.clear(); + storeBuildGraph(); + m_executor->deleteLater(); +} + +void InternalBuildJob::emitFinished() +{ + emit finished(this); +} + +InternalCleanJob::InternalCleanJob(const Logger &logger, QObject *parent) + : BuildGraphTouchingJob(logger, parent) +{ +} + +void InternalCleanJob::init(const TopLevelProjectPtr &project, + const QList &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(); + emit finished(this); +} + + +InternalInstallJob::InternalInstallJob(const Logger &logger) + : InternalJob(logger) +{ +} + +InternalInstallJob::~InternalInstallJob() +{ +} + +void InternalInstallJob::init(const TopLevelProjectPtr &project, + const QList &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); + } + 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 00000000..8553719e --- /dev/null +++ b/src/lib/corelib/api/internaljobs.h @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_INTERNALJOBS_H +#define QBS_INTERNALJOBS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace qbs { +class ProcessResult; +class Settings; + +namespace Internal { +class BuildGraphLoadResult; +class BuildGraphLocker; +class Executor; +class JobObserver; +class ScriptEngine; + +class InternalJob : public QObject +{ + Q_OBJECT + friend class JobObserver; +public: + ~InternalJob(); + + void cancel(); + virtual void start() {} + 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 TopLevelProjectPtr &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() override; + InternalJob *synchronousJob() const { return m_job; } + +signals: + void startRequested(); + +private: + void handleFinished(); + + QThread m_thread; + InternalJob *m_job; + bool m_running; +}; + +class InternalSetupProjectJob : public InternalJob +{ + Q_OBJECT +public: + InternalSetupProjectJob(const Logger &logger); + ~InternalSetupProjectJob(); + + void init(const TopLevelProjectPtr &existingProject, const SetupProjectParameters ¶meters); + void reportError(const ErrorInfo &error); + + TopLevelProjectPtr project() const; + +private: + void start() override; + void resolveProjectFromScratch(Internal::ScriptEngine *engine); + void resolveBuildDataFromScratch(const RulesEvaluationContextPtr &evalContext); + BuildGraphLoadResult restoreProject(const RulesEvaluationContextPtr &evalContext); + void execute(); + + TopLevelProjectPtr m_existingProject; + TopLevelProjectPtr m_newProject; + SetupProjectParameters m_parameters; +}; + + +class BuildGraphTouchingJob : public InternalJob +{ + Q_OBJECT +public: + const QList &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 &products, + bool dryRun); + void storeBuildGraph(); + +private: + TopLevelProjectPtr m_project; + QList 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 &products, + const BuildOptions &buildOptions); + +private: + void handleFinished(); + void emitFinished(); + + Executor *m_executor; +}; + + +class InternalCleanJob : public BuildGraphTouchingJob +{ + Q_OBJECT +public: + InternalCleanJob(const Logger &logger, QObject *parent = 0); + + void init(const TopLevelProjectPtr &project, const QList &products, + const CleanOptions &options); + +private: + void start() override; + + CleanOptions m_options; +}; + + +class InternalInstallJob : public InternalJob +{ + Q_OBJECT +public: + InternalInstallJob(const Logger &logger); + ~InternalInstallJob(); + + void init(const TopLevelProjectPtr &project, const QList &products, + const InstallOptions &options); + +private: + void start() override; + + TopLevelProjectPtr m_project; + QList 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 00000000..1533c4c2 --- /dev/null +++ b/src/lib/corelib/api/jobs.cpp @@ -0,0 +1,360 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "jobs.h" + +#include "internaljobs.h" +#include "project_p.h" +#include +#include + +#include + +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, &InternalJob::newTaskStarted, + this, &AbstractJob::handleTaskStarted, Qt::QueuedConnection); + connect(m_internalJob, &InternalJob::totalEffortChanged, + this, &AbstractJob::handleTotalEffortChanged); + connect(m_internalJob, &InternalJob::taskProgress, + this, &AbstractJob::handleTaskProgress, Qt::QueuedConnection); + connect(m_internalJob, &InternalJob::finished, this, &AbstractJob::handleFinished); + m_state = StateRunning; +} + +bool AbstractJob::lockProject(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 progress.")); + QTimer::singleShot(0, this, [this] { emit finished(false, this); }); + return false; + } + project->locked = true; + m_project = project; + return true; +} + +void AbstractJob::unlockProject() +{ + if (!m_project) + return; + QBS_ASSERT(m_project->locked, return); + m_project->locked = false; +} + +/*! + * \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); + finish(); + m_state = StateFinished; + unlockProject(); + 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(internalJob()); + const InternalSetupProjectJob * const job + = qobject_cast(wrapper->synchronousJob()); + return Project(job->project(), job->logger()); +} + +void SetupProjectJob::resolve(const Project &existingProject, + const SetupProjectParameters ¶meters) +{ + m_existingProject = existingProject; + const TopLevelProjectPtr &existingInternalProject + = existingProject.d ? existingProject.d->internalProject : TopLevelProjectPtr(); + if (existingInternalProject && !lockProject(existingInternalProject)) + return; + InternalJobThreadWrapper * const wrapper + = qobject_cast(internalJob()); + InternalSetupProjectJob * const job + = qobject_cast(wrapper->synchronousJob()); + job->init(existingInternalProject, parameters); + wrapper->start(); +} + +void SetupProjectJob::reportError(const ErrorInfo &error) +{ + InternalJobThreadWrapper * const wrapper + = qobject_cast(internalJob()); + InternalSetupProjectJob * const job + = qobject_cast(wrapper->synchronousJob()); + job->reportError(error); +} + +void SetupProjectJob::finish() +{ + // If the new project was successfully created, invalidate the existing one. + // The invariant is that there must always be at most one valid Project object + // for the same build directory, so that exclusive ownership of the build graph lock + // is ensured. + // We also need to invalidate the project if an error has occurred after the build data was + // already transferred. + if (m_existingProject.isValid() + && (!error().hasError() || !m_existingProject.d->internalProject->buildData)) { + m_existingProject.d->internalProject.clear(); + } +} + +/*! + * \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(internalJob()); + connect(job, &BuildGraphTouchingJob::reportCommandDescription, + this, &BuildJob::reportCommandDescription); + connect(job, &BuildGraphTouchingJob::reportProcessResult, + this, &BuildJob::reportProcessResult); +} + +void BuildJob::build(const TopLevelProjectPtr &project, const QList &products, + const BuildOptions &options) +{ + if (!lockProject(project)) + return; + qobject_cast(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 &products, + const qbs::CleanOptions &options) +{ + if (!lockProject(project)) + return; + InternalJobThreadWrapper * wrapper = qobject_cast(internalJob()); + qobject_cast(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 &products, const InstallOptions &options) +{ + if (!lockProject(project)) + return; + InternalJobThreadWrapper *wrapper = qobject_cast(internalJob()); + InternalInstallJob *installJob = qobject_cast(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 00000000..c293094b --- /dev/null +++ b/src/lib/corelib/api/jobs.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_JOBS_H +#define QBS_JOBS_H + +#include "project.h" +#include "../language/forward_decls.h" +#include "../tools/error.h" +#include "../tools/qbs_export.h" + +#include +#include +#include + +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; + +public slots: + void cancel(); + +protected: + AbstractJob(Internal::InternalJob *internalJob, QObject *parent); + Internal::InternalJob *internalJob() const { return m_internalJob; } + + bool lockProject(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: + void handleTaskStarted(const QString &description, int maximumProgressValue); + void handleTotalEffortChanged(int totalEffort); + void handleTaskProgress(int newProgressValue); + void handleFinished(); + + void unlockProject(); + virtual void finish() { } + + Internal::InternalJob * const m_internalJob; + Internal::TopLevelProjectPtr m_project; + 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 Project &existingProject, const SetupProjectParameters ¶meters); + void reportError(const ErrorInfo &error); + + void finish(); + + Project m_existingProject; +}; + +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 &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 &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 &products, const InstallOptions &options); +}; + +} // namespace qbs + +#endif // QBS_JOBS_H diff --git a/src/lib/corelib/api/languageinfo.cpp b/src/lib/corelib/api/languageinfo.cpp new file mode 100644 index 00000000..2b330c18 --- /dev/null +++ b/src/lib/corelib/api/languageinfo.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "languageinfo.h" + +#include +#include + +#include + +namespace qbs { + +LanguageInfo::LanguageInfo() +{ +} + +QByteArray LanguageInfo::qmlTypeInfo() +{ + const Internal::BuiltinDeclarations &builtins = Internal::BuiltinDeclarations::instance(); + + // 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: + auto typeNames = builtins.allTypeNames(); + typeNames.sort(); + foreach (const QString &typeName, typeNames) { + QByteArray utf8TypeName = typeName.toUtf8(); + result.append(" Component {\n"); + result.append(QByteArray(" name: \"") + utf8TypeName + QByteArray("\"\n")); + result.append(" exports: [ \"qbs/"); + result.append(utf8TypeName); + result.append(" "); + const auto v = builtins.languageVersion(); + result.append(QString::fromLatin1("%1.%2") + .arg(v.majorVersion()).arg(v.minorVersion()).toUtf8()); + result.append("\" ]\n"); + result.append(" prototype: \"QQuickItem\"\n"); + + Internal::ItemDeclaration itemDecl + = builtins.declarationsForType(builtins.typeForName(typeName)); + auto properties = itemDecl.properties(); + std::sort(std::begin(properties), std::end(properties), [] + (const Internal::PropertyDeclaration &a, const Internal::PropertyDeclaration &b) { + return a.name() < b.name(); + }); + foreach (const Internal::PropertyDeclaration &property, 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; + } + result.append(" }\n"); // Property + } + + result.append(" }\n"); // Component + } + + // Footer: + result.append("}\n"); // Module + return result; +} + +QString LanguageInfo::qbsVersion() +{ + return Internal::Version::qbsVersion().toString(); +} + +} // namespace qbs diff --git a/src/lib/corelib/api/languageinfo.h b/src/lib/corelib/api/languageinfo.h new file mode 100644 index 00000000..66f039b6 --- /dev/null +++ b/src/lib/corelib/api/languageinfo.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_LANGUAGEINFO_H +#define QBS_LANGUAGEINFO_H + +#include "../tools/qbs_export.h" + +#include + +namespace qbs { + +class QBS_EXPORT LanguageInfo +{ +public: + LanguageInfo(); + + QByteArray qmlTypeInfo(); + + static QString qbsVersion(); // TODO: Use QVersionNumber once we require Qt 5.6 +}; + +} // namespace qbs + +#endif // QBS_LANGUAGEINFO_H diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp new file mode 100644 index 00000000..aec1a80c --- /dev/null +++ b/src/lib/corelib/api/project.cpp @@ -0,0 +1,1208 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "project.h" +#include "project_p.h" + +#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES +#include "projectfileupdater.h" +#endif + +#include "internaljobs.h" +#include "jobs.h" +#include "projectdata_p.h" +#include "propertymap_p.h" +#include "rulecommand_p.h" +#include "runenvironment.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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("qbs::ErrorInfo"); + qRegisterMetaType("qbs::ProcessResult"); + qRegisterMetaType("Internal::InternalJob *"); + qRegisterMetaType("qbs::AbstractJob *"); + pluginsLoaded = true; +} + +ProjectData ProjectPrivate::projectData() +{ + m_projectData = ProjectData(); + retrieveProjectData(m_projectData, internalProject); + m_projectData.d->buildDir = internalProject->buildDirectory; + return m_projectData; +} + +static void addDependencies(QList &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 &products, + const BuildOptions &options, bool needsDepencencyResolving, + QObject *jobOwner) +{ + QList 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 &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 &products, + const InstallOptions &options, bool needsDepencencyResolving, QObject *jobOwner) +{ + QList productsToInstall = products; + if (needsDepencencyResolving) + addDependencies(productsToInstall); + InstallJob * const job = new InstallJob(logger, jobOwner); + job->install(internalProject, productsToInstall, options); + return job; +} + +QList ProjectPrivate::internalProducts(const QList &products) const +{ + QList internalProducts; + foreach (const ProductData &product, products) { + if (product.isEnabled()) + internalProducts << internalProduct(product); + } + return internalProducts; +} + +static QList enabledInternalProducts(const ResolvedProjectConstPtr &project, + bool includingNonDefault) +{ + QList products; + foreach (const ResolvedProductPtr &p, project->products) { + if (p->enabled && (includingNonDefault || p->builtByDefault())) + products << p; + } + foreach (const ResolvedProjectConstPtr &subProject, project->subProjects) + products << enabledInternalProducts(subProject, includingNonDefault); + return products; +} + +QList ProjectPrivate::allEnabledInternalProducts(bool includingNonDefault) const +{ + return enabledInternalProducts(internalProject, includingNonDefault); +} + +static ResolvedProductPtr internalProductForProject(const ResolvedProjectConstPtr &project, + const ProductData &product) +{ + foreach (const ResolvedProductPtr &resolvedProduct, project->products) { + if (product.name() == resolvedProduct->name + && product.profile() == resolvedProduct->profile) { + 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 ProductData &product) const +{ + foreach (const ProductData &p, m_projectData.allProducts()) { + if (p.name() == product.name() && p.profile() == product.profile()) + return p; + } + return ProductData(); +} + +QList ProjectPrivate::findProductsByName(const QString &name) const +{ + QList list; + foreach (const ProductData &p, m_projectData.allProducts()) { + if (p.name() == name) + list << p; + } + return list; +} + +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, + const ResolvedProductConstPtr &product) +{ + GroupData group; + group.d->name = resolvedGroup->name; + group.d->prefix = resolvedGroup->prefix; + group.d->location = resolvedGroup->location; + foreach (const SourceArtifactConstPtr &sa, resolvedGroup->files) { + ArtifactData artifact = createApiSourceArtifact(sa); + setupInstallData(artifact, product); + group.d->sourceArtifacts << artifact; + } + if (resolvedGroup->wildcards) { + foreach (const SourceArtifactConstPtr &sa, resolvedGroup->wildcards->files) { + ArtifactData artifact = createApiSourceArtifact(sa); + setupInstallData(artifact, product); + group.d->sourceArtifactsFromWildcards << artifact; + } + } + qSort(group.d->sourceArtifacts); + qSort(group.d->sourceArtifactsFromWildcards); + group.d->properties.d->m_map = resolvedGroup->properties; + group.d->isEnabled = resolvedGroup->enabled; + group.d->isValid = true; + return group; +} + +ArtifactData ProjectPrivate::createApiSourceArtifact(const SourceArtifactConstPtr &sa) +{ + ArtifactData saApi; + saApi.d->isValid = true; + saApi.d->filePath = sa->absoluteFilePath; + saApi.d->fileTags = sa->fileTags.toStringList(); + saApi.d->isGenerated = false; + saApi.d->isTargetArtifact = false; + saApi.d->properties.d->m_map = sa->properties; + return saApi; +} + +void ProjectPrivate::setupInstallData(ArtifactData &artifact, + const ResolvedProductConstPtr &product) +{ + artifact.d->installData.d->isValid = true; + artifact.d->installData.d->isInstallable = artifact.properties().getModuleProperty( + QLatin1String("qbs"), QLatin1String("install")).toBool(); + if (!artifact.d->installData.d->isInstallable) + return; + const QString installRoot = artifact.properties().getModuleProperty( + QLatin1String("qbs"), QLatin1String("installRoot")).toString(); + InstallOptions options; + options.setInstallRoot(installRoot); + QString installFilePath = ProductInstaller::targetFilePath(product->topLevelProject(), + product->sourceDirectory, artifact.filePath(), artifact.properties().d->m_map, options); + if (!installRoot.isEmpty()) + installFilePath.remove(0, installRoot.count()); + artifact.d->installData.d->installRoot = installRoot; + artifact.d->installData.d->installFilePath = installFilePath; +} + +#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES +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.")); + QList products = findProductsByName(product.name()); + if (products.isEmpty()) + throw ErrorInfo(Tr::tr("Product '%1' does not exist.").arg(product.name())); + const QList resolvedProducts = internalProducts(products); + QBS_CHECK(products.count() == resolvedProducts.count()); + + foreach (const GroupPtr &resolvedGroup, resolvedProducts.first()->groups) { + if (resolvedGroup->name == groupName) { + throw ErrorInfo(Tr::tr("Group '%1' already exists in product '%2'.") + .arg(groupName, product.name()), resolvedGroup->location); + } + } + + ProjectFileGroupInserter groupInserter(products.first(), 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()); + + products = findProductsByName(products.first().name()); // These are new objects. + QBS_CHECK(products.count() == resolvedProducts.count()); + for (int i = 0; i < products.count(); ++i) { + const GroupPtr resolvedGroup = ResolvedGroup::create(); + resolvedGroup->location = groupInserter.itemPosition(); + resolvedGroup->enabled = true; + resolvedGroup->name = groupName; + resolvedGroup->properties = resolvedProducts[i]->moduleProperties; + resolvedGroup->overrideTags = false; + resolvedProducts.at(i)->groups << resolvedGroup; + products.at(i).d->groups << createGroupDataFromGroup(resolvedGroup, resolvedProducts.at(i)); + qSort(products.at(i).d->groups); + } +} + +ProjectPrivate::GroupUpdateContext ProjectPrivate::getGroupContext(const ProductData &product, + const GroupData &group) +{ + GroupUpdateContext context; + if (!product.isValid()) + throw ErrorInfo(Tr::tr("Product is invalid.")); + context.products = findProductsByName(product.name()); + if (context.products.isEmpty()) + throw ErrorInfo(Tr::tr("Product '%1' does not exist.").arg(product.name())); + context.resolvedProducts = internalProducts(context.products); + + const QString groupName = group.isValid() ? group.name() : product.name(); + foreach (const ResolvedProductPtr &p, context.resolvedProducts) { + foreach (const GroupPtr &g, p->groups) { + if (g->name == groupName) { + context.resolvedGroups << g; + break; + } + } + } + if (context.resolvedGroups.isEmpty()) + throw ErrorInfo(Tr::tr("Group '%1' does not exist.").arg(groupName)); + foreach (const ProductData &p, context.products) { + const GroupData &g = findGroupData(p, groupName); + QBS_CHECK(p.isValid()); + context.groups << g; + } + QBS_CHECK(context.resolvedProducts.count() == context.products.count()); + QBS_CHECK(context.resolvedProducts.count() == context.resolvedGroups.count()); + QBS_CHECK(context.products.count() == context.groups.count()); + return context; +} + +static bool matchesWildcard(const QString &filePath, const GroupConstPtr &group) +{ + if (!group->wildcards) + return false; + foreach (const QString &pattern, group->wildcards->patterns) { + QString fullPattern; + if (QFileInfo(group->prefix).isAbsolute()) { + fullPattern = group->prefix; + } else { + fullPattern = QFileInfo(group->location.filePath()).absolutePath() + + QLatin1Char('/') + group->prefix; + } + fullPattern.append(QLatin1Char('/')).append(pattern); + fullPattern = QDir::cleanPath(fullPattern); + if (QRegExp(fullPattern, Qt::CaseSensitive, QRegExp::Wildcard).exactMatch(filePath)) + return true; + } + return false; +} + +ProjectPrivate::FileListUpdateContext ProjectPrivate::getFileListContext(const ProductData &product, + const GroupData &group, const QStringList &filePaths, bool forAdding) +{ + FileListUpdateContext filesContext; + GroupUpdateContext &groupContext = filesContext.groupContext; + groupContext = getGroupContext(product, group); + + if (filePaths.isEmpty()) + throw ErrorInfo(Tr::tr("No files supplied.")); + + QString prefix; + for (int i = 0; i < groupContext.resolvedGroups.count(); ++i) { + const GroupPtr &g = groupContext.resolvedGroups.at(i); + if (!g->prefix.isEmpty() && !g->prefix.endsWith(QLatin1Char('/'))) + throw ErrorInfo(Tr::tr("Group has non-directory prefix.")); + if (i == 0) + prefix = g->prefix; + else if (prefix != g->prefix) + throw ErrorInfo(Tr::tr("Cannot update: Group prefix depends on properties.")); + } + QString baseDirPath = QFileInfo(product.location().filePath()).dir().absolutePath() + + QLatin1Char('/') + 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 (forAdding && !FileInfo(absPath).exists()) + throw ErrorInfo(Tr::tr("File '%1' does not exist.").arg(absPath)); + if (matchesWildcard(absPath, groupContext.resolvedGroups.first())) { + filesContext.absoluteFilePathsFromWildcards << absPath; + } else { + filesContext.absoluteFilePaths << absPath; + filesContext.relativeFilePaths << baseDir.relativeFilePath(absPath); + } + } + + return filesContext; +} + +static SourceArtifactPtr createSourceArtifact(const QString &filePath, + const ResolvedProductPtr &product, const GroupPtr &group, bool wildcard, Logger &logger) +{ + const SourceArtifactPtr artifact + = ProjectResolver::createSourceArtifact(product, filePath, group, wildcard); + ProjectResolver::applyFileTaggers(artifact, product, logger); + return artifact; +} + +void ProjectPrivate::addFiles(const ProductData &product, const GroupData &group, + const QStringList &filePaths) +{ + FileListUpdateContext filesContext = getFileListContext(product, group, filePaths, true); + GroupUpdateContext &groupContext = filesContext.groupContext; + + // We do not check for entries in other groups, because such doublettes might be legitimate + // due to conditions. + foreach (const GroupPtr &group, groupContext.resolvedGroups) { + foreach (const QString &filePath, filesContext.absoluteFilePaths) { + foreach (const SourceArtifactConstPtr &sa, group->files) { + if (sa->absoluteFilePath == filePath) { + throw ErrorInfo(Tr::tr("File '%1' already exists in group '%2'.") + .arg(filePath, group->name)); + } + } + } + } + + ProjectFileFilesAdder adder(groupContext.products.first(), + group.isValid() ? groupContext.groups.first() : GroupData(), + filesContext.relativeFilePaths); + adder.apply(); + + m_projectData.d.detach(); + updateInternalCodeLocations(internalProject, adder.itemPosition(), adder.lineOffset()); + updateExternalCodeLocations(m_projectData, adder.itemPosition(), adder.lineOffset()); + + QHash> addedSourceArtifacts; + for (int i = 0; i < groupContext.resolvedGroups.count(); ++i) { + const ResolvedProductPtr &resolvedProduct = groupContext.resolvedProducts.at(i); + const GroupPtr &resolvedGroup = groupContext.resolvedGroups.at(i); + foreach (const QString &file, filesContext.absoluteFilePaths) { + const SourceArtifactPtr sa = createSourceArtifact(file, resolvedProduct, resolvedGroup, + false, logger); + addedSourceArtifacts.insert(file, qMakePair(sa, resolvedProduct)); + } + foreach (const QString &file, filesContext.absoluteFilePathsFromWildcards) { + QBS_CHECK(resolvedGroup->wildcards); + const SourceArtifactPtr sa = createSourceArtifact(file, resolvedProduct, resolvedGroup, + true, logger); + addedSourceArtifacts.insert(file, qMakePair(sa, resolvedProduct)); + } + if (resolvedProduct->enabled) { + foreach (const auto &pair, addedSourceArtifacts) + createArtifact(resolvedProduct, pair.first, logger); + } + } + doSanityChecks(internalProject, logger); + QList sourceArtifacts; + QList sourceArtifactsFromWildcards; + foreach (const QString &fp, filesContext.absoluteFilePaths) { + const auto pair = addedSourceArtifacts.value(fp); + const SourceArtifactConstPtr sa = pair.first; + QBS_CHECK(sa); + ArtifactData artifactData = createApiSourceArtifact(sa); + setupInstallData(artifactData, pair.second); + sourceArtifacts << artifactData; + } + foreach (const QString &fp, filesContext.absoluteFilePathsFromWildcards) { + const auto pair = addedSourceArtifacts.value(fp); + const SourceArtifactConstPtr sa = pair.first; + QBS_CHECK(sa); + ArtifactData artifactData = createApiSourceArtifact(sa); + setupInstallData(artifactData, pair.second); + sourceArtifactsFromWildcards << artifactData; + } + foreach (const GroupData &g, groupContext.groups) { + g.d->sourceArtifacts << sourceArtifacts; + qSort(g.d->sourceArtifacts); + g.d->sourceArtifactsFromWildcards << sourceArtifactsFromWildcards; + qSort(g.d->sourceArtifactsFromWildcards); + } +} + +void ProjectPrivate::removeFiles(const ProductData &product, const GroupData &group, + const QStringList &filePaths) +{ + FileListUpdateContext filesContext = getFileListContext(product, group, filePaths, false); + GroupUpdateContext &groupContext = filesContext.groupContext; + + if (!filesContext.absoluteFilePathsFromWildcards.isEmpty()) { + throw ErrorInfo(Tr::tr("The following files cannot be removed from the project file, " + "because they match wildcard patterns: %1") + .arg(filesContext.absoluteFilePathsFromWildcards.join(QLatin1String(", ")))); + } + QStringList filesNotFound = filesContext.absoluteFilePaths; + QList sourceArtifacts; + foreach (const SourceArtifactPtr &sa, groupContext.resolvedGroups.first()->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.products.first(), + group.isValid() ? groupContext.groups.first() : GroupData(), + filesContext.relativeFilePaths); + remover.apply(); + + for (int i = 0; i < groupContext.resolvedProducts.count(); ++i) { + removeFilesFromBuildGraph(groupContext.resolvedProducts.at(i), sourceArtifacts); + foreach (const SourceArtifactPtr &sa, sourceArtifacts) + groupContext.resolvedGroups.at(i)->files.removeOne(sa); + } + doSanityChecks(internalProject, logger); + + m_projectData.d.detach(); + updateInternalCodeLocations(internalProject, remover.itemPosition(), remover.lineOffset()); + updateExternalCodeLocations(m_projectData, remover.itemPosition(), remover.lineOffset()); + foreach (const GroupData &g, groupContext.groups) { + for (int i = g.d->sourceArtifacts.count() - 1; i >= 0; --i) { + if (filesContext.absoluteFilePaths.contains(g.d->sourceArtifacts.at(i).filePath())) + g.d->sourceArtifacts.removeAt(i); + } + } +} + +void ProjectPrivate::removeGroup(const ProductData &product, const GroupData &group) +{ + GroupUpdateContext context = getGroupContext(product, group); + + ProjectFileGroupRemover remover(context.products.first(), context.groups.first()); + remover.apply(); + + for (int i = 0; i < context.resolvedProducts.count(); ++i) { + const ResolvedProductPtr &product = context.resolvedProducts.at(i); + const GroupPtr &group = context.resolvedGroups.at(i); + removeFilesFromBuildGraph(product, group->allFiles()); + const bool removed = product->groups.removeOne(group); + QBS_CHECK(removed); + } + doSanityChecks(internalProject, logger); + + m_projectData.d.detach(); + updateInternalCodeLocations(internalProject, remover.itemPosition(), remover.lineOffset()); + updateExternalCodeLocations(m_projectData, remover.itemPosition(), remover.lineOffset()); + for (int i = 0; i < context.products.count(); ++i) { + const bool removed = context.products.at(i).d->groups.removeOne(context.groups.at(i)); + QBS_CHECK(removed); + } +} +#endif // QBS_ENABLE_PROJECT_FILE_UPDATES + +void ProjectPrivate::removeFilesFromBuildGraph(const ResolvedProductConstPtr &product, + const QList &files) +{ + if (!product->enabled) + return; + QBS_CHECK(internalProject->buildData); + ArtifactSet allRemovedArtifacts; + foreach (const SourceArtifactPtr &sa, files) { + ArtifactSet removedArtifacts; + Artifact * const artifact = lookupArtifact(product, sa->absoluteFilePath); + if (artifact) { // Can be null if the executor has not yet applied the respective rule. + internalProject->buildData->removeArtifactAndExclusiveDependents(artifact, logger, + true, &removedArtifacts); + } + allRemovedArtifacts.unite(removedArtifacts); + } + EmptyDirectoriesRemover(product->topLevelProject(), logger) + .removeEmptyParentDirectories(allRemovedArtifacts); + qDeleteAll(allRemovedArtifacts); +} + +static void updateLocationIfNecessary(CodeLocation &location, const CodeLocation &changeLocation, + int lineOffset) +{ + if (location.filePath() == changeLocation.filePath() + && location.line() >= changeLocation.line()) { + location = CodeLocation(location.filePath(), 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->prepareScript->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 ResolvedScannerConstPtr &scanner, product->scanners) { + updateLocationIfNecessary(scanner->searchPathsScript->location, changeLocation, lineOffset); + updateLocationIfNecessary(scanner->scanScript->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_projectData.isValid()) + retrieveProjectData(m_projectData, internalProject); +} + +RuleCommandList ProjectPrivate::ruleCommands(const ProductData &product, + const QString &inputFilePath, const QString &outputFileTag) const +{ + if (internalProject->locked) + throw ErrorInfo(Tr::tr("A job is currently in process.")); + const ResolvedProductConstPtr resolvedProduct = internalProduct(product); + if (!resolvedProduct) + throw ErrorInfo(Tr::tr("No such product '%1'.").arg(product.name())); + if (!resolvedProduct->enabled) + throw ErrorInfo(Tr::tr("Product '%1' is disabled.").arg(product.name())); + QBS_CHECK(resolvedProduct->buildData); + const ArtifactSet &outputArtifacts = resolvedProduct->buildData->artifactsByFileTag + .value(FileTag(outputFileTag.toLocal8Bit())); + foreach (const Artifact * const outputArtifact, outputArtifacts) { + const TransformerConstPtr transformer = outputArtifact->transformer; + if (!transformer) + continue; + foreach (const Artifact * const inputArtifact, transformer->inputs) { + if (inputArtifact->filePath() == inputFilePath) { + RuleCommandList list; + foreach (const AbstractCommandPtr &internalCommand, transformer->commands) { + RuleCommand externalCommand; + externalCommand.d->description = internalCommand->description(); + externalCommand.d->extendedDescription = internalCommand->extendedDescription(); + switch (internalCommand->type()) { + case AbstractCommand::JavaScriptCommandType: { + externalCommand.d->type = RuleCommand::JavaScriptCommandType; + const JavaScriptCommandPtr &jsCmd + = internalCommand.staticCast(); + externalCommand.d->sourceCode = jsCmd->sourceCode(); + break; + } + case AbstractCommand::ProcessCommandType: { + externalCommand.d->type = RuleCommand::ProcessCommandType; + const ProcessCommandPtr &procCmd + = internalCommand.staticCast(); + externalCommand.d->executable = procCmd->program(); + externalCommand.d->arguments = procCmd->arguments(); + externalCommand.d->workingDir = procCmd->workingDir(); + externalCommand.d->environment = procCmd->environment(); + break; + } + } + list << externalCommand; + } + return list; + } + } + } + + throw ErrorInfo(Tr::tr("No rule was found that produces an artifact tagged '%1' " + "from input file '%2'.").arg(outputFileTag, inputFilePath)); +} + +static bool productIsRunnable(const ResolvedProductConstPtr &product) +{ + return product->fileTags.contains("application"); +} + +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->type = resolvedProduct->fileTags.toStringList(); + product.d->name = resolvedProduct->name; + product.d->targetName = resolvedProduct->targetName; + product.d->version = resolvedProduct->productProperties + .value(QLatin1String("version")).toString(); + product.d->profile = resolvedProduct->profile; + product.d->location = resolvedProduct->location; + product.d->buildDirectory = resolvedProduct->buildDirectory(); + product.d->isEnabled = resolvedProduct->enabled; + product.d->isRunnable = productIsRunnable(resolvedProduct); + product.d->properties = resolvedProduct->productProperties; + product.d->moduleProperties.d->m_map = resolvedProduct->moduleProperties; + foreach (const GroupPtr &resolvedGroup, resolvedProduct->groups) + product.d->groups << createGroupDataFromGroup(resolvedGroup, resolvedProduct); + if (resolvedProduct->enabled) { + QBS_CHECK(resolvedProduct->buildData); + const ArtifactSet targetArtifacts = resolvedProduct->targetArtifacts(); + foreach (Artifact * const a, + filterByType(resolvedProduct->buildData->nodes)) { + if (a->artifactType != Artifact::Generated) + continue; + ArtifactData ta; + ta.d->filePath = a->filePath(); + ta.d->fileTags = a->fileTags().toStringList(); + ta.d->properties.d->m_map = a->properties; + ta.d->isGenerated = true; + ta.d->isTargetArtifact = targetArtifacts.contains(a); + ta.d->isValid = true; + setupInstallData(ta, resolvedProduct); + product.d->generatedArtifacts << ta; + } + for (auto it = resolvedProduct->buildData->rescuableArtifactData.constBegin(); + it != resolvedProduct->buildData->rescuableArtifactData.constEnd(); ++it) { + ArtifactData ta; + ta.d->filePath = it.key(); + ta.d->fileTags = it.value().fileTags.toStringList(); + ta.d->properties.d->m_map = PropertyMapInternal::create(); + ta.d->properties.d->m_map->setValue(it.value().properties); + ta.d->isGenerated = true; + ta.d->isTargetArtifact = resolvedProduct->fileTags.matches(it.value().fileTags); + ta.d->isValid = true; + setupInstallData(ta, resolvedProduct); + product.d->generatedArtifacts << ta; + } + } + foreach (const ResolvedProductPtr &resolvedDependentProduct, resolvedProduct->dependencies) + product.d->dependencies << resolvedDependentProduct->name; + qSort(product.d->type); + qSort(product.d->groups); + qSort(product.d->generatedArtifacts); + qSort(product.d->dependencies); + product.d->isValid = true; + projectData.d->products << product; + } + foreach (const ResolvedProjectConstPtr &internalSubProject, internalProject->subProjects) { + if (!internalSubProject->enabled) + continue; + ProjectData subProject; + retrieveProjectData(subProject, internalSubProject); + projectData.d->subProjects << subProject; + } + projectData.d->isValid = true; + qSort(projectData.d->products); + qSort(projectData.d->subProjects); +} + +} // 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; +} + +/*! + * \brief The top-level profile for building this project. + */ +QString Project::profile() const +{ + QBS_ASSERT(isValid(), return QString()); + return d->internalProject->profile(); +} + +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. + * If the function is called on a valid \c Project object, the build graph will not be loaded + * from a file, but will be taken from the existing project. In that case, if resolving + * finishes successfully, the existing project will be invalidated. If resolving fails, qbs will + * try to keep the existing project valid. However, under certain circumstances, resolving the new + * project will fail at a time where existing project data has already been touched, in which case + * the existing project has to be invalidated (this could be avoided, but it would hurt performance). + * So after an unsuccessful re-resolve job, the existing project may or may not be valid anymore. + * \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 ¶meters, + ILogSink *logSink, QObject *jobOwner) +{ + Logger logger(logSink); + SetupProjectJob * const job = new SetupProjectJob(logger, jobOwner); + try { + loadPlugins(parameters.pluginPaths(), logger); + job->resolve(*this, 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 +{ + QBS_ASSERT(isValid(), return ProjectData()); + return d->projectData(); +} + +RunEnvironment Project::getRunEnvironment(const ProductData &product, + const InstallOptions &installOptions, + const QProcessEnvironment &environment, Settings *settings) const +{ + const ResolvedProductPtr resolvedProduct = d->internalProduct(product); + return RunEnvironment(resolvedProduct, installOptions, environment, settings, d->logger); +} + +/*! + * \enum Project::ProductSelection + * This enum type specifies which products to include if "all" products are to be built. + * \value Project::ProdProductSelectionDefaultOnly Indicates that only those products should be + * built whose \c builtByDefault property + * is \c true. + * \value Project::ProdProductSelectionWithNonDefault Indicates that products whose + * \c builtByDefault property is \c false should + * also be built. + */ + +/*! + * \brief Causes all products of this project to be built, if necessary. + * If and only if \c producSelection is \c Project::ProductSelectionWithNonDefault, products with + * the \c builtByDefault property set to \c false will be built too. + * The function will finish immediately, returning a \c BuildJob identifiying the operation. + */ +BuildJob *Project::buildAllProducts(const BuildOptions &options, ProductSelection productSelection, + QObject *jobOwner) const +{ + QBS_ASSERT(isValid(), return 0); + const bool includingNonDefault = productSelection == ProductSelectionWithNonDefault; + return d->buildProducts(d->allEnabledInternalProducts(includingNonDefault), options, + !includingNonDefault, 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 &products, + const BuildOptions &options, QObject *jobOwner) const +{ + QBS_ASSERT(isValid(), return 0); + 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() << 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 +{ + QBS_ASSERT(isValid(), return 0); + return d->cleanProducts(d->allEnabledInternalProducts(true), 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 &products, + const CleanOptions &options, QObject *jobOwner) const +{ + QBS_ASSERT(isValid(), return 0); + 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() << product, options, jobOwner); +} + +/*! + * \brief Installs the installable files of all products in the project. + * If and only if \c producSelection is \c Project::ProductSelectionWithNonDefault, products with + * the \c builtByDefault property set to \c false will be installed too. + * The function will finish immediately, returning an \c InstallJob identifiying this operation. + */ +InstallJob *Project::installAllProducts(const InstallOptions &options, + ProductSelection productSelection, QObject *jobOwner) const +{ + QBS_ASSERT(isValid(), return 0); + const bool includingNonDefault = productSelection == ProductSelectionWithNonDefault; + return d->installProducts(d->allEnabledInternalProducts(includingNonDefault), options, + !includingNonDefault, 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 &products, + const InstallOptions &options, QObject *jobOwner) const +{ + QBS_ASSERT(isValid(), return 0); + 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() << product, options, jobOwner); +} + +/*! + * \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 &products) +{ + QBS_ASSERT(isValid(), return); + TimestampsUpdater().updateTimestamps(d->internalProject, d->internalProducts(products), + d->logger); +} + +/*! + * \brief Finds files generated from the given file in the given product. + * If \a recursive is \c false, only files generated directly from \a file will be considered, + * otherwise the generated files are collected recursively. + * If \a tags is not empty, only generated files matching at least one of these tags will + * be considered. + */ +QStringList Project::generatedFiles(const ProductData &product, const QString &file, + bool recursive, const QStringList &tags) const +{ + QBS_ASSERT(isValid(), return QStringList()); + const ResolvedProductConstPtr internalProduct = d->internalProduct(product); + return internalProduct->generatedFiles(file, recursive, FileTags::fromStringList(tags)); +} + +QVariantMap Project::projectConfiguration() const +{ + QBS_ASSERT(isValid(), return QVariantMap()); + return d->internalProject->buildConfiguration(); +} + +QHash Project::usedEnvironment() const +{ + typedef QHash EnvType; + QBS_ASSERT(isValid(), return EnvType()); + return d->internalProject->usedEnvironment; +} + +QSet Project::buildSystemFiles() const +{ + QBS_ASSERT(isValid(), return QSet()); + return d->internalProject->buildSystemFiles; +} + +RuleCommandList Project::ruleCommands(const ProductData &product, + const QString &inputFilePath, const QString &outputFileTag, ErrorInfo *error) const +{ + QBS_ASSERT(isValid(), return RuleCommandList()); + QBS_ASSERT(product.isValid(), return RuleCommandList()); + + try { + return d->ruleCommands(product, inputFilePath, outputFileTag); + } catch (const ErrorInfo &e) { + if (error) + *error = e; + return RuleCommandList(); + } +} + +ErrorInfo Project::dumpNodesTree(QIODevice &outDevice, const QList &products) +{ + try { + NodeTreeDumper(outDevice).start(d->internalProducts(products)); + } catch (const ErrorInfo &e) { + return e; + } + return ErrorInfo(); +} + +#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES +/*! + * \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 { + QBS_CHECK(isValid()); + d->prepareChangeToProject(); + d->addGroup(product, groupName); + d->internalProject->lastResolveTime = FileTime::currentTime(); + d->internalProject->store(d->logger); + 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 { + QBS_CHECK(isValid()); + d->prepareChangeToProject(); + d->addFiles(product, group, filePaths); + d->internalProject->lastResolveTime = FileTime::currentTime(); + d->internalProject->store(d->logger); + 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 { + QBS_CHECK(isValid()); + d->prepareChangeToProject(); + d->removeFiles(product, group, filePaths); + d->internalProject->lastResolveTime = FileTime::currentTime(); + d->internalProject->store(d->logger); + 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 { + QBS_CHECK(isValid()); + d->prepareChangeToProject(); + d->removeGroup(product, group); + d->internalProject->lastResolveTime = FileTime::currentTime(); + d->internalProject->store(d->logger); + return ErrorInfo(); + } catch (ErrorInfo errorInfo) { + errorInfo.prepend(Tr::tr("Failure removing group '%1' from product '%2'.") + .arg(group.name(), product.name())); + return errorInfo; + } +} +#endif // QBS_ENABLE_PROJECT_FILE_UPDATES + +} // namespace qbs diff --git a/src/lib/corelib/api/project.h b/src/lib/corelib/api/project.h new file mode 100644 index 00000000..ccf1d736 --- /dev/null +++ b/src/lib/corelib/api/project.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROJECT_H +#define QBS_PROJECT_H + +#include "rulecommand.h" +#include "../language/forward_decls.h" +#include "../tools/qbs_export.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QIODevice; +class QObject; +class QProcessEnvironment; +QT_END_NAMESPACE + +namespace qbs { +class BuildJob; +class BuildOptions; +class CleanJob; +class CleanOptions; +class ErrorInfo; +class GroupData; +class ILogSink; +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: + SetupProjectJob *setupProject(const SetupProjectParameters ¶meters, + ILogSink *logSink, QObject *jobOwner); + + Project(); + Project(const Project &other); + Project &operator=(const Project &other); + ~Project(); + + bool isValid() const; + QString profile() const; + ProjectData projectData() const; + RunEnvironment getRunEnvironment(const ProductData &product, + const InstallOptions &installOptions, + const QProcessEnvironment &environment, Settings *settings) const; + + enum ProductSelection { ProductSelectionDefaultOnly, ProductSelectionWithNonDefault }; + BuildJob *buildAllProducts(const BuildOptions &options, + ProductSelection productSelection = ProductSelectionDefaultOnly, + QObject *jobOwner = 0) const; + BuildJob *buildSomeProducts(const QList &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 &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, + ProductSelection productSelection = ProductSelectionDefaultOnly, + QObject *jobOwner = 0) const; + InstallJob *installSomeProducts(const QList &products, + const InstallOptions &options, QObject *jobOwner = 0) const; + InstallJob *installOneProduct(const ProductData &product, const InstallOptions &options, + QObject *jobOwner = 0) const; + + void updateTimestamps(const QList &products); + + bool operator==(const Project &other) const { return d.data() == other.d.data(); } + + QStringList generatedFiles(const ProductData &product, const QString &file, + bool recursive, const QStringList &tags = QStringList()) const; + + QVariantMap projectConfiguration() const; + QHash usedEnvironment() const; + + QSet buildSystemFiles() const; + + RuleCommandList ruleCommands(const ProductData &product, const QString &inputFilePath, + const QString &outputFileTag, ErrorInfo *error = 0) const; + + ErrorInfo dumpNodesTree(QIODevice &outDevice, const QList &products); + +#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES + 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); +#endif // QBS_ENABLE_PROJECT_FILE_UPDATES + +private: + Project(const Internal::TopLevelProjectPtr &internalProject, const Internal::Logger &logger); + + QExplicitlySharedDataPointer 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/project_p.h b/src/lib/corelib/api/project_p.h new file mode 100644 index 00000000..7377f6d1 --- /dev/null +++ b/src/lib/corelib/api/project_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROJECT_P_H +#define QBS_PROJECT_P_H + +#include "projectdata.h" +#include "rulecommand.h" + +#include +#include + +#include +#include + +namespace qbs { +class BuildJob; +class BuildOptions; +class CleanJob; +class CleanOptions; +class InstallJob; +class InstallOptions; + +namespace Internal { + +class ProjectPrivate : public QSharedData +{ +public: + ProjectPrivate(const TopLevelProjectPtr &internalProject, const Logger &logger) + : internalProject(internalProject), logger(logger) + { + } + + ProjectData projectData(); + BuildJob *buildProducts(const QList &products, const BuildOptions &options, + bool needsDepencencyResolving, + QObject *jobOwner); + CleanJob *cleanProducts(const QList &products, const CleanOptions &options, + QObject *jobOwner); + InstallJob *installProducts(const QList &products, + const InstallOptions &options, bool needsDepencencyResolving, + QObject *jobOwner); + QList internalProducts(const QList &products) const; + QList allEnabledInternalProducts(bool includingNonDefault) const; + ResolvedProductPtr internalProduct(const ProductData &product) const; + ProductData findProductData(const ProductData &product) const; + QList findProductsByName(const QString &name) const; + GroupData findGroupData(const ProductData &product, const QString &groupName) const; + + GroupData createGroupDataFromGroup(const GroupPtr &resolvedGroup, + const ResolvedProductConstPtr &product); + ArtifactData createApiSourceArtifact(const SourceArtifactConstPtr &sa); + void setupInstallData(ArtifactData &artifact, const ResolvedProductConstPtr &product); + + struct GroupUpdateContext { + QList resolvedProducts; + QList resolvedGroups; + QList products; + QList groups; + }; + + struct FileListUpdateContext { + GroupUpdateContext groupContext; + QStringList absoluteFilePaths; + QStringList relativeFilePaths; + QStringList absoluteFilePathsFromWildcards; // Not included in the other two lists. + }; + + GroupUpdateContext getGroupContext(const ProductData &product, const GroupData &group); + FileListUpdateContext getFileListContext(const ProductData &product, const GroupData &group, + const QStringList &filePaths, bool forAdding); + + 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 &files); + void updateInternalCodeLocations(const ResolvedProjectPtr &project, + const CodeLocation &changeLocation, int lineOffset); + void updateExternalCodeLocations(const ProjectData &project, + const CodeLocation &changeLocation, int lineOffset); + void prepareChangeToProject(); + + RuleCommandList ruleCommands(const ProductData &product, + const QString &inputFilePath, const QString &outputFileTag) const; + + TopLevelProjectPtr internalProject; + Logger logger; + +private: + void retrieveProjectData(ProjectData &projectData, + const ResolvedProjectConstPtr &internalProject); + + ProjectData m_projectData; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard diff --git a/src/lib/corelib/api/projectdata.cpp b/src/lib/corelib/api/projectdata.cpp new file mode 100644 index 00000000..4363a0cc --- /dev/null +++ b/src/lib/corelib/api/projectdata.cpp @@ -0,0 +1,872 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "projectdata.h" + +#include "projectdata_p.h" +#include "propertymap_p.h" +#include +#include +#include +#include +#include + +#include + +#include + +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 prefix of the group. + */ +QString GroupData::prefix() const +{ + return d->prefix; +} + +/*! + * \brief The files listed in the group item's "files" binding. + * \note These do not include expanded wildcards. + * \sa GroupData::sourceArtifactsFromWildcards + */ +QList GroupData::sourceArtifacts() const +{ + return d->sourceArtifacts; +} + +/*! + * \brief The list of files resulting from expanding all wildcard patterns in the group. + */ +QList GroupData::sourceArtifactsFromWildcards() const +{ + return d->sourceArtifactsFromWildcards; +} + +/*! + * \brief All files in this group, regardless of how whether they were given explicitly + * or via wildcards. + * \sa GroupData::sourceArtifacts + * \sa GroupData::sourceArtifactsFromWildcards + */ +QList GroupData::allSourceArtifacts() const +{ + return sourceArtifacts() + sourceArtifactsFromWildcards(); +} + +/*! + * \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; +} + +/*! + * \brief The paths of all files in this group. + * \sa GroupData::allSourceArtifacts + */ +QStringList GroupData::allFilePaths() const +{ + const QList &artifacts = allSourceArtifacts(); + QStringList paths; + paths.reserve(artifacts.count()); + std::transform(artifacts.constBegin(), artifacts.constEnd(), std::back_inserter(paths), + [](const ArtifactData &sa) { return sa.filePath(); }); + return paths; +} + +bool operator!=(const GroupData &lhs, const GroupData &rhs) +{ + return !(lhs == rhs); +} + +bool operator==(const GroupData &lhs, const GroupData &rhs) +{ + if (!lhs.isValid() && !rhs.isValid()) + return true; + + return lhs.isValid() == rhs.isValid() + && lhs.name() == rhs.name() + && lhs.location() == rhs.location() + && lhs.sourceArtifactsFromWildcards() == rhs.sourceArtifactsFromWildcards() + && lhs.sourceArtifacts() == rhs.sourceArtifacts() + && lhs.properties() == rhs.properties() + && lhs.isEnabled() == rhs.isEnabled(); +} + +bool operator<(const GroupData &lhs, const GroupData &rhs) +{ + return lhs.name() < rhs.name(); +} + + +/*! + * \class ArtifactData + * The \c ArtifactData class describes a file in a product. It is either a source file + * or it gets generated during the build process. + */ + +ArtifactData::ArtifactData() : d(new Internal::ArtifactDataPrivate) +{ +} + +ArtifactData::ArtifactData(const ArtifactData &other) : d(other.d) +{ +} + +ArtifactData &ArtifactData::operator=(const ArtifactData &other) +{ + d = other.d; + return *this; +} + +ArtifactData::~ArtifactData() +{ +} + +/*! + * \brief Returns true if and only if this object holds data that was initialized by Qbs. + */ +bool ArtifactData::isValid() const +{ + return d->isValid; +} + +/*! + * \brief The full path of this file. + */ +QString ArtifactData::filePath() const +{ + return d->filePath; +} + +/*! + * \brief The tags of this file. + * Typically, this list will contain just one element. + */ +QStringList ArtifactData::fileTags() const +{ + return d->fileTags; +} + +bool ArtifactData::isGenerated() const +{ + return d->isGenerated; +} + +/*! + * \brief True if and only if this file is executable, + * either natively or through an interpreter or shell. + */ +bool ArtifactData::isExecutable() const +{ + return d->fileTags.contains(QLatin1String("application")) + || d->fileTags.contains(QLatin1String("msi")); +} + +/*! + * \brief True if and only if this artifact is a target artifact of its product. + */ +bool ArtifactData::isTargetArtifact() const +{ + QBS_ASSERT(isValid(), return false); + return d->isTargetArtifact; +} + +/*! + * \brief The properties of this file. + */ +PropertyMap ArtifactData::properties() const +{ + return d->properties; +} + +/*! + \brief The installation-related data of this artifact. + */ +InstallData ArtifactData::installData() const +{ + return d->installData; +} + +bool operator==(const ArtifactData &ad1, const ArtifactData &ad2) +{ + return ad1.filePath() == ad2.filePath() + && ad1.fileTags() == ad2.fileTags() + && ad1.isGenerated() == ad2.isGenerated() + && ad1.properties() == ad2.properties(); +} + +bool operator!=(const ArtifactData &ta1, const ArtifactData &ta2) +{ + return !(ta1 == ta2); +} + +bool operator<(const ArtifactData &ta1, const ArtifactData &ta2) +{ + return ta1.filePath() < ta2.filePath(); +} + + +/*! + * \class InstallData + * \brief The \c InstallData class provides the installation-related data of an artifact. + */ + +InstallData::InstallData() : d(new Internal::InstallDataPrivate) +{ +} + +InstallData::InstallData(const InstallData &other) : d(other.d) +{ +} + +InstallData &InstallData::operator=(const InstallData &other) +{ + d = other.d; + return *this; +} + +InstallData::~InstallData() +{ +} + +/*! + * \brief Returns true if and only if this object holds data that was initialized by Qbs. + */ +bool InstallData::isValid() const +{ + return d->isValid; +} + +/*! + \brief Returns true if and only if \c{qbs.install} is \c true for the artifact. + */ +bool InstallData::isInstallable() const +{ + QBS_ASSERT(isValid(), return false); + return d->isInstallable; +} + +/*! + \brief Returns the directory into which the artifact will be installed. + \note This is not necessarily the same as \c{qbs.installDir}, because \c{qbs.installSourceBase} + might have been used. + */ +QString InstallData::installDir() const +{ + QBS_ASSERT(isValid(), return QString()); + return Internal::FileInfo::path(installFilePath()); +} + +/*! + \brief Returns the installed file path of the artifact. + */ +QString InstallData::installFilePath() const +{ + QBS_ASSERT(isValid(), return QString()); + return d->installFilePath; +} + +/*! + \brief Returns the value of \c{qbs.installRoot} for the artifact. + */ +QString InstallData::installRoot() const +{ + QBS_ASSERT(isValid(), return QString()); + return d->installRoot; +} + +/*! + \brief Returns the local installation directory of the artifact, that is \c installDir() + prepended by \c installRoot(). + */ +QString InstallData::localInstallDir() const +{ + return QDir::cleanPath(installRoot() + QLatin1Char('/') + installDir()); +} + +/*! + \brief Returns the local installed file path of the artifact, that is \c installFilePath() + prepended by \c installRoot(). + */ +QString InstallData::localInstallFilePath() const +{ + return QDir::cleanPath(installRoot() + QLatin1Char('/') + installFilePath()); +} + +/*! + * \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 product type, which is the list of file tags matching the product's target artifacts. + */ +QStringList ProductData::type() const +{ + return d->type; +} + +/*! + * \brief The names of dependent products. + */ +QStringList ProductData::dependencies() const +{ + return d->dependencies; +} + +/*! + * \brief The name of the product as given in the qbs source file. + */ +QString ProductData::name() const +{ + return d->name; +} + +/*! + * \brief The base name of the product's target file as given in the qbs source file. + */ +QString ProductData::targetName() const +{ + return d->targetName; +} + +/*! + * \brief The version number of the product. + */ +QString ProductData::version() const +{ + return d->version; +} + +/*! + * \brief The profile this product will be built for. + */ +QString ProductData::profile() const +{ + return d->profile; +} + +/*! + * \brief The location at which the product is defined in the source file. + */ +CodeLocation ProductData::location() const +{ + return d->location; +} + +/*! + * \brief The directory under which the product's generated artifacts are located. + */ +QString ProductData::buildDirectory() const +{ + return d->buildDirectory; +} + +/*! + * \brief All artifacts that are generated when building this product. + */ +QList ProductData::generatedArtifacts() const +{ + return d->generatedArtifacts; +} + +/*! + \brief This product's target artifacts. + This is a subset of \c generatedArtifacts() + */ +QList ProductData::targetArtifacts() const +{ + QList list; + std::copy_if(d->generatedArtifacts.constBegin(), d->generatedArtifacts.constEnd(), + std::back_inserter(list), + [](const ArtifactData &a) { return a.isTargetArtifact(); }); + return list; +} + +/*! + * \brief The list of artifacts in this product that are to be installed. + */ +QList ProductData::installableArtifacts() const +{ + QList artifacts; + foreach (const GroupData &g, groups()) { + foreach (const ArtifactData &a, g.allSourceArtifacts()) { + if (a.installData().isInstallable()) + artifacts << a; + } + } + foreach (const ArtifactData &a, targetArtifacts()) { + if (a.installData().isInstallable()) + artifacts << a; + } + return artifacts; +} + +/*! + * \brief Returns the file path of the executable associated with this product. + * If the product is not an application, an empty string is returned. + */ +QString ProductData::targetExecutable() const +{ + QBS_ASSERT(isValid(), return QString()); + foreach (const ArtifactData &ta, targetArtifacts()) { + if (ta.isExecutable()) { + if (ta.installData().isInstallable()) + return ta.installData().localInstallFilePath(); + return ta.filePath(); + } + } + return QString(); +} + +/*! + * \brief The list of \c GroupData in this product. + */ +QList ProductData::groups() const +{ + return d->groups; +} + +/*! + * \brief The product properties. + */ +QVariantMap ProductData::properties() const +{ + return d->properties; +} + +/*! + * \brief The set of properties inherited from dependent products and modules. + */ +PropertyMap ProductData::moduleProperties() const +{ + return d->moduleProperties; +} + +/*! + * \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 ProductData::isRunnable() const +{ + QBS_ASSERT(isValid(), return false); + return d->isRunnable; +} + +bool operator==(const ProductData &lhs, const ProductData &rhs) +{ + if (!lhs.isValid() && !rhs.isValid()) + return true; + + return lhs.isValid() == rhs.isValid() + && lhs.name() == rhs.name() + && lhs.targetName() == rhs.targetName() + && lhs.type() == rhs.type() + && lhs.version() == rhs.version() + && lhs.dependencies() == rhs.dependencies() + && lhs.profile() == rhs.profile() + && lhs.location() == rhs.location() + && lhs.groups() == rhs.groups() + && lhs.generatedArtifacts() == rhs.generatedArtifacts() + && lhs.properties() == rhs.properties() + && lhs.moduleProperties() == rhs.moduleProperties() + && lhs.isEnabled() == rhs.isEnabled(); +} + +bool operator!=(const ProductData &lhs, const ProductData &rhs) +{ + return !(lhs == rhs); +} + +bool operator<(const ProductData &lhs, const ProductData &rhs) +{ + const int nameCmp = lhs.name().compare(rhs.name()); + if (nameCmp < 0) + return true; + if (nameCmp > 0) + return false; + return lhs.profile() < rhs.profile(); +} + +/*! + * \class ProjectData + * \brief The \c ProjectData class corresponds to the \c Project item in a qbs source file. + */ + +/*! + * \fn QList 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 ProjectData::products() const +{ + return d->products; +} + +/*! + * The sub-projects of this project. + */ +QList ProjectData::subProjects() const +{ + return d->subProjects; +} + +/*! + * All products in this projects and its direct and indirect sub-projects. + */ +QList ProjectData::allProducts() const +{ + QList productList = products(); + foreach (const ProjectData &pd, subProjects()) + productList << pd.allProducts(); + return productList; +} + +/*! + * The artifacts of all products in this project that are to be installed. + */ +QList ProjectData::installableArtifacts() const +{ + QList artifacts; + foreach (const ProductData &p, allProducts()) + artifacts << p.installableArtifacts(); + return artifacts; +} + +bool operator==(const ProjectData &lhs, const ProjectData &rhs) +{ + if (!lhs.isValid() && !rhs.isValid()) + return true; + + return lhs.isValid() == rhs.isValid() + && lhs.isEnabled() == rhs.isEnabled() + && lhs.name() == rhs.name() + && lhs.buildDirectory() == rhs.buildDirectory() + && 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) +{ + if (this != &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()) + 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 Convenience wrapper around \c PropertyMap::getModuleProperty for properties of list type. + * + */ +QStringList PropertyMap::getModulePropertiesAsStringList(const QString &moduleName, + const QString &propertyName) const +{ + const QVariantList &vl = Internal::PropertyFinder().propertyValue(d->m_map->value(), moduleName, + propertyName).toList(); + QStringList sl; + foreach (const QVariant &v, vl) { + QBS_ASSERT(v.canConvert(), continue); + sl << v.toString(); + } + return sl; +} + +/*! + * \brief Returns the value of the given module property. + */ +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(), prefix + key + QLatin1Char('.')); + } else { + stringRep += QString::fromLatin1("%1%2: %3\n") + .arg(prefix, key, 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 00000000..3e4065c5 --- /dev/null +++ b/src/lib/corelib/api/projectdata.h @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROJECTDATA_H +#define QBS_PROJECTDATA_H + +#include "../tools/codelocation.h" +#include "../tools/qbs_export.h" + +#include +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { +class ArtifactDataPrivate; +class GroupDataPrivate; +class InstallDataPrivate; +class ProductDataPrivate; +class ProjectPrivate; +class ProjectDataPrivate; +class PropertyMapPrivate; +} // 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; + + 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 InstallData; + +class QBS_EXPORT ArtifactData +{ + friend class Internal::ProjectPrivate; +public: + ArtifactData(); + ArtifactData(const ArtifactData &other); + ArtifactData &operator=(const ArtifactData &other); + ~ArtifactData(); + + bool isValid() const; + + QString filePath() const; + QStringList fileTags() const; + bool isGenerated() const; + bool isExecutable() const; + bool isTargetArtifact() const; + PropertyMap properties() const; + InstallData installData() const; + +private: + QExplicitlySharedDataPointer d; +}; + +class QBS_EXPORT InstallData +{ + friend class Internal::ProjectPrivate; +public: + InstallData(); + InstallData(const InstallData &other); + InstallData &operator=(const InstallData &other); + ~InstallData(); + + bool isValid() const; + + bool isInstallable() const; + QString installDir() const; + QString installFilePath() const; + QString installRoot() const; + QString localInstallDir() const; + QString localInstallFilePath() const; +private: + QExplicitlySharedDataPointer d; +}; + +QBS_EXPORT bool operator==(const ArtifactData &ta1, const ArtifactData &ta2); +QBS_EXPORT bool operator!=(const ArtifactData &ta1, const ArtifactData &ta2); +QBS_EXPORT bool operator<(const ArtifactData &ta1, const ArtifactData &ta2); + +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; + QString prefix() const; + QList sourceArtifacts() const; + QList sourceArtifactsFromWildcards() const; + QList allSourceArtifacts() const; + PropertyMap properties() const; + bool isEnabled() const; + QStringList allFilePaths() const; + +private: + QExplicitlySharedDataPointer 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 ProductData +{ + friend class Internal::ProjectPrivate; +public: + ProductData(); + ProductData(const ProductData &other); + ProductData &operator=(const ProductData &other); + ~ProductData(); + + bool isValid() const; + + QStringList type() const; + QStringList dependencies() const; + QString name() const; + QString targetName() const; + QString version() const; + QString profile() const; + CodeLocation location() const; + QString buildDirectory() const; + QList generatedArtifacts() const; + QList targetArtifacts() const; + QList installableArtifacts() const; + QString targetExecutable() const; + QList groups() const; + QVariantMap properties() const; + PropertyMap moduleProperties() const; + bool isEnabled() const; + bool isRunnable() const; + +private: + QExplicitlySharedDataPointer 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 products() const; + QList subProjects() const; + QList allProducts() const; + QList installableArtifacts() const; + +private: + QExplicitlySharedDataPointer 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 00000000..3df3383e --- /dev/null +++ b/src/lib/corelib/api/projectdata_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROJECTDATA_P_H +#define QBS_PROJECTDATA_P_H + +#include "projectdata.h" + +#include + +namespace qbs { +namespace Internal { + +class InstallDataPrivate : public QSharedData +{ +public: + InstallDataPrivate() : isValid(false) {} + + QString installFilePath; + QString installRoot; + bool isValid; + bool isInstallable; +}; + +class ArtifactDataPrivate : public QSharedData +{ +public: + ArtifactDataPrivate() : isValid(false) {} + + QString filePath; + QStringList fileTags; + PropertyMap properties; + InstallData installData; + bool isValid; + bool isGenerated; + bool isTargetArtifact; +}; + +class GroupDataPrivate : public QSharedData +{ +public: + GroupDataPrivate() : isValid(false) + { } + + QString name; + QString prefix; + CodeLocation location; + QList sourceArtifacts; + QList sourceArtifactsFromWildcards; + PropertyMap properties; + bool isEnabled; + bool isValid; +}; + +class InstallableFilePrivate: public QSharedData +{ +public: + InstallableFilePrivate() : isValid(false) {} + + QString sourceFilePath; + QString targetFilePath; + QStringList fileTags; + bool isValid; +}; + +class ProductDataPrivate : public QSharedData +{ +public: + ProductDataPrivate() : isValid(false) + { } + + QStringList type; + QStringList dependencies; + QString name; + QString targetName; + QString version; + QString profile; + CodeLocation location; + QString buildDirectory; + QList groups; + QVariantMap properties; + PropertyMap moduleProperties; + QList generatedArtifacts; + bool isEnabled; + bool isRunnable; + bool isValid; +}; + +class ProjectDataPrivate : public QSharedData +{ +public: + ProjectDataPrivate() : isValid(false) + { } + + QString name; + CodeLocation location; + bool enabled; + bool isValid; + QList products; + QList 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 00000000..2881d45b --- /dev/null +++ b/src/lib/corelib/api/projectfileupdater.cpp @@ -0,0 +1,549 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "projectfileupdater.h" + +#include "projectdata.h" +#include "qmljsrewriter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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.filePath(), 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) +{ +} + +ProjectFileUpdater::LineEndingType ProjectFileUpdater::guessLineEndingType(const QByteArray &text) +{ + char before = 0; + int lfCount = 0; + int crlfCount = 0; + int i = text.indexOf('\n'); + while (i != -1) { + if (i > 0) + before = text.at(i - 1); + if (before == '\r') + ++crlfCount; + else + ++lfCount; + i = text.indexOf('\n', i + 1); + } + if (lfCount == 0 && crlfCount == 0) + return UnknownLineEndings; + if (crlfCount == 0) + return UnixLineEndings; + if (lfCount == 0) + return WindowsLineEndings; + return MixedLineEndings; +} + +void ProjectFileUpdater::convertToUnixLineEndings(QByteArray *text, LineEndingType oldLineEndings) +{ + if (oldLineEndings == UnixLineEndings) + return; + text->replace("\r\n", "\n"); +} + +void ProjectFileUpdater::convertFromUnixLineEndings(QByteArray *text, LineEndingType newLineEndings) +{ + if (newLineEndings == WindowsLineEndings + || (newLineEndings != UnixLineEndings && HostOsInfo::isWindowsHost())) { + text->replace('\n', "\r\n"); + } +} + +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())); + } + QByteArray rawContent = file.readAll(); + const LineEndingType origLineEndingType = guessLineEndingType(rawContent); + convertToUnixLineEndings(&rawContent, origLineEndingType); + QString content = QString::fromUtf8(rawContent); + + file.close(); + Engine engine; + Lexer lexer(&engine); + lexer.setCode(content, 1); + Parser parser(&engine); + if (!parser.parse()) { + QList 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); + rawContent = content.toUtf8(); + convertFromUnixLineEndings(&rawContent, origLineEndingType); + file.write(rawContent); +} + + +ProjectFileGroupInserter::ProjectFileGroupInserter(const ProductData &product, + const QString &groupName) + : ProjectFileUpdater(product.location().filePath()) + , 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 &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 QbsQmlJS::AST::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 &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().filePath()) + , m_product(product) + , m_group(group) + , m_files(files) +{ +} + +static QString &addToFilesRepr(QString &filesRepr, const QString &fileRepr, int indentation) +{ + filesRepr += QString(indentation, QLatin1Char(' ')); + filesRepr += fileRepr; + filesRepr += QLatin1String(",\n"); + return filesRepr; +} + +static QString &addToFilesRepr(QString &filesRepr, const QStringList &filePaths, int indentation) +{ + foreach (const QString &f, filePaths) + addToFilesRepr(filesRepr, toJSLiteral(f), indentation); + return filesRepr; +} + +static QString &completeFilesRepr(QString &filesRepr, int indentation) +{ + return filesRepr.prepend(QLatin1String("[\n")).append(QString(indentation, QLatin1Char(' '))) + .append(QLatin1Char(']')); +} + +void ProjectFileFilesAdder::doApply(QString &fileContent, UiProgram *ast) +{ + if (m_files.isEmpty()) + return; + QStringList sortedFiles = m_files; + sortedFiles.sort(); + + // 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; + + // 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) { + QString filesRepresentation; + if (filesBinding->statement->kind != QbsQmlJS::AST::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(filesBinding->statement); + switch (exprStatement->expression->kind) { + case QbsQmlJS::AST::Node::Kind_ArrayLiteral: { + const ElementList *elem + = static_cast(exprStatement->expression)->elements; + QStringList oldFileReprs; + while (elem) { + oldFileReprs << getNodeRepresentation(fileContent, elem->expression); + elem = elem->next; + } + + // Insert new files "sorted", but do not change the order of existing files. + const QString firstNewFileRepr = toJSLiteral(sortedFiles.first()); + while (!oldFileReprs.isEmpty()) { + if (oldFileReprs.first() > firstNewFileRepr) + break; + addToFilesRepr(filesRepresentation, oldFileReprs.takeFirst(), arrayElemIndentation); + } + addToFilesRepr(filesRepresentation, sortedFiles, arrayElemIndentation); + while (!oldFileReprs.isEmpty()) + addToFilesRepr(filesRepresentation, oldFileReprs.takeFirst(), arrayElemIndentation); + completeFilesRepr(filesRepresentation, bindingIndentation); + break; + } + case QbsQmlJS::AST::Node::Kind_StringLiteral: { + const QString existingElement + = static_cast(exprStatement->expression)->value.toString(); + sortedFiles << existingElement; + sortedFiles.sort(); + addToFilesRepr(filesRepresentation, sortedFiles, arrayElemIndentation); + completeFilesRepr(filesRepresentation, bindingIndentation); + 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); + addToFilesRepr(filesRepresentation, sortedFiles, arrayElemIndentation); + completeFilesRepr(filesRepresentation, bindingIndentation); + + // It cannot be the other way around, since the existing right-hand side could + // have string type. + filesRepresentation += QString::fromLatin1(".concat(%1)").arg(rhsRepr); + + } + } + rewriter.changeBinding(itemFinder.item()->initializer, QLatin1String("files"), + filesRepresentation, Rewriter::ScriptBinding); + } else { // Can happen for the product itself, for which the "files" binding is not mandatory. + QString filesRepresentation; + addToFilesRepr(filesRepresentation, sortedFiles, arrayElemIndentation); + completeFilesRepr(filesRepresentation, bindingIndentation); + const QString bindingString = QString(bindingIndentation, QLatin1Char(' ')) + + QLatin1String("files"); + rewriter.addBinding(itemFinder.item()->initializer, bindingString, filesRepresentation, + Rewriter::ScriptBinding); + } + + setLineOffset(getLineOffsetForChangedBinding(changeSet, + filesBinding ? getNodeRepresentation(fileContent, filesBinding->statement) + : QString())); + const int insertionLine = getBindingLine(changeSet, fileContent); + 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().filePath()) + , m_product(product) + , m_group(group) + , m_files(files) +{ +} + +void ProjectFileFilesRemover::doApply(QString &fileContent, UiProgram *ast) +{ + if (m_files.isEmpty()) + return; + + // 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 != QbsQmlJS::AST::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(bindingFinder.binding()->statement); + switch (exprStatement->expression->kind) { + case QbsQmlJS::AST::Node::Kind_ArrayLiteral: { + QStringList filesToRemove = m_files; + QStringList newFilesList; + const ElementList *elem = static_cast(exprStatement->expression)->elements; + while (elem) { + if (elem->expression->kind != QbsQmlJS::AST::Node::Kind_StringLiteral) { + throw ErrorInfo(Tr::tr("JavaScript construct in source file is too complex."), + bindingLocation); + } + const QString existingFile + = static_cast(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::fromLatin1("\"%1\",\n").arg(file); + } + filesString += QString(bindingIndentation, QLatin1Char(' ')); + filesString += QLatin1Char(']'); + rewriter.changeBinding(itemFinder.item()->initializer, QLatin1String("files"), + filesString, Rewriter::ScriptBinding); + break; + } + case QbsQmlJS::AST::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(exprStatement->expression)->value.toString(); + if (existingFile != m_files.first()) { + throw ErrorInfo(Tr::tr("File '%1' could not be found in the 'files' list.") + .arg(m_files.first()), 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().filePath()) + , 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 &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 00000000..f8bf4e18 --- /dev/null +++ b/src/lib/corelib/api/projectfileupdater.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROJECTFILEUPDATER_H +#define QBS_PROJECTFILEUPDATER_H + +#include "projectdata.h" + +#include +#include + +#include + +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; + + enum LineEndingType + { + UnknownLineEndings, + UnixLineEndings, + WindowsLineEndings, + MixedLineEndings + }; + + static LineEndingType guessLineEndingType(const QByteArray &text); + static void convertToUnixLineEndings(QByteArray *text, LineEndingType oldLineEndings); + static void convertFromUnixLineEndings(QByteArray *text, LineEndingType newLineEndings); + + 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 00000000..5f592484 --- /dev/null +++ b/src/lib/corelib/api/propertymap_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PROPERTYMAP_P_H +#define QBS_PROPERTYMAP_P_H + +#include + +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 00000000..8ce0b1e8 --- /dev/null +++ b/src/lib/corelib/api/qmljsrewriter.cpp @@ -0,0 +1,728 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmljsrewriter.h" + +#include + +#include +#include +#include + +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(member)) + lastObjectDef = iter; + else if (UiArrayBinding *arrayBinding = cast(member)) + idx = propertyOrder.indexOf(toString(arrayBinding->qualifiedId)); + else if (UiObjectBinding *objectBinding = cast(member)) + idx = propertyOrder.indexOf(toString(objectBinding->qualifiedId)); + else if (UiScriptBinding *scriptBinding = cast(member)) + idx = propertyOrder.indexOf(toString(scriptBinding->qualifiedId)); + else if (cast(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(member)) + lastObjectDef = iter; + else if (UiArrayBinding *arrayBinding = cast(member)) + idx = propertyOrder.indexOf(toString(arrayBinding->qualifiedId)); + else if (UiObjectBinding *objectBinding = cast(member)) + idx = propertyOrder.indexOf(toString(objectBinding->qualifiedId)); + else if (UiScriptBinding *scriptBinding = cast(member)) + idx = propertyOrder.indexOf(toString(scriptBinding->qualifiedId)); + else if (cast(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 orderedMembers; + + for (UiObjectMemberList *iter = members; iter; iter = iter->next) { + UiObjectMember *member = iter->member; + + if (UiArrayBinding *arrayBinding = cast(member)) + orderedMembers[toString(arrayBinding->qualifiedId)] = iter; + else if (UiObjectBinding *objectBinding = cast(member)) + orderedMembers[toString(objectBinding->qualifiedId)] = iter; + else if (cast(member)) + orderedMembers[QString::null] = iter; + else if (UiScriptBinding *scriptBinding = cast(member)) + orderedMembers[toString(scriptBinding->qualifiedId)] = iter; + else if (cast(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(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(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(propertyMember)) { + startOffset = objectBinding->qualifiedTypeNameId->identifierToken.offset; + endOffset = objectBinding->initializer->rbraceToken.end(); + } else if (UiScriptBinding *scriptBinding = AST::cast(propertyMember)) { + startOffset = scriptBinding->statement->firstSourceLocation().offset; + endOffset = scriptBinding->statement->lastSourceLocation().end(); + } else if (UiArrayBinding *arrayBinding = AST::cast(propertyMember)) { + startOffset = arrayBinding->lbracketToken.offset; + endOffset = arrayBinding->rbracketToken.end(); + } else if (UiPublicMember *publicMember = AST::cast(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(member)) + return publicMember->name == propertyName; + else if (UiObjectBinding *objectBinding = cast(member)) + return toString(objectBinding->qualifiedId) == propertyName; + else if (UiScriptBinding *scriptBinding = cast(member)) + return toString(scriptBinding->qualifiedId) == propertyName; + else if (UiArrayBinding *arrayBinding = cast(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(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(member)) + lastObjectDef = iter; + else if (UiArrayBinding *arrayBinding = cast(member)) + idx = propertyOrder.indexOf(toString(arrayBinding->qualifiedId)); + else if (UiObjectBinding *objectBinding = cast(member)) + idx = propertyOrder.indexOf(toString(objectBinding->qualifiedId)); + else if (UiScriptBinding *scriptBinding = cast(member)) + idx = propertyOrder.indexOf(toString(scriptBinding->qualifiedId)); + else if (cast(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 orderedMembers; + + for (UiObjectMemberList *iter = members; iter; iter = iter->next) { + UiObjectMember *member = iter->member; + + if (UiArrayBinding *arrayBinding = cast(member)) + orderedMembers[toString(arrayBinding->qualifiedId)] = iter; + else if (UiObjectBinding *objectBinding = cast(member)) + orderedMembers[toString(objectBinding->qualifiedId)] = iter; + else if (cast(member)) + orderedMembers[QString::null] = iter; + else if (UiScriptBinding *scriptBinding = cast(member)) + orderedMembers[toString(scriptBinding->qualifiedId)] = iter; + else if (cast(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(parent)) { + extendToLeadingOrTrailingComma(parentArray, member, start, end); + } else { + if (UiObjectDefinition *parentObjectDefinition = cast(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 00000000..901467d6 --- /dev/null +++ b/src/lib/corelib/api/qmljsrewriter.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLJSREWRITER_H +#define QMLJSREWRITER_H + +#include "changeset.h" + +#include + +#include + +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/rulecommand.cpp b/src/lib/corelib/api/rulecommand.cpp new file mode 100644 index 00000000..12c1c153 --- /dev/null +++ b/src/lib/corelib/api/rulecommand.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "rulecommand.h" +#include "rulecommand_p.h" + +#include + +namespace qbs { + +/*! + * \class RuleCommand + * \brief The \c RuleCommand class corresponds to a \c ProcessCommand or \c JavaScriptCommand + * in \QBS. + */ + +/*! + * \enum RuleCommand::Type + * This enum type represents the different kinds of commands. + * \value ProcessCommandType For the \QBS type \c ProcessCommand, which represents a command + * whose execution involves calling an executable. + * \value JavaScriptCommandType For the \QBS type \c JavaScriptCommand, which represents a command + * whose execution involves running a piece of JavaScript code inside \QBS. + * \value InvalidType Used to mark \c RuleCommand objects as invalid. + */ + + +RuleCommand::RuleCommand() : d(new Internal::RuleCommandPrivate) +{ +} + +RuleCommand::RuleCommand(const RuleCommand &other) : d(other.d) {} + +RuleCommand::~RuleCommand() +{ +} + +RuleCommand& RuleCommand::operator=(const RuleCommand &other) +{ + d = other.d; + return *this; +} + +/*! + * Returns the type of this object. If the value is \c InvalidType, the object is invalid. + */ +RuleCommand::Type RuleCommand::type() const +{ + return d->type; +} + +/*! + * Returns the human-readable description of this command that \QBS will print when + * the command executed. + */ +QString RuleCommand::description() const +{ + return d->description; +} + +/*! + * Returns the detailed description of this command that \QBS will print when + * the command is executed. + */ +QString RuleCommand::extendedDescription() const +{ + return d->extendedDescription; +} + +/*! + * Returns the source of the command if \c type() is \c JavaScriptCommandType. + * If \c type() is anything else, the behavior of this function is undefined. + */ +QString RuleCommand::sourceCode() const +{ + QBS_ASSERT(type() == JavaScriptCommandType, return QString()); + return d->sourceCode; +} + +/*! + * Returns the executable that will be called when the corresponding \c ProcessCommand + * is executed. + * If \c type() is not \c ProcessCommandType, the behavior of this function is undefined. + */ +QString RuleCommand::executable() const +{ + QBS_ASSERT(type() == ProcessCommandType, return QString()); + return d->executable; +} + +/*! + * Returns the command-line arguments of the executable that will be called when the + * corresponding \c ProcessCommand is executed. + * If \c type() is not \c ProcessCommandType, the behavior of this function is undefined. + */ +QStringList RuleCommand::arguments() const +{ + QBS_ASSERT(type() == ProcessCommandType, return QStringList()); + return d->arguments; +} + +/*! + * Returns the working directory of the executable that will be called when the + * corresponding \c ProcessCommand is executed. + * If \c type() is not \c ProcessCommandType, the behavior of this function is undefined. + */ +QString RuleCommand::workingDirectory() const +{ + QBS_ASSERT(type() == ProcessCommandType, return QString()); + return d->workingDir; +} + +/*! + * Returns the environment of the executable that will be called when the + * corresponding \c ProcessCommand is executed. + * If \c type() is not \c ProcessCommandType, the behavior of this function is undefined. + */ +QProcessEnvironment RuleCommand::environment() const +{ + QBS_ASSERT(type() == ProcessCommandType, return QProcessEnvironment()); + return d->environment; +} + +} // namespace qbs diff --git a/src/lib/corelib/api/rulecommand.h b/src/lib/corelib/api/rulecommand.h new file mode 100644 index 00000000..cca1db84 --- /dev/null +++ b/src/lib/corelib/api/rulecommand.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_RULECOMMAND_H +#define QBS_RULECOMMAND_H + +#include + +#include +#include + +QT_BEGIN_NAMESPACE +class QProcessEnvironment; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { +class ProjectPrivate; +class RuleCommandPrivate; +} + +class QBS_EXPORT RuleCommand +{ + friend class Internal::ProjectPrivate; +public: + RuleCommand(); + RuleCommand(const RuleCommand &other); + ~RuleCommand(); + RuleCommand &operator=(const RuleCommand &other); + + enum Type { ProcessCommandType, JavaScriptCommandType, InvalidType }; + + + Type type() const; + QString description() const; + QString extendedDescription() const; + QString sourceCode() const; + QString executable() const; + QStringList arguments() const; + QString workingDirectory() const; + QProcessEnvironment environment() const; + +private: + QExplicitlySharedDataPointer d; +}; + + +typedef QList RuleCommandList; + +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/api/rulecommand_p.h b/src/lib/corelib/api/rulecommand_p.h new file mode 100644 index 00000000..246e9520 --- /dev/null +++ b/src/lib/corelib/api/rulecommand_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_RULECOMMAND_P_H +#define QBS_RULECOMMAND_P_H + +#include "rulecommand.h" + +#include +#include + +namespace qbs { +namespace Internal { + +class RuleCommandPrivate : public QSharedData +{ +public: + RuleCommandPrivate(): type(RuleCommand::InvalidType) {} + + RuleCommand::Type type; + QString description; + QString extendedDescription; + QString sourceCode; + QString executable; + QStringList arguments; + QString workingDir; + QProcessEnvironment environment; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/api/runenvironment.cpp b/src/lib/corelib/api/runenvironment.cpp new file mode 100644 index 00000000..fc9525a8 --- /dev/null +++ b/src/lib/corelib/api/runenvironment.cpp @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "runenvironment.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace qbs { +using namespace Internal; + +class RunEnvironment::RunEnvironmentPrivate +{ +public: + RunEnvironmentPrivate(const ResolvedProductPtr &product, const InstallOptions &installOptions, + const QProcessEnvironment &environment, Settings *settings, const Logger &logger) + : resolvedProduct(product) + , installOptions(installOptions) + , environment(environment) + , settings(settings) + , logger(logger) + , engine(this->logger, EvalContext::PropertyEvaluation) + { + } + + const ResolvedProductPtr resolvedProduct; + InstallOptions installOptions; + const QProcessEnvironment environment; + Settings * const settings; + Logger logger; + ScriptEngine engine; +}; + +RunEnvironment::RunEnvironment(const ResolvedProductPtr &product, + const InstallOptions &installOptions, + const QProcessEnvironment &environment, Settings *settings, const Logger &logger) + : d(new RunEnvironmentPrivate(product, installOptions, environment, settings, logger)) +{ +} + +RunEnvironment::~RunEnvironment() +{ + delete d; +} + +int RunEnvironment::runShell(ErrorInfo *error) +{ + try { + return doRunShell(); + } catch (const ErrorInfo &e) { + if (error) + *error = e; + return -1; + } +} + +int RunEnvironment::runTarget(const QString &targetBin, const QStringList &arguments, + ErrorInfo *error) +{ + try { + return doRunTarget(targetBin, arguments); + } catch (const ErrorInfo &e) { + if (error) + *error = e; + return -1; + } +} + +const QProcessEnvironment RunEnvironment::runEnvironment(ErrorInfo *error) const +{ + try { + return getRunEnvironment(); + } catch (const ErrorInfo &e) { + if (error) + *error = e; + return QProcessEnvironment(); + } +} + +const QProcessEnvironment RunEnvironment::buildEnvironment(ErrorInfo *error) const +{ + try { + return getBuildEnvironment(); + } catch (const ErrorInfo &e) { + if (error) + *error = e; + return QProcessEnvironment(); + } +} + +int RunEnvironment::doRunShell() +{ + 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 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 += QLatin1String(" --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()); +} + +static QString findExecutable(const QStringList &fileNames) +{ + const QStringList path = QString::fromLocal8Bit(qgetenv("PATH")) + .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + + foreach (const QString &fileName, fileNames) { + const QString exeFileName = HostOsInfo::appendExecutableSuffix(fileName); + foreach (const QString &ppath, path) { + const QString fullPath = ppath + QLatin1Char('/') + exeFileName; + QFileInfo fi(fullPath); + if (fi.exists() && fi.isFile() && fi.isExecutable()) + return QDir::cleanPath(fullPath); + } + } + return QString(); +} + +int RunEnvironment::doRunTarget(const QString &targetBin, const QStringList &arguments) +{ + const QStringList targetOS = PropertyFinder().propertyValue( + d->resolvedProduct->moduleProperties->value(), + QLatin1String("qbs"), + QLatin1String("targetOS")).toStringList(); + + const QStringList toolchain = PropertyFinder().propertyValue( + d->resolvedProduct->moduleProperties->value(), + QLatin1String("qbs"), + QLatin1String("toolchain")).toStringList(); + + QString targetExecutable = targetBin; + QStringList targetArguments = arguments; + const QString completeSuffix = QFileInfo(targetBin).completeSuffix(); + + if (targetOS.contains(QLatin1String("ios"))) { + QString bundlePath = d->resolvedProduct->buildDirectory(); + const bool install = PropertyFinder().propertyValue( + d->resolvedProduct->moduleProperties->value(), + QLatin1String("qbs"), + QLatin1String("install")).toBool(); + if (install) { + bundlePath = d->installOptions.installRoot(); + const QString installDir = PropertyFinder().propertyValue( + d->resolvedProduct->moduleProperties->value(), + QLatin1String("qbs"), + QLatin1String("installDir")).toString(); + bundlePath += QLatin1Char('/') + installDir; + } + + const QString bundleName = PropertyFinder().propertyValue( + d->resolvedProduct->moduleProperties->value(), + QLatin1String("bundle"), + QLatin1String("bundleName")).toString(); + bundlePath += QLatin1Char('/') + bundleName; + + QBS_CHECK(targetExecutable.startsWith(bundlePath)); + + if (QFileInfo(targetExecutable = findExecutable(QStringList() + << QStringLiteral("iostool"))).isExecutable()) { + targetArguments = QStringList() + << QStringLiteral("-run") + << QStringLiteral("-bundle") + << QDir::cleanPath(bundlePath); + if (!arguments.isEmpty()) + targetArguments << QStringLiteral("-extra-args") << arguments; + } else if (QFileInfo(targetExecutable = findExecutable(QStringList() + << QStringLiteral("ios-deploy"))).isExecutable()) { + targetArguments = QStringList() + << QStringLiteral("--no-wifi") + << QStringLiteral("--noninteractive") + << QStringLiteral("--bundle") + << QDir::cleanPath(bundlePath); + if (!arguments.isEmpty()) + targetArguments << QStringLiteral("--args") << arguments; + } else { + d->logger.qbsLog(LoggerError) + << Tr::tr("No suitable iOS deployment tools were found in the environment. " + "Consider installing ios-deploy."); + return EXIT_FAILURE; + } + } else 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"); + } + } + + if (toolchain.contains(QLatin1String("mono"))) { + targetArguments.prepend(targetExecutable); + targetExecutable = QLatin1String("mono"); + } + + if (completeSuffix == QLatin1String("js")) { + // The Node.js binary is called nodejs on Debian/Ubuntu-family operating systems due to a + // conflict with another package containing a binary named node + targetExecutable = findExecutable(QStringList() + << QLatin1String("nodejs") + << QLatin1String("node")); + targetArguments.prepend(targetBin); + } + + // Only check if the target is executable if we're not running it through another + // known application such as msiexec or wine, as we can't check in this case anyways + QFileInfo fi(targetExecutable); + if (targetBin == targetExecutable && (!fi.isFile() || !fi.isExecutable())) { + d->logger.qbsLog(LoggerError) << Tr::tr("File '%1' is not an executable.") + .arg(QDir::toNativeSeparators(targetExecutable)); + return EXIT_FAILURE; + } + + QProcessEnvironment env = d->environment; + env.insert(QLatin1String("QBS_RUN_FILE_PATH"), targetBin); + d->resolvedProduct->setupRunEnvironment(&d->engine, env); + + 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); + if (!process.waitForFinished(-1)) { + if (process.error() == QProcess::FailedToStart) { + QString errorPrefixString; +#ifdef Q_OS_UNIX + if (QFileInfo(targetExecutable).isExecutable()) { + const QString interpreter(shellInterpreter(targetExecutable)); + if (!interpreter.isEmpty()) { + errorPrefixString = Tr::tr("%1: bad interpreter: ").arg(interpreter); + } + } +#endif + throw ErrorInfo(Tr::tr("The process '%1' could not be started: %2") + .arg(targetExecutable) + .arg(errorPrefixString + process.errorString())); + } else { + d->logger.qbsWarning() + << "QProcess error: " << process.errorString(); + } + + return EXIT_FAILURE; + } + return process.exitCode(); +} + +const QProcessEnvironment RunEnvironment::getRunEnvironment() const +{ + if (!d->resolvedProduct) + return d->environment; + d->resolvedProduct->setupRunEnvironment(&d->engine, d->environment); + return d->resolvedProduct->runEnvironment; +} + +const QProcessEnvironment RunEnvironment::getBuildEnvironment() const +{ + if (!d->resolvedProduct) + return d->environment; + d->resolvedProduct->setupBuildEnvironment(&d->engine, d->environment); + return d->resolvedProduct->buildEnvironment; +} + +} // namespace qbs diff --git a/src/lib/corelib/api/runenvironment.h b/src/lib/corelib/api/runenvironment.h new file mode 100644 index 00000000..6c33dd67 --- /dev/null +++ b/src/lib/corelib/api/runenvironment.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_RUNENVIRONMENT_H +#define QBS_RUNENVIRONMENT_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QProcessEnvironment; +QT_END_NAMESPACE + +namespace qbs { +class ErrorInfo; +class InstallOptions; +class Settings; + +namespace Internal { +class Logger; +class ResolvedProduct; +} // namespace Internal + +class QBS_EXPORT RunEnvironment +{ + friend class CommandLineFrontend; + friend class Project; +public: + ~RunEnvironment(); + + int runShell(ErrorInfo *error = nullptr); + int runTarget(const QString &targetBin, const QStringList &arguments, + ErrorInfo *error = nullptr); + + const QProcessEnvironment runEnvironment(ErrorInfo *error = nullptr) const; + const QProcessEnvironment buildEnvironment(ErrorInfo *error = nullptr) const; + +private: + RunEnvironment(const Internal::ResolvedProductPtr &product, + const InstallOptions &installOptions, + const QProcessEnvironment &environment, Settings *settings, + const Internal::Logger &logger); + + int doRunShell(); + int doRunTarget(const QString &targetBin, const QStringList &arguments); + + const QProcessEnvironment getRunEnvironment() const; + const QProcessEnvironment getBuildEnvironment() const; + + 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 00000000..cacea2d9 --- /dev/null +++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractcommandexecutor.h" + +#include "command.h" + +#include +#include + +namespace qbs { +namespace Internal { + +AbstractCommandExecutor::AbstractCommandExecutor(const Logger &logger, QObject *parent) + : QObject(parent) + , m_echoMode(defaultCommandEchoMode()) + , 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; + doSetup(); + doReportCommandDescription(); + doStart(); +} + +void AbstractCommandExecutor::doReportCommandDescription() +{ + if (m_command->isSilent() || m_echoMode == CommandEchoModeSilent) + return; + + 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()); + } +} + +} // 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 00000000..e70dbbb8 --- /dev/null +++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ABSTRACTCOMMANDEXECUTOR_H +#define QBS_ABSTRACTCOMMANDEXECUTOR_H + +#include +#include +#include + +#include + +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; } + void setEchoMode(CommandEchoMode echoMode) { m_echoMode = echoMode; } + + virtual void cancel() = 0; + + void start(Transformer *transformer, const AbstractCommand *cmd); + +signals: + void reportCommandDescription(const QString &highlight, const QString &message); + void finished(const qbs::ErrorInfo &err = ErrorInfo()); // !hasError() <=> command successful + +protected: + virtual void doReportCommandDescription(); + 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; } + CommandEchoMode m_echoMode; + +private: + virtual void doSetup() { }; + 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 00000000..a00d7b62 --- /dev/null +++ b/src/lib/corelib/buildgraph/artifact.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "artifact.h" + +#include "transformer.h" +#include "buildgraphvisitor.h" +#include "productbuilddata.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static QDataStream &operator >>(QDataStream &s, qbs::Internal::Artifact::ArtifactType &t) +{ + int i; + s >> i; + t = static_cast(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() +{ + for (Artifact *p : parentArtifacts()) + p->childrenAddedByScanner.remove(this); +} + +void Artifact::accept(BuildGraphVisitor *visitor) +{ + if (visitor->visit(this)) + acceptChildren(visitor); + visitor->endVisit(this); +} + +QString Artifact::toString() const +{ + return QLatin1String("ARTIFACT ") + filePath() + QLatin1String(" [") + + (!product.isNull() ? product->name : QLatin1String("")) + QLatin1Char(']'); +} + +void Artifact::addFileTag(const FileTag &t) +{ + m_fileTags += t; + if (!product.isNull() && product->buildData) + product->buildData->artifactsByFileTag[t] += this; +} + +void Artifact::removeFileTag(const FileTag &t) +{ + m_fileTags -= t; + if (!product.isNull() && product->buildData) + removeArtifactFromSetByFileTag(this, t, product->buildData->artifactsByFileTag); +} + +void Artifact::setFileTags(const FileTags &newFileTags) +{ + if (product.isNull() || !product->buildData) { + m_fileTags = newFileTags; + return; + } + foreach (const FileTag &t, m_fileTags) + removeArtifactFromSetByFileTag(this, t, product->buildData->artifactsByFileTag); + m_fileTags = newFileTags; + addArtifactToSet(this, product->buildData->artifactsByFileTag); +} + +void Artifact::initialize() +{ + artifactType = Unknown; + inputsScanned = false; + timestampRetrieved = false; + alwaysUpdated = true; + oldDataPossiblyPresent = true; +} + +const TypeFilter Artifact::parentArtifacts() const +{ + return TypeFilter(parents); +} + +const TypeFilter Artifact::childArtifacts() const +{ + return TypeFilter(children); +} + +void Artifact::onChildDisconnected(BuildGraphNode *child) +{ + Artifact *childArtifact = dynamic_cast(child); + if (!childArtifact) + return; + childrenAddedByScanner.remove(childArtifact); +} + +void Artifact::load(PersistentPool &pool) +{ + FileResourceBase::load(pool); + BuildGraphNode::load(pool); + children.load(pool); + + // restore parents of the loaded children + for (NodeSet::const_iterator it = children.constBegin(); it != children.constEnd(); ++it) + (*it)->parents.insert(this); + + pool.loadContainer(childrenAddedByScanner); + pool.loadContainer(fileDependencies); + properties = pool.idLoadS(); + transformer = pool.idLoadS(); + m_fileTags.load(pool); + unsigned char c; + pool.stream() + >> artifactType + >> c; + alwaysUpdated = c; + pool.stream() >> c; + oldDataPossiblyPresent = c; +} + +void Artifact::store(PersistentPool &pool) const +{ + FileResourceBase::store(pool); + BuildGraphNode::store(pool); + // Do not store parents to avoid recursion. + children.store(pool); + pool.storeContainer(childrenAddedByScanner); + pool.storeContainer(fileDependencies); + pool.store(properties); + pool.store(transformer); + m_fileTags.store(pool); + pool.stream() + << artifactType + << static_cast(alwaysUpdated) + << static_cast(oldDataPossiblyPresent); +} + +} // 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 00000000..4567847a --- /dev/null +++ b/src/lib/corelib/buildgraph/artifact.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ARTIFACT_H +#define QBS_ARTIFACT_H + +#include "artifactset.h" +#include "filedependency.h" +#include "buildgraphnode.h" +#include "forward_decls.h" +#include +#include + +#include +#include + +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 BuildGraphNode +{ +public: + Artifact(); + ~Artifact(); + + Type type() const { return ArtifactNodeType; } + void accept(BuildGraphVisitor *visitor); + QString toString() const; + + void addFileTag(const FileTag &t); + void removeFileTag(const FileTag &t); + void setFileTags(const FileTags &newFileTags); + const FileTags &fileTags() const { return m_fileTags; } + + ArtifactSet childrenAddedByScanner; + QSet fileDependencies; + TransformerPtr transformer; + PropertyMapPtr properties; + + enum ArtifactType + { + Unknown = 1, + SourceFile = 2, + Generated = 4 + }; + + ArtifactType artifactType; + 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; + bool oldDataPossiblyPresent : 1; + + void initialize(); + const TypeFilter parentArtifacts() const; + const TypeFilter childArtifacts() const; + void onChildDisconnected(BuildGraphNode *child); + +private: + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + + FileTags m_fileTags; +}; + +// 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(BuildGraphNode::BuildState s) +{ + switch (s) { + case BuildGraphNode::Untouched: + return QLatin1String("Untouched"); + case BuildGraphNode::Buildable: + return QLatin1String("Buildable"); + case BuildGraphNode::Building: + return QLatin1String("Building"); + case BuildGraphNode::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 00000000..3ea02403 --- /dev/null +++ b/src/lib/corelib/buildgraph/artifactcleaner.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "artifactcleaner.h" + +#include "artifact.h" +#include "artifactvisitor.h" +#include "productbuilddata.h" +#include "projectbuilddata.h" +#include "transformer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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::fileExists(fileInfo)) { + 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 ProgressObserver *observer, + const Logger &logger) + : ArtifactVisitor(Artifact::Generated) + , m_options(options) + , m_observer(observer) + , m_logger(logger) + , m_hasError(false) + { + } + + void visitProduct(const ResolvedProductPtr &product) + { + m_product = product; + ArtifactVisitor::visitProduct(product); + auto it = product->buildData->rescuableArtifactData.begin(); + while (it != product->buildData->rescuableArtifactData.end()) { + Artifact tmp; + tmp.product = product; + tmp.setFilePath(it.key()); + tmp.setTimestamp(it.value().timeStamp); + removeArtifactFromDisk(&tmp, m_options.dryRun(), m_logger); + it = product->buildData->rescuableArtifactData.erase(it); + } + } + + const QSet &directories() const { return m_directories; } + bool hasError() const { return m_hasError; } + +private: + void doVisit(Artifact *artifact) + { + if (m_observer->canceled()) + throw ErrorInfo(Tr::tr("Cleaning up was canceled.")); + + if (artifact->product != m_product) + 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; + const ProgressObserver * const m_observer; + Logger m_logger; + bool m_hasError; + ResolvedProductConstPtr m_product; + QSet m_directories; +}; + +ArtifactCleaner::ArtifactCleaner(const Logger &logger, ProgressObserver *observer) + : m_logger(logger), m_observer(observer) +{ +} + +void ArtifactCleaner::cleanup(const TopLevelProjectPtr &project, + const QList &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 directories; + foreach (const ResolvedProductPtr &product, products) { + CleanupVisitor visitor(options, m_observer, 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 dirList = directories.toList(); + for (int i = 0; i < dirList.count(); ++i) { + const QString &dir = dirList.at(i); + if (!dir.startsWith(project->buildDirectory)) + continue; + if (FileInfo(dir).exists()) + removeEmptyDirectories(dir, options); + if (dir != project->buildDirectory) { + const QString parentDir = QDir::cleanPath(dir + QLatin1String("/..")); + 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::Hidden | 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 00000000..510a7467 --- /dev/null +++ b/src/lib/corelib/buildgraph/artifactcleaner.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_ARTIFACTCLEANER_H +#define QBS_ARTIFACTCLEANER_H + +#include + +#include +#include + +namespace qbs { +class CleanOptions; + +namespace Internal { +class ProgressObserver; + +class ArtifactCleaner +{ +public: + ArtifactCleaner(const Logger &logger, ProgressObserver *observer); + void cleanup(const TopLevelProjectPtr &project, const QList &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/artifactset.cpp b/src/lib/corelib/buildgraph/artifactset.cpp new file mode 100644 index 00000000..b2481fa4 --- /dev/null +++ b/src/lib/corelib/buildgraph/artifactset.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "artifactset.h" +#include "artifact.h" + +namespace qbs { +namespace Internal { + +ArtifactSet::ArtifactSet() +{ +} + +ArtifactSet::ArtifactSet(const ArtifactSet &other) + : QSet(other) +{ +} + +ArtifactSet::ArtifactSet(const QSet &other) + : QSet(other) +{ +} + +ArtifactSet &ArtifactSet::unite(const ArtifactSet &other) +{ + QSet::unite(other); + return *this; +} + +QStringList ArtifactSet::toStringList() const +{ + QStringList sl; + foreach (Artifact *a, *this) + sl += a->filePath(); + return sl; +} + +QString ArtifactSet::toString() const +{ + return QLatin1Char('[') + toStringList().join(QLatin1String(", ")) + QLatin1Char(']'); +} + +ArtifactSet ArtifactSet::fromNodeSet(const NodeSet &nodes) +{ + ArtifactSet result; + result.reserve(nodes.count()); + foreach (BuildGraphNode *node, nodes) { + Artifact *artifact = dynamic_cast(node); + if (artifact) + result += artifact; + } + return result; +} + +ArtifactSet ArtifactSet::fromNodeList(const QList &lst) +{ + ArtifactSet result; + result.reserve(lst.count()); + for (QList::const_iterator it = lst.constBegin(); it != lst.end(); ++it) + result.insert(*it); + return result; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/buildgraph/artifactset.h b/src/lib/corelib/buildgraph/artifactset.h new file mode 100644 index 00000000..0b084ec5 --- /dev/null +++ b/src/lib/corelib/buildgraph/artifactset.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ARTIFACTSET_H +#define QBS_ARTIFACTSET_H + +#include +#include + +namespace qbs { +namespace Internal { + +class Artifact; +class NodeSet; + +class ArtifactSet : public QSet +{ +public: + ArtifactSet(); + ArtifactSet(const ArtifactSet &other); + ArtifactSet(const QSet &other); + + ArtifactSet &unite(const ArtifactSet &other); + QStringList toStringList() const; + QString toString() const; + + static ArtifactSet fromNodeSet(const NodeSet &nodes); + static ArtifactSet fromNodeList(const QList &lst); +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_ARTIFACTSET_H diff --git a/src/lib/corelib/buildgraph/artifactvisitor.cpp b/src/lib/corelib/buildgraph/artifactvisitor.cpp new file mode 100644 index 00000000..dcd4454e --- /dev/null +++ b/src/lib/corelib/buildgraph/artifactvisitor.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "artifactvisitor.h" + +#include "artifact.h" +#include "productbuilddata.h" +#include +#include + +namespace qbs { +namespace Internal { + +ArtifactVisitor::ArtifactVisitor(int artifactType) : m_artifactType(artifactType) +{ +} + +void ArtifactVisitor::visitProduct(const ResolvedProductConstPtr &product) +{ + if (!product->buildData) + return; + foreach (BuildGraphNode *node, product->buildData->nodes) + node->accept(this); +} + +void ArtifactVisitor::visitProject(const ResolvedProjectConstPtr &project) +{ + foreach (const ResolvedProductConstPtr &product, project->allProducts()) + visitProduct(product); +} + +bool ArtifactVisitor::visit(RuleNode *ruleNode) +{ + Q_UNUSED(ruleNode); + return false; +} + +bool ArtifactVisitor::visit(Artifact *artifact) +{ + QBS_CHECK(artifact); + if (m_artifactType & artifact->artifactType) + doVisit(artifact); + return false; +} + +} // 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 00000000..e0b68d9e --- /dev/null +++ b/src/lib/corelib/buildgraph/artifactvisitor.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_ARTIFACTVISITOR_H +#define QBS_ARTIFACTVISITOR_H + +#include "forward_decls.h" + +#include "buildgraphvisitor.h" +#include + +#include +#include + +namespace qbs { +namespace Internal { + +class ArtifactVisitor : public BuildGraphVisitor +{ +public: + ArtifactVisitor(int artifactType); + + void visitProduct(const ResolvedProductConstPtr &product); + void visitProject(const ResolvedProjectConstPtr &project); + bool visit(RuleNode *ruleNode); + bool visit(Artifact *artifact); + +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/buildgraph.cpp b/src/lib/corelib/buildgraph/buildgraph.cpp new file mode 100644 index 00000000..3929f676 --- /dev/null +++ b/src/lib/corelib/buildgraph/buildgraph.cpp @@ -0,0 +1,552 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "buildgraph.h" + +#include "artifact.h" +#include "cycledetector.h" +#include "projectbuilddata.h" +#include "productbuilddata.h" +#include "transformer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { + +static QScriptValue setupProjectScriptValue(ScriptEngine *engine, + const ResolvedProjectConstPtr &project, PrepareScriptObserver *observer) +{ + QScriptValue obj = engine->newObject(); + obj.setProperty(QLatin1String("filePath"), project->location.filePath()); + obj.setProperty(QLatin1String("path"), FileInfo::path(project->location.filePath())); + const QVariantMap &projectProperties = project->projectProperties(); + for (QVariantMap::const_iterator it = projectProperties.begin(); + it != projectProperties.end(); ++it) { + engine->setObservedProperty(obj, it.key(), engine->toScriptValue(it.value()), observer); + } + return obj; +} + +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, + (void *)product.data()); + setProduct(depfunc, product.data()); + productScriptValue.setProperty(QLatin1String("dependencies"), depfunc, + QScriptValue::ReadOnly | QScriptValue::Undeletable + | QScriptValue::PropertyGetter); + } + +private: + static QScriptValue js_productDependencies(QScriptContext *, QScriptEngine *engine, void *arg) + { + const ResolvedProduct * const product = static_cast(arg); + QScriptValue result = engine->newArray(); + quint32 idx = 0; + QList productDeps = product->dependencies.toList(); + std::sort(productDeps.begin(), productDeps.end(), + [](const ResolvedProductPtr &p1, const ResolvedProductPtr &p2) { + return p1->name < p2->name; + }); + foreach (const ResolvedProductPtr &dependency, productDeps) { + QScriptValue obj = engine->newObject(); + setupProductScriptValue(static_cast(engine), obj, dependency, 0); + result.setProperty(idx++, obj); + } + foreach (const ResolvedModuleConstPtr &dependency, product->modules) { + QScriptValue obj = engine->newObject(); + setupModuleScriptValue(static_cast(engine), obj, + product->moduleProperties->value(), dependency->name); + result.setProperty(idx++, obj); + } + return result; + } + + static QScriptValue js_moduleDependencies(QScriptContext *, QScriptEngine *engine, void *arg) + { + const QVariantMap *modulesMap = static_cast(arg); + 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(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)); + } + QVariantMap *modulesMap = new QVariantMap(propMap.value(QLatin1String("modules")).toMap()); + engine->registerOwnedVariantMap(modulesMap); + QScriptValue depfunc = engine->newFunction(&js_moduleDependencies, modulesMap); + moduleScriptValue.setProperty(QLatin1String("dependencies"), depfunc, + QScriptValue::ReadOnly | QScriptValue::Undeletable + | QScriptValue::PropertyGetter); + } + + static void setProduct(QScriptValue scriptValue, const ResolvedProduct *product) + { + attachPointerTo(scriptValue, product); + } + + static const ResolvedProduct *getProduct(const QScriptValue &scriptValue) + { + return attachedPointer(scriptValue); + } + + 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->productProperties; + for (QVariantMap::ConstIterator it = propMap.constBegin(); it != propMap.constEnd(); ++it) { + engine->setObservedProperty(productScriptValue, it.key(), engine->toScriptValue(it.value()), + observer); + } +} + +void setupScriptEngineForFile(ScriptEngine *engine, const ResolvedFileContextConstPtr &fileContext, + QScriptValue targetObject) +{ + engine->import(fileContext, targetObject); + JsExtensions::setupExtensions(fileContext->jsExtensions(), targetObject); +} + +void setupScriptEngineForProduct(ScriptEngine *engine, const ResolvedProductConstPtr &product, + const ResolvedModuleConstPtr &module, QScriptValue targetObject, + PrepareScriptObserver *observer) +{ + QScriptValue projectScriptValue = setupProjectScriptValue(engine, product->project, observer); + targetObject.setProperty(QLatin1String("project"), projectScriptValue); + if (observer) + observer->setProjectObjectId(projectScriptValue.objectId()); + + { + QVariant v; + v.setValue(&product->buildEnvironment); + engine->setProperty("_qbs_procenv", v); + } + QScriptValue productScriptValue = engine->newObject(); + setupProductScriptValue(engine, productScriptValue, product, observer); + targetObject.setProperty(QLatin1String("product"), productScriptValue); + + // If the Rule is in a Module, set up the 'moduleName' property + productScriptValue.setProperty(QLatin1String("moduleName"), + module->name.isEmpty() ? QScriptValue() : module->name); +} + +bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList &path) +{ + if (u == v) { + path.append(v); + return true; + } + + for (NodeSet::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(BuildGraphNode *p, BuildGraphNode *c) +{ + QBS_CHECK(p != c); + if (Artifact *ac = dynamic_cast(c)) { + for (const Artifact *child : filterByType(p->children)) { + if (child != ac && child->filePath() == ac->filePath()) { + throw ErrorInfo(QString::fromLatin1("%1 already has a child artifact %2 as " + "different object.").arg(p->toString(), + ac->filePath()), + CodeLocation(), true); + } + } + } + p->children.insert(c); + c->parents.insert(p); + p->product->topLevelProject()->buildData->isDirty = true; +} + +void loggedConnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger) +{ + QBS_CHECK(u != v); + if (logger.traceEnabled()) { + logger.qbsTrace() << QString::fromLatin1("[BG] connect '%1' -> '%2'") + .arg(u->toString(), v->toString()); + } + connect(u, v); +} + +static bool existsPath_impl(BuildGraphNode *u, BuildGraphNode *v, QSet *seen) +{ + if (u == v) + return true; + + if (seen->contains(u)) + return false; + + seen->insert(u); + for (NodeSet::const_iterator it = u->children.begin(); it != u->children.end(); ++it) + if (existsPath_impl(*it, v, seen)) + return true; + + return false; +} + +static bool existsPath(BuildGraphNode *u, BuildGraphNode *v) +{ + QSet seen; + return existsPath_impl(u, v, &seen); +} + +static QStringList toStringList(const QList &path) +{ + QStringList lst; + foreach (BuildGraphNode *node, path) + lst << node->toString(); + return lst; +} + +bool safeConnect(Artifact *u, Artifact *v, const Logger &logger) +{ + QBS_CHECK(u != v); + if (logger.traceEnabled()) { + logger.qbsTrace() << QString::fromLatin1("[BG] safeConnect: '%1' '%2'") + .arg(relativeArtifactFileName(u), relativeArtifactFileName(v)); + } + + if (existsPath(v, u)) { + QList circle; + findPath(v, u, circle); + logger.qbsTrace() << "[BG] safeConnect: circle detected " << toStringList(circle); + return false; + } + + connect(u, v); + return true; +} + +void disconnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger) +{ + if (logger.traceEnabled()) { + logger.qbsTrace() << QString::fromLatin1("[BG] disconnect: '%1' '%2'") + .arg(u->toString(), v->toString()); + } + u->children.remove(v); + v->parents.remove(u); + u->onChildDisconnected(v); +} + +void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger) +{ + if (artifact->artifactType != Artifact::Generated) + return; + removeGeneratedArtifactFromDisk(artifact->filePath(), logger); +} + +void removeGeneratedArtifactFromDisk(const QString &filePath, const Logger &logger) +{ + QFile file(filePath); + if (!file.exists()) + return; + logger.qbsDebug() << "removing " << filePath; + if (!file.remove()) + logger.qbsWarning() << QString::fromLatin1("Cannot remove '%1'.").arg(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(QLatin1Char('/'))) + str.remove(0, 1); + return str; +} + +Artifact *lookupArtifact(const ResolvedProductConstPtr &product, + const ProjectBuildData *projectBuildData, const QString &dirPath, const QString &fileName, + bool compareByName) +{ + const QList lookupResults + = projectBuildData->lookupFiles(dirPath, fileName); + for (QList::const_iterator it = lookupResults.constBegin(); + it != lookupResults.constEnd(); ++it) { + Artifact *artifact = dynamic_cast(*it); + if (artifact && (compareByName + ? artifact->product->uniqueName() == product->uniqueName() + : 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->setFileTags(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->nodes.contains(artifact)); + artifact->product = product; + product->topLevelProject()->buildData->insertIntoLookupTable(artifact); + product->topLevelProject()->buildData->isDirty = true; + product->buildData->nodes.insert(artifact); + addArtifactToSet(artifact, product->buildData->artifactsByFileTag); + + if (logger.traceEnabled()) { + logger.qbsTrace() << QString::fromLatin1("[BG] insert artifact '%1'") + .arg(artifact->filePath()); + } +} + +static void doSanityChecksForProduct(const ResolvedProductConstPtr &product, + const QSet &allProducts, const Logger &logger) +{ + logger.qbsTrace() << "Sanity checking product '" << product->uniqueName() << "'"; + 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 (BuildGraphNode * const node, buildData->roots) { + if (logger.traceEnabled()) + logger.qbsTrace() << "Checking root node '" << node->toString() << "'."; + QBS_CHECK(buildData->nodes.contains(node)); + } + QSet filePaths; + foreach (BuildGraphNode * const node, buildData->nodes) { + logger.qbsTrace() << "Sanity checking node '" << node->toString() << "'"; + QBS_CHECK(node->product == product); + foreach (const BuildGraphNode * const parent, node->parents) + QBS_CHECK(parent->children.contains(node)); + foreach (BuildGraphNode * const child, node->children) { + QBS_CHECK(child->parents.contains(node)); + QBS_CHECK(!child->product.isNull()); + QBS_CHECK(!child->product->buildData.isNull()); + QBS_CHECK(child->product->buildData->nodes.contains(child)); + QBS_CHECK(allProducts.contains(child->product)); + } + + Artifact * const artifact = dynamic_cast(node); + if (!artifact) + continue; + + QBS_CHECK(!filePaths.contains(artifact->filePath())); + filePaths << artifact->filePath(); + + 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)); + logger.qbsTrace() << "The transformer has " << transformer->outputs.count() + << " outputs."; + ArtifactSet transformerOutputChildren; + foreach (const Artifact * const output, transformer->outputs) { + QBS_CHECK(output->transformer == transformer); + transformerOutputChildren.unite(ArtifactSet::fromNodeSet(output->children)); + QSet childFilePaths; + for (const Artifact *a : filterByType(output->children)) { + if (childFilePaths.contains(a->filePath())) { + throw ErrorInfo(QString::fromLatin1("There is more than one artifact for " + "file '%1' in the child list for output '%2'.") + .arg(a->filePath(), output->filePath()), CodeLocation(), true); + } + childFilePaths << a->filePath(); + } + } + 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, + const QSet &allProducts, QSet &productNames, + const Logger &logger) +{ + logger.qbsDebug() << "Sanity checking project '" << project->name << "'"; + foreach (const ResolvedProjectPtr &subProject, project->subProjects) + doSanityChecks(subProject, allProducts, productNames, logger); + + foreach (const ResolvedProductConstPtr &product, project->products) { + QBS_CHECK(product->project == project); + QBS_CHECK(product->topLevelProject() == project->topLevelProject()); + doSanityChecksForProduct(product, allProducts, logger); + QBS_CHECK(!productNames.contains(product->uniqueName())); + productNames << product->uniqueName(); + } +} + +static bool isReleaseBuild() +{ +#ifdef QT_NO_DEBUG + return true; +#else + return false; +#endif +} + +void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger) +{ + if (isReleaseBuild() && qgetenv("QBS_SANITY_CHECKS").isEmpty()) + return; + QSet productNames; + const QSet allProducts = project->allProducts().toSet(); + doSanityChecks(project, allProducts, 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 00000000..155dfe06 --- /dev/null +++ b/src/lib/corelib/buildgraph/buildgraph.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BUILDGRAPH_H +#define QBS_BUILDGRAPH_H + +#include "forward_decls.h" +#include + +#include +#include + +namespace qbs { +namespace Internal { +class BuildGraphNode; +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 dumpProductBuildData(const ResolvedProductConstPtr &product); + + +bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList &path); +void connect(BuildGraphNode *p, BuildGraphNode *c); +void loggedConnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger); +bool safeConnect(Artifact *u, Artifact *v, const Logger &logger); +void removeGeneratedArtifactFromDisk(Artifact *artifact, const Logger &logger); +void removeGeneratedArtifactFromDisk(const QString &filePath, const Logger &logger); + +void disconnect(BuildGraphNode *u, BuildGraphNode *v, const Logger &logger); + +void setupScriptEngineForFile(ScriptEngine *engine, const ResolvedFileContextConstPtr &fileContext, + QScriptValue targetObject); +void setupScriptEngineForProduct(ScriptEngine *engine, const ResolvedProductConstPtr &product, + const ResolvedModuleConstPtr &module, QScriptValue targetObject, + PrepareScriptObserver *observer = 0); +QString relativeArtifactFileName(const Artifact *artifact); // Debugging helpers + +void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger); + +} // 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 00000000..8fe388d9 --- /dev/null +++ b/src/lib/corelib/buildgraph/buildgraph.pri @@ -0,0 +1,82 @@ +include(../../../install_prefix.pri) + +SOURCES += \ + $$PWD/abstractcommandexecutor.cpp \ + $$PWD/artifact.cpp \ + $$PWD/artifactcleaner.cpp \ + $$PWD/artifactset.cpp \ + $$PWD/artifactvisitor.cpp \ + $$PWD/buildgraph.cpp \ + $$PWD/buildgraphloader.cpp \ + $$PWD/buildgraphnode.cpp \ + $$PWD/command.cpp \ + $$PWD/cycledetector.cpp \ + $$PWD/depscanner.cpp \ + $$PWD/emptydirectoriesremover.cpp \ + $$PWD/executor.cpp \ + $$PWD/executorjob.cpp \ + $$PWD/filedependency.cpp \ + $$PWD/inputartifactscanner.cpp \ + $$PWD/jscommandexecutor.cpp \ + $$PWD/nodeset.cpp \ + $$PWD/nodetreedumper.cpp \ + $$PWD/processcommandexecutor.cpp \ + $$PWD/productbuilddata.cpp \ + $$PWD/productinstaller.cpp \ + $$PWD/projectbuilddata.cpp \ + $$PWD/qtmocscanner.cpp \ + $$PWD/rescuableartifactdata.cpp \ + $$PWD/rulegraph.cpp \ + $$PWD/rulenode.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/artifactset.h \ + $$PWD/artifactvisitor.h \ + $$PWD/buildgraph.h \ + $$PWD/buildgraphloader.h \ + $$PWD/buildgraphnode.h \ + $$PWD/buildgraphvisitor.h \ + $$PWD/command.h \ + $$PWD/cycledetector.h \ + $$PWD/depscanner.h \ + $$PWD/emptydirectoriesremover.h \ + $$PWD/executor.h \ + $$PWD/executorjob.h \ + $$PWD/filedependency.h \ + $$PWD/forward_decls.h \ + $$PWD/inputartifactscanner.h \ + $$PWD/jscommandexecutor.h \ + $$PWD/nodeset.h \ + $$PWD/nodetreedumper.h \ + $$PWD/processcommandexecutor.h \ + $$PWD/productbuilddata.h \ + $$PWD/productinstaller.h \ + $$PWD/projectbuilddata.h \ + $$PWD/qtmocscanner.h \ + $$PWD/rescuableartifactdata.h \ + $$PWD/rulegraph.h \ + $$PWD/rulenode.h \ + $$PWD/rulesapplicator.h \ + $$PWD/rulesevaluationcontext.h \ + $$PWD/scanresultcache.h \ + $$PWD/timestampsupdater.h \ + $$PWD/transformer.h + +qbs_enable_unit_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 00000000..89eeca28 --- /dev/null +++ b/src/lib/corelib/buildgraph/buildgraphloader.cpp @@ -0,0 +1,879 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "buildgraphloader.h" + +#include "artifact.h" +#include "artifactset.h" +#include "buildgraph.h" +#include "command.h" +#include "cycledetector.h" +#include "emptydirectoriesremover.h" +#include "productbuilddata.h" +#include "projectbuilddata.h" +#include "rulesevaluationcontext.h" +#include "transformer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace qbs { +namespace Internal { + +BuildGraphLoader::BuildGraphLoader(const QProcessEnvironment &env, const Logger &logger) : + m_logger(logger), m_environment(env) +{ +} + +BuildGraphLoader::~BuildGraphLoader() +{ + qDeleteAll(m_objectsToDelete); +} + +static void restoreBackPointers(const ResolvedProjectPtr &project) +{ + foreach (const ResolvedProductPtr &product, project->products) { + product->project = project; + if (!product->buildData) + continue; + foreach (BuildGraphNode * const n, product->buildData->nodes) { + if (Artifact *a = dynamic_cast(n)) + project->topLevelProject()->buildData->insertIntoLookupTable(a); + } + } + + foreach (const ResolvedProjectPtr &subProject, project->subProjects) { + subProject->parentProject = project; + restoreBackPointers(subProject); + } +} + +BuildGraphLoadResult BuildGraphLoader::load(const TopLevelProjectPtr &existingProject, + const SetupProjectParameters ¶meters, + const RulesEvaluationContextPtr &evalContext) +{ + m_parameters = parameters; + m_result = BuildGraphLoadResult(); + m_evalContext = evalContext; + + if (existingProject) { + QBS_CHECK(existingProject->buildData); + existingProject->buildData->evaluationContext = evalContext; + checkBuildGraphCompatibility(existingProject); + m_result.loadedProject = existingProject; + } else { + loadBuildGraphFromDisk(); + } + if (!m_result.loadedProject) + return m_result; + if (parameters.restoreBehavior() == SetupProjectParameters::RestoreOnly) { + foreach (const ErrorInfo &e, m_result.loadedProject->warningsEncountered) + m_logger.printWarning(e); + return m_result; + } + QBS_CHECK(parameters.restoreBehavior() == SetupProjectParameters::RestoreAndTrackChanges); + + if (m_parameters.logElapsedTime()) + m_wildcardExpansionEffort = 0; + trackProjectChanges(); + if (m_parameters.logElapsedTime()) { + m_logger.qbsLog(LoggerInfo, true) << "\t" + << Tr::tr("Wilcard expansion took %1.") + .arg(elapsedTimeString(m_wildcardExpansionEffort)); + } + return m_result; +} + +void BuildGraphLoader::loadBuildGraphFromDisk() +{ + const QString projectId = TopLevelProject::deriveId(m_parameters.finalBuildConfigurationTree()); + const QString buildDir + = TopLevelProject::deriveBuildDirectory(m_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 (m_parameters.restoreBehavior() == SetupProjectParameters::RestoreOnly) + throw; + m_logger.qbsInfo() << loadError.toString(); + return; + } + + const TopLevelProjectPtr project = TopLevelProject::create(); + + // TODO: Store some meta data that will enable us to show actual progress (e.g. number of products). + m_evalContext->initializeObserver(Tr::tr("Restoring build graph from disk"), 1); + + project->load(pool); + project->buildData->evaluationContext = m_evalContext; + project->setBuildConfiguration(pool.headData().projectConfig); + project->buildDirectory = buildDir; + checkBuildGraphCompatibility(project); + restoreBackPointers(project); + project->location = CodeLocation(m_parameters.projectFilePath(), project->location.line(), + project->location.column()); + m_result.loadedProject = project; + m_evalContext->incrementProgressValue(); + doSanityChecks(project, m_logger); +} + +void BuildGraphLoader::checkBuildGraphCompatibility(const TopLevelProjectConstPtr &project) +{ + if (QFileInfo(project->location.filePath()) != QFileInfo(m_parameters.projectFilePath())) { + QString errorMessage = Tr::tr("Stored build graph at '%1' is for project file '%2', but " + "input file is '%3'. ") + .arg(QDir::toNativeSeparators(project->buildGraphFilePath()), + QDir::toNativeSeparators(project->location.filePath()), + QDir::toNativeSeparators(m_parameters.projectFilePath())); + if (!m_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; + } +} + +static bool checkProductForChangedDependency(QList &changedProducts, + QSet &seenProducts, const ResolvedProductPtr &product) +{ + if (seenProducts.contains(product)) + return false; + if (changedProducts.contains(product)) + return true; + foreach (const ResolvedProductPtr &dep, product->dependencies) { + if (checkProductForChangedDependency(changedProducts, seenProducts, dep)) { + changedProducts << product; + return true; + } + } + seenProducts << product; + return false; +} + +// All products depending on changed products also become changed. Otherwise the output +// artifacts of the rules taking the artifacts from the dependency as inputs will be +// rebuilt due to their rule getting re-applied (as the rescued input artifacts will show +// up as newly added) and no rescue data being available. +static void makeChangedProductsListComplete(QList &changedProducts, + const QList &allRestoredProducts) +{ + QSet seenProducts; + foreach (const ResolvedProductPtr &p, allRestoredProducts) + checkProductForChangedDependency(changedProducts, seenProducts, p); +} + +void BuildGraphLoader::trackProjectChanges() +{ + TimedActivityLogger trackingTimer(m_logger, Tr::tr("Change tracking"), + m_parameters.logElapsedTime()); + const TopLevelProjectPtr &restoredProject = m_result.loadedProject; + QSet buildSystemFiles = restoredProject->buildSystemFiles; + QList allRestoredProducts = restoredProject->allProducts(); + QList changedProducts; + bool reResolvingNecessary = false; + if (!isConfigCompatible()) + reResolvingNecessary = true; + if (hasProductFileChanged(allRestoredProducts, restoredProject->lastResolveTime, + buildSystemFiles, changedProducts)) { + 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 (probeExecutionForced(allRestoredProducts) + || hasBuildSystemFileChanged(buildSystemFiles, restoredProject->lastResolveTime) + || hasEnvironmentChanged(restoredProject) + || hasCanonicalFilePathResultChanged(restoredProject) + || hasFileExistsResultChanged(restoredProject) + || hasDirectoryEntriesResultChanged(restoredProject) + || hasFileLastModifiedResultChanged(restoredProject)) { + reResolvingNecessary = true; + } + + if (!reResolvingNecessary) { + foreach (const ErrorInfo &e, restoredProject->warningsEncountered) + m_logger.printWarning(e); + return; + } + + restoredProject->buildData->isDirty = true; + Loader ldr(m_evalContext->engine(), m_logger); + ldr.setSearchPaths(m_parameters.searchPaths()); + ldr.setProgressObserver(m_evalContext->observer()); + QHash> restoredProbes; + foreach (const auto restoredProduct, allRestoredProducts) + restoredProbes.insert(restoredProduct->uniqueName(), restoredProduct->probes); + ldr.setOldProbes(restoredProbes); + m_result.newlyResolvedProject = ldr.loadProject(m_parameters); + + QMap freshProductsByName; + QList allNewlyResolvedProducts + = m_result.newlyResolvedProject->allProducts(); + foreach (const ResolvedProductPtr &cp, allNewlyResolvedProducts) + freshProductsByName.insert(cp->uniqueName(), cp); + + checkAllProductsForChanges(allRestoredProducts, freshProductsByName, changedProducts); + + QSharedPointer oldBuildData; + ChildListHash childLists; + if (!changedProducts.isEmpty()) { + oldBuildData = QSharedPointer( + 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. + for (const Artifact *a : filterByType(product->buildData->nodes)) { + childLists.insert(a, ChildrenInfo(ArtifactSet::fromNodeSet(a->children), + a->childrenAddedByScanner)); + } + } + } + + makeChangedProductsListComplete(changedProducts, allRestoredProducts); + + // Set up build data from scratch for all changed products. This does not necessarily + // mean that artifacts will have to get rebuilt; whether this is necesessary will be decided + // an a per-artifact basis by the Executor on the next build. + QHash rescuableArtifactData; + foreach (const ResolvedProductPtr &product, changedProducts) { + ResolvedProductPtr freshProduct = freshProductsByName.value(product->uniqueName()); + if (!freshProduct) + continue; + onProductRemoved(product, product->topLevelProject()->buildData.data(), false); + if (product->buildData) { + rescuableArtifactData.insert(product->uniqueName(), + product->buildData->rescuableArtifactData); + } + allRestoredProducts.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->uniqueName() == restoredProduct->uniqueName()) { + if (newlyResolvedProduct->enabled) + newlyResolvedProduct->buildData.swap(restoredProduct->buildData); + if (newlyResolvedProduct->buildData) { + foreach (BuildGraphNode *node, newlyResolvedProduct->buildData->nodes) + node->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) { + changedProducts.removeOne(removedProduct); + onProductRemoved(removedProduct, m_result.newlyResolvedProject->buildData.data()); + } + + // 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); + } + + foreach (const ResolvedProductConstPtr &changedProduct, changedProducts) { + rescueOldBuildData(changedProduct, freshProductsByName.value(changedProduct->uniqueName()), + childLists, rescuableArtifactData.value(changedProduct->uniqueName())); + } + + EmptyDirectoriesRemover(m_result.newlyResolvedProject.data(), m_logger) + .removeEmptyParentDirectories(m_artifactsRemovedFromDisk); + + foreach (FileResourceBase * const f, m_objectsToDelete) { + Artifact * const a = dynamic_cast(f); + if (a) + a->product.clear(); // To help with the sanity checks. + } + doSanityChecks(m_result.newlyResolvedProject, m_logger); +} + +bool BuildGraphLoader::probeExecutionForced(const QList &restoredProducts) const +{ + if (!m_parameters.forceProbeExecution()) + return false; + foreach (const auto &p, restoredProducts) { + if (!p->probes.isEmpty()) + return true; + } + return false; +} + +bool BuildGraphLoader::hasEnvironmentChanged(const TopLevelProjectConstPtr &restoredProject) const +{ + QProcessEnvironment oldEnv = restoredProject->environment; + QProcessEnvironment newEnv = m_environment; + + // HACK. Valgrind screws up our null-build benchmarker otherwise. + // TODO: Think about a (module-provided) whitelist. + oldEnv.remove(QLatin1String("LD_PRELOAD")); + newEnv.remove(QLatin1String("LD_PRELOAD")); + + if (oldEnv != newEnv) { + m_logger.qbsDebug() << "Set of environment variables changed. Must re-resolve project."; + m_logger.qbsTrace() << "old: " << restoredProject->environment.toStringList() << "\nnew:" + << m_environment.toStringList(); + return true; + } + return false; +} + +bool BuildGraphLoader::hasCanonicalFilePathResultChanged(const TopLevelProjectConstPtr &restoredProject) const +{ + for (auto it = restoredProject->canonicalFilePathResults.constBegin(); + it != restoredProject->canonicalFilePathResults.constEnd(); ++it) { + if (QFileInfo(it.key()).canonicalFilePath() != it.value()) { + m_logger.qbsDebug() << "Canonical file path for file '" << it.key() + << "' changed, must re-resolve project."; + return true; + } + } + + return false; +} + +bool BuildGraphLoader::hasFileExistsResultChanged(const TopLevelProjectConstPtr &restoredProject) const +{ + for (QHash::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::hasDirectoryEntriesResultChanged(const TopLevelProjectConstPtr &restoredProject) const +{ + for (auto it = restoredProject->directoryEntriesResults.constBegin(); + it != restoredProject->directoryEntriesResults.constEnd(); ++it) { + if (QDir(it.key().first).entryList(static_cast(it.key().second), QDir::Name) + != it.value()) { + m_logger.qbsDebug() << "Entry list for directory '" << it.key().first + << "' (" + << static_cast(it.key().second) + << ") changed, must re-resolve project."; + return true; + } + } + + return false; +} + +bool BuildGraphLoader::hasFileLastModifiedResultChanged(const TopLevelProjectConstPtr &restoredProject) const +{ + for (QHash::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 &restoredProducts, + const FileTime &referenceTime, QSet &remainingBuildSystemFiles, + QList &changedProducts) +{ + bool hasChanged = false; + foreach (const ResolvedProductPtr &product, restoredProducts) { + const QString filePath = product->location.filePath(); + const FileInfo pfi(filePath); + remainingBuildSystemFiles.remove(filePath); + 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 (!changedProducts.contains(product)) { + AccumulatingTimer wildcardTimer(m_parameters.logElapsedTime() + ? &m_wildcardExpansionEffort : nullptr); + foreach (const GroupPtr &group, product->groups) { + if (!group->wildcards) + continue; + const QSet files + = group->wildcards->expandPatterns(group, product->sourceDirectory); + QSet wcFiles; + foreach (const SourceArtifactConstPtr &sourceArtifact, group->wildcards->files) + wcFiles += sourceArtifact->absoluteFilePath; + if (files == wcFiles) + continue; + hasChanged = true; + changedProducts += product; + break; + } + } + } + + return hasChanged; +} + +bool BuildGraphLoader::hasBuildSystemFileChanged(const QSet &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 &restoredProducts, + const QMap &newlyResolvedProductsByName, + QList &changedProducts) +{ + foreach (const ResolvedProductPtr &restoredProduct, restoredProducts) { + const ResolvedProductPtr newlyResolvedProduct + = newlyResolvedProductsByName.value(restoredProduct->uniqueName()); + if (!newlyResolvedProduct) + continue; + if (newlyResolvedProduct->enabled != restoredProduct->enabled) { + m_logger.qbsDebug() << "Condition of product '" << restoredProduct->uniqueName() + << "' was changed, must set up build data from scratch"; + if (!changedProducts.contains(restoredProduct)) + changedProducts << restoredProduct; + continue; + } + + if (checkProductForChanges(restoredProduct, newlyResolvedProduct)) { + m_logger.qbsDebug() << "Product '" << restoredProduct->uniqueName() + << "' was changed, must set up build data from scratch"; + if (!changedProducts.contains(restoredProduct)) + changedProducts << restoredProduct; + continue; + } + + if (!sourceArtifactSetsAreEqual(restoredProduct->allEnabledFiles(), + newlyResolvedProduct->allEnabledFiles())) { + m_logger.qbsDebug() << "File list of product '" << restoredProduct->uniqueName() + << "' was changed."; + if (!changedProducts.contains(restoredProduct)) + changedProducts << restoredProduct; + } + } +} + +static bool dependenciesAreEqual(const ResolvedProductConstPtr &p1, + const ResolvedProductConstPtr &p2) +{ + if (p1->dependencies.count() != p2->dependencies.count()) + return false; + QSet names1; + QSet names2; + foreach (const ResolvedProductConstPtr &dep, p1->dependencies) + names1 << dep->uniqueName(); + foreach (const ResolvedProductConstPtr &dep, p2->dependencies) + names2 << dep->uniqueName(); + 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 Environment.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; + if (!ruleListsAreEqual(restoredProduct->rules.toList(), newlyResolvedProduct->rules.toList())) + return true; + if (!dependenciesAreEqual(restoredProduct, newlyResolvedProduct)) + return true; + const FileTime referenceTime = restoredProduct->topLevelProject()->lastResolveTime; + foreach (const RuleConstPtr &rule, newlyResolvedProduct->rules) { + if (!isPrepareScriptUpToDate(rule->prepareScript, referenceTime)) + return true; + } + return false; +} + +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") + << QLatin1String("installRoot"); + foreach (const QString &key, specialProperties) { + if (restoredProduct->moduleProperties->qbsPropertyValue(key) + != newlyResolvedProduct->moduleProperties->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->uniqueName() << "'."; + if (!restoredProduct->buildData) + return false; + + // This check must come first, as it can prevent build data rescuing. + if (checkTransformersForPropertyChanges(restoredProduct, newlyResolvedProduct)) + return true; + + if (restoredProduct->fileTags != newlyResolvedProduct->fileTags) { + m_logger.qbsTrace() << "Product type changed from " << restoredProduct->fileTags + << "to " << newlyResolvedProduct->fileTags; + 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 seenTransformers; + for (Artifact *artifact : filterByType(restoredProduct->buildData->nodes)) { + 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->uniqueName() << "'."; + } + return transformerChanges; +} + +void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product, + ProjectBuildData *projectBuildData, bool removeArtifactsFromDisk) +{ + m_logger.qbsDebug() << "[BG] product '" << product->uniqueName() << "' removed."; + + product->project->products.removeOne(product); + if (product->buildData) { + foreach (BuildGraphNode * const node, product->buildData->nodes) { + if (node->type() == BuildGraphNode::ArtifactNodeType) { + Artifact * const artifact = static_cast(node); + projectBuildData->removeArtifact(artifact, m_logger, removeArtifactsFromDisk, + false); + if (removeArtifactsFromDisk && artifact->artifactType == Artifact::Generated) + m_artifactsRemovedFromDisk << artifact->filePath(); + } else { + foreach (BuildGraphNode * const parent, node->parents) + parent->children.remove(node); + node->parents.clear(); + foreach (BuildGraphNode * const child, node->children) + child->parents.remove(node); + node->children.clear(); + } + } + } +} + +static SourceArtifactConstPtr findSourceArtifact(const ResolvedProductConstPtr &product, + const QString &artifactFilePath, QMap &artifactMap) +{ + SourceArtifactConstPtr &artifact = artifactMap[artifactFilePath]; + if (!artifact) { + foreach (const SourceArtifactConstPtr &a, product->allFiles()) { + if (a->absoluteFilePath == artifactFilePath) { + artifact = a; + break; + } + } + } + return artifact; +} + +static QVariantMap propertyMapByKind(const ResolvedProductConstPtr &product, Property::Kind kind) +{ + switch (kind) { + case Property::PropertyInModule: + return product->moduleProperties->value(); + case Property::PropertyInProduct: + return product->productProperties; + case Property::PropertyInProject: + return product->project->projectProperties(); + default: + QBS_CHECK(false); + } + return QVariantMap(); +} + +static void invalidateTransformer(const TransformerPtr &transformer) +{ + const JavaScriptCommandPtr &pseudoCommand = JavaScriptCommand::create(); + pseudoCommand->setSourceCode(QLatin1String("random stuff that will cause " + "commandsEqual() to fail")); + transformer->commands << pseudoCommand; +} + +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) { + if (checkForPropertyChange(property, propertyMapByKind(freshProduct, property.kind))) { + invalidateTransformer(restoredTrafo); + return true; + } + } + + QMap artifactMap; + for (auto it = restoredTrafo->propertiesRequestedFromArtifactInCommands.cbegin(); + it != restoredTrafo->propertiesRequestedFromArtifactInCommands.cend(); ++it) { + const SourceArtifactConstPtr artifact + = findSourceArtifact(freshProduct, it.key(), artifactMap); + if (!artifact) + continue; + foreach (const Property &property, it.value()) { + if (checkForPropertyChange(property, artifact->properties->value())) { + invalidateTransformer(restoredTrafo); + return true; + } + } + } + + foreach (const Property &property, restoredTrafo->propertiesRequestedInPrepareScript) { + if (checkForPropertyChange(property, propertyMapByKind(freshProduct, property.kind))) + return true; + } + + for (QHash::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: + 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::fromLatin1("[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; + for (Artifact *artifactInProduct : filterByType(product->buildData->nodes)) { + 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; +} + +bool BuildGraphLoader::isConfigCompatible() +{ + const TopLevelProjectConstPtr restoredProject = m_result.loadedProject; + if (m_parameters.finalBuildConfigurationTree() != restoredProject->buildConfiguration()) + return false; + for (QVariantMap::ConstIterator it = restoredProject->profileConfigs.constBegin(); + it != restoredProject->profileConfigs.constEnd(); ++it) { + const QVariantMap buildConfig = SetupProjectParameters::expandedBuildConfiguration( + m_parameters.settingsDirectory(), it.key(), m_parameters.configurationName()); + const QVariantMap newConfig = SetupProjectParameters::finalBuildConfigurationTree( + buildConfig, m_parameters.overriddenValues(), m_parameters.buildRoot()); + if (newConfig != it.value()) + return false; + } + return true; +} + +bool BuildGraphLoader::isPrepareScriptUpToDate(const ScriptFunctionConstPtr &script, + const FileTime &referenceTime) +{ + foreach (const JsImport &jsImport, script->fileContext->jsImports()) { + foreach (const QString &filePath, jsImport.filePaths) { + if (FileInfo(filePath).lastModified() > referenceTime) { + m_logger.qbsDebug() << "Change in import '" << filePath + << "' potentially influences prepare script, marking as out of date"; + return false; + } + } + } + return true; +} + +void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restoredProduct, + const ResolvedProductPtr &newlyResolvedProduct, const ChildListHash &childLists, + const AllRescuableArtifactData &existingRad) +{ + QBS_CHECK(newlyResolvedProduct); + if (!restoredProduct->enabled || !newlyResolvedProduct->enabled) + return; + + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << QString::fromLatin1("[BG] rescue data of product '%1'") + .arg(restoredProduct->uniqueName()); + } + QBS_CHECK(newlyResolvedProduct->buildData); + QBS_CHECK(newlyResolvedProduct->buildData->rescuableArtifactData.isEmpty()); + newlyResolvedProduct->buildData->rescuableArtifactData = existingRad; + + // This is needed for artifacts created by rules, which happens later in the executor. + for (Artifact * const oldArtifact : filterByType(restoredProduct->buildData->nodes)) { + if (!oldArtifact->transformer) + continue; + Artifact * const newArtifact = lookupArtifact(newlyResolvedProduct, oldArtifact, false); + if (!newArtifact) { + RescuableArtifactData rad; + rad.timeStamp = oldArtifact->timestamp(); + rad.fileTags = oldArtifact->fileTags(); + rad.properties = oldArtifact->properties->value(); + rad.commands = oldArtifact->transformer->commands; + rad.propertiesRequestedInPrepareScript + = oldArtifact->transformer->propertiesRequestedInPrepareScript; + rad.propertiesRequestedInCommands + = oldArtifact->transformer->propertiesRequestedInCommands; + rad.propertiesRequestedFromArtifactInPrepareScript + = oldArtifact->transformer->propertiesRequestedFromArtifactInPrepareScript; + rad.propertiesRequestedFromArtifactInCommands + = oldArtifact->transformer->propertiesRequestedFromArtifactInCommands; + const ChildrenInfo &childrenInfo = childLists.value(oldArtifact); + foreach (Artifact * const child, childrenInfo.children) { + rad.children << RescuableArtifactData::ChildData(child->product->name, + child->product->profile, child->filePath(), + childrenInfo.childrenAddedByScanner.contains(child)); + } + newlyResolvedProduct->buildData->rescuableArtifactData.insert( + oldArtifact->filePath(), rad); + } + } +} + +} // 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 00000000..6ec918e1 --- /dev/null +++ b/src/lib/corelib/buildgraph/buildgraphloader.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BUILDGRAPHLOADER_H +#define QBS_BUILDGRAPHLOADER_H + +#include "forward_decls.h" + +#include "rescuableartifactdata.h" + +#include +#include +#include +#include + +#include +#include + +namespace qbs { + +namespace Internal { +class FileDependency; +class FileResourceBase; +class FileTime; +class NodeSet; +class Property; + +class BuildGraphLoadResult +{ +public: + TopLevelProjectPtr newlyResolvedProject; + TopLevelProjectPtr loadedProject; +}; + + +class BuildGraphLoader +{ +public: + BuildGraphLoader(const QProcessEnvironment &env, const Logger &logger); + ~BuildGraphLoader(); + + BuildGraphLoadResult load(const TopLevelProjectPtr &existingProject, + const SetupProjectParameters ¶meters, + const RulesEvaluationContextPtr &evalContext); + +private: + void loadBuildGraphFromDisk(); + void checkBuildGraphCompatibility(const TopLevelProjectConstPtr &project); + void trackProjectChanges(); + bool probeExecutionForced(const QList &restoredProducts) const; + bool hasEnvironmentChanged(const TopLevelProjectConstPtr &restoredProject) const; + bool hasCanonicalFilePathResultChanged(const TopLevelProjectConstPtr &restoredProject) const; + bool hasFileExistsResultChanged(const TopLevelProjectConstPtr &restoredProject) const; + bool hasDirectoryEntriesResultChanged(const TopLevelProjectConstPtr &restoredProject) const; + bool hasFileLastModifiedResultChanged(const TopLevelProjectConstPtr &restoredProject) const; + bool hasProductFileChanged(const QList &restoredProducts, + const FileTime &referenceTime, + QSet &remainingBuildSystemFiles, + QList &productsWithChangedFiles); + bool hasBuildSystemFileChanged(const QSet &buildSystemFiles, + const FileTime &referenceTime); + void checkAllProductsForChanges(const QList &restoredProducts, + const QMap &newlyResolvedProductsByName, + QList &changedProducts); + 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); + 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); + bool isConfigCompatible(); + bool isPrepareScriptUpToDate(const ScriptFunctionConstPtr &script, + const FileTime &referenceTime); + + struct ChildrenInfo { + ChildrenInfo() {} + ChildrenInfo(const ArtifactSet &c1, const ArtifactSet &c2) + : children(c1), childrenAddedByScanner(c2) {} + ArtifactSet children; + ArtifactSet childrenAddedByScanner; + }; + typedef QHash ChildListHash; + void rescueOldBuildData(const ResolvedProductConstPtr &restoredProduct, + const ResolvedProductPtr &newlyResolvedProduct, + const ChildListHash &childLists, + const AllRescuableArtifactData &existingRad); + + RulesEvaluationContextPtr m_evalContext; + SetupProjectParameters m_parameters; + BuildGraphLoadResult m_result; + Logger m_logger; + QProcessEnvironment m_environment; + QStringList m_artifactsRemovedFromDisk; + qint64 m_wildcardExpansionEffort; + + // These must only be deleted at the end so we can still peek into the old look-up table. + QList m_objectsToDelete; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/buildgraph/buildgraphnode.cpp b/src/lib/corelib/buildgraph/buildgraphnode.cpp new file mode 100644 index 00000000..458e8845 --- /dev/null +++ b/src/lib/corelib/buildgraph/buildgraphnode.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "buildgraphnode.h" + +#include "buildgraphvisitor.h" +#include "projectbuilddata.h" +#include +#include +#include +#include + +//#include + +namespace qbs { +namespace Internal { + +BuildGraphNode::BuildGraphNode() : buildState(Untouched) +{ +} + +BuildGraphNode::~BuildGraphNode() +{ + foreach (BuildGraphNode *p, parents) + p->children.remove(this); + foreach (BuildGraphNode *c, children) + c->parents.remove(this); +} + +void BuildGraphNode::onChildDisconnected(BuildGraphNode *child) +{ + Q_UNUSED(child); +} + +void BuildGraphNode::acceptChildren(BuildGraphVisitor *visitor) +{ + foreach (BuildGraphNode *child, children) + child->accept(visitor); +} + +void BuildGraphNode::load(PersistentPool &pool) +{ + children.load(pool); + // Parents must be updated after loading all nodes. +} + +void BuildGraphNode::store(PersistentPool &pool) const +{ + children.store(pool); + // Do not store parents to avoid recursion. +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/buildgraph/buildgraphnode.h b/src/lib/corelib/buildgraph/buildgraphnode.h new file mode 100644 index 00000000..a270b12d --- /dev/null +++ b/src/lib/corelib/buildgraph/buildgraphnode.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_BUILDGRAPHNODE_H +#define QBS_BUILDGRAPHNODE_H + +#include "nodeset.h" +#include +#include +#include + +namespace qbs { +namespace Internal { + +class BuildGraphVisitor; +class TopLevelProject; + +class BuildGraphNode : public virtual PersistentObject +{ + friend class NodeSet; +public: + virtual ~BuildGraphNode(); + + NodeSet parents; + NodeSet children; + WeakPointer product; + + enum BuildState + { + Untouched = 0, + Buildable, + Building, + Built + }; + + BuildState buildState; // Do not serialize. Will be refreshed for every build. + + enum Type + { + ArtifactNodeType, + RuleNodeType + }; + + virtual Type type() const = 0; + virtual void accept(BuildGraphVisitor *visitor) = 0; + virtual QString toString() const = 0; + virtual void onChildDisconnected(BuildGraphNode *child); + +protected: + explicit BuildGraphNode(); + void acceptChildren(BuildGraphVisitor *visitor); + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_BUILDGRAPHNODE_H diff --git a/src/lib/corelib/buildgraph/buildgraphvisitor.h b/src/lib/corelib/buildgraph/buildgraphvisitor.h new file mode 100644 index 00000000..216e16a8 --- /dev/null +++ b/src/lib/corelib/buildgraph/buildgraphvisitor.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_BUILDGRAPHVISITOR_H +#define QBS_BUILDGRAPHVISITOR_H + +namespace qbs { +namespace Internal { + +class Artifact; +class RuleNode; + +/*! + * \brief The BuildGraphVisitor class + * + * The return value of a visit method indicates whether the children of the current node + * are to be visited next. + */ +class BuildGraphVisitor +{ +public: + virtual bool visit(Artifact *) { return true; } + virtual void endVisit(Artifact *) { } + virtual bool visit(RuleNode *) { return true; } + virtual void endVisit(RuleNode *) { } +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_BUILDGRAPHVISITOR_H diff --git a/src/lib/corelib/buildgraph/command.cpp b/src/lib/corelib/buildgraph/command.cpp new file mode 100644 index 00000000..55b486df --- /dev/null +++ b/src/lib/corelib/buildgraph/command.cpp @@ -0,0 +1,429 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "command.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +AbstractCommand::AbstractCommand() + : m_description(defaultDescription()), + m_extendedDescription(defaultExtendedDescription()), + m_highlight(defaultHighLight()), + m_ignoreDryRun(defaultIgnoreDryRun()), + m_silent(defaultIsSilent()) +{ +} + +AbstractCommand::~AbstractCommand() +{ +} + +bool AbstractCommand::equals(const AbstractCommand *other) const +{ + return type() == other->type() + && m_description == other->m_description + && m_extendedDescription == other->m_extendedDescription + && m_highlight == other->m_highlight + && m_ignoreDryRun == other->m_ignoreDryRun + && m_silent == other->m_silent + && m_properties == other->m_properties; +} + +void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation) +{ + m_description = scriptValue->property(QLatin1String("description")).toString(); + m_extendedDescription = scriptValue->property(QLatin1String("extendedDescription")).toString(); + m_highlight = scriptValue->property(QLatin1String("highlight")).toString(); + m_ignoreDryRun = scriptValue->property(QLatin1String("ignoreDryRun")).toBool(); + m_silent = scriptValue->property(QLatin1String("silent")).toBool(); + m_codeLocation = codeLocation; + + m_predefinedProperties + << QLatin1String("description") + << QLatin1String("extendedDescription") + << QLatin1String("highlight") + << QLatin1String("ignoreDryRun") + << QLatin1String("silent"); +} + +void AbstractCommand::load(PersistentPool &pool) +{ + m_description = pool.idLoadString(); + m_extendedDescription = pool.idLoadString(); + m_highlight = pool.idLoadString(); + pool.stream() >> m_ignoreDryRun; + pool.stream() >> m_silent; + m_codeLocation.load(pool); + m_properties = pool.loadVariantMap(); +} + +void AbstractCommand::store(PersistentPool &pool) const +{ + pool.storeString(m_description); + pool.storeString(m_extendedDescription); + pool.storeString(m_highlight); + pool.stream() << m_ignoreDryRun; + pool.stream() << m_silent; + m_codeLocation.store(pool); + pool.store(m_properties); +} + +void AbstractCommand::applyCommandProperties(const QScriptValue *scriptValue) +{ + QScriptValueIterator it(*scriptValue); + while (it.hasNext()) { + it.next(); + if (m_predefinedProperties.contains(it.name())) + continue; + const QVariant value = it.value().toVariant(); + if (QMetaType::Type(value.type()) == QMetaType::QObjectStar) { + throw ErrorInfo(Tr::tr("Property '%1' has a type unsuitable for storing in a command " + "object.").arg(it.name()), m_codeLocation); + } + m_properties.insert(it.name(), value); + } +} + +static QScriptValue js_CommandBase(QScriptContext *context, QScriptEngine *engine) +{ + QScriptValue cmd = context->thisObject(); + QBS_ASSERT(context->isCalledAsConstructor(), cmd = engine->newObject()); + cmd.setProperty(QLatin1String("description"), + engine->toScriptValue(AbstractCommand::defaultDescription())); + cmd.setProperty(QLatin1String("extendedDescription"), + engine->toScriptValue(AbstractCommand::defaultExtendedDescription())); + cmd.setProperty(QLatin1String("highlight"), + engine->toScriptValue(AbstractCommand::defaultHighLight())); + cmd.setProperty(QLatin1String("ignoreDryRun"), + engine->toScriptValue(AbstractCommand::defaultIgnoreDryRun())); + cmd.setProperty(QLatin1String("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 ProcessCommandPtr commandPrototype = ProcessCommand::create(); + + 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(QLatin1String("className"), + engine->toScriptValue(QString::fromLatin1("Command"))); + cmd.setProperty(QLatin1String("program"), program); + cmd.setProperty(QLatin1String("arguments"), arguments); + cmd.setProperty(QLatin1String("workingDir"), + engine->toScriptValue(commandPrototype->workingDir())); + cmd.setProperty(QLatin1String("maxExitCode"), + engine->toScriptValue(commandPrototype->maxExitCode())); + cmd.setProperty(QLatin1String("stdoutFilterFunction"), + engine->toScriptValue(commandPrototype->stdoutFilterFunction())); + cmd.setProperty(QLatin1String("stderrFilterFunction"), + engine->toScriptValue(commandPrototype->stderrFilterFunction())); + cmd.setProperty(QLatin1String("responseFileThreshold"), + engine->toScriptValue(commandPrototype->responseFileThreshold())); + cmd.setProperty(QLatin1String("responseFileArgumentIndex"), + engine->toScriptValue(commandPrototype->responseFileArgumentIndex())); + cmd.setProperty(QLatin1String("responseFileUsagePrefix"), + engine->toScriptValue(commandPrototype->responseFileUsagePrefix())); + cmd.setProperty(QLatin1String("stdoutFilePath"), + engine->toScriptValue(commandPrototype->stdoutFilePath())); + cmd.setProperty(QLatin1String("stderrFilePath"), + engine->toScriptValue(commandPrototype->stderrFilePath())); + cmd.setProperty(QLatin1String("environment"), + engine->toScriptValue(commandPrototype->environment().toStringList())); + cmd.setProperty(QLatin1String("ignoreDryRun"), + engine->toScriptValue(commandPrototype->ignoreDryRun())); + return cmd; +} + + +void ProcessCommand::setupForJavaScript(QScriptValue targetObject) +{ + QBS_CHECK(targetObject.isObject()); + QScriptValue ctor = targetObject.engine()->newFunction(js_Command, 2); + targetObject.setProperty(QLatin1String("Command"), ctor); +} + +ProcessCommand::ProcessCommand() + : m_maxExitCode(0) + , m_responseFileThreshold(HostOsInfo::isWindowsHost() ? 32000 : -1) + , m_responseFileArgumentIndex(0) +{ +} + +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(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_responseFileArgumentIndex == other->m_responseFileArgumentIndex + && m_responseFileUsagePrefix == other->m_responseFileUsagePrefix + && m_stdoutFilePath == other->m_stdoutFilePath + && m_stderrFilePath == other->m_stderrFilePath + && m_environment == other->m_environment; +} + +void ProcessCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation) +{ + AbstractCommand::fillFromScriptValue(scriptValue, codeLocation); + m_program = scriptValue->property(QLatin1String("program")).toString(); + m_arguments = scriptValue->property(QLatin1String("arguments")).toVariant().toStringList(); + m_workingDir = scriptValue->property(QLatin1String("workingDirectory")).toString(); + m_maxExitCode = scriptValue->property(QLatin1String("maxExitCode")).toInt32(); + QScriptValue stdoutFilterFunction = + scriptValue->property(QLatin1String("stdoutFilterFunction")).toString(); + if (stdoutFilterFunction.isFunction()) + m_stdoutFilterFunction = QLatin1String("(") + stdoutFilterFunction.toString() + + QLatin1String(")()"); + else + m_stdoutFilterFunction = stdoutFilterFunction.toString(); + QScriptValue stderrFilterFunction = + scriptValue->property(QLatin1String("stderrFilterFunction")).toString(); + if (stderrFilterFunction.isFunction()) + m_stderrFilterFunction = QLatin1String("(") + stderrFilterFunction.toString() + + QLatin1String(")()"); + else + m_stderrFilterFunction = stderrFilterFunction.toString(); + m_responseFileThreshold = scriptValue->property(QLatin1String("responseFileThreshold")) + .toInt32(); + m_responseFileArgumentIndex = scriptValue->property(QLatin1String("responseFileArgumentIndex")) + .toInt32(); + m_responseFileUsagePrefix = scriptValue->property(QLatin1String("responseFileUsagePrefix")) + .toString(); + QStringList envList = scriptValue->property(QLatin1String("environment")).toVariant() + .toStringList(); + getEnvironmentFromList(envList); + m_stdoutFilePath = scriptValue->property(QLatin1String("stdoutFilePath")).toString(); + m_stderrFilePath = scriptValue->property(QLatin1String("stderrFilePath")).toString(); + + m_predefinedProperties + << QLatin1String("program") + << QLatin1String("arguments") + << QLatin1String("workingDirectory") + << QLatin1String("maxExitCode") + << QLatin1String("stdoutFilterFunction") + << QLatin1String("stderrFilterFunction") + << QLatin1String("responseFileThreshold") + << QLatin1String("responseFileArgumentIndex") + << QLatin1String("responseFileUsagePrefix") + << QLatin1String("environment") + << QLatin1String("stdoutFilePath") + << QLatin1String("stderrFilePath"); + applyCommandProperties(scriptValue); +} + +void ProcessCommand::load(PersistentPool &pool) +{ + AbstractCommand::load(pool); + m_program = pool.idLoadString(); + m_arguments = pool.idLoadStringList(); + const QStringList envList = pool.idLoadStringList(); + m_workingDir = pool.idLoadString(); + m_stdoutFilterFunction = pool.idLoadString(); + m_stderrFilterFunction = pool.idLoadString(); + m_responseFileUsagePrefix = pool.idLoadString(); + pool.stream() >> m_maxExitCode >> m_responseFileThreshold >> m_responseFileArgumentIndex; + m_stdoutFilePath = pool.idLoadString(); + m_stderrFilePath = pool.idLoadString(); + getEnvironmentFromList(envList); +} + +void ProcessCommand::store(PersistentPool &pool) const +{ + AbstractCommand::store(pool); + pool.storeString(m_program); + pool.storeStringList(m_arguments); + pool.storeStringList(m_environment.toStringList()); + pool.storeString(m_workingDir); + pool.storeString(m_stdoutFilterFunction); + pool.storeString(m_stderrFilterFunction); + pool.storeString(m_responseFileUsagePrefix); + pool.stream() << m_maxExitCode << m_responseFileThreshold << m_responseFileArgumentIndex; + pool.storeString(m_stdoutFilePath); + pool.storeString(m_stderrFilePath); +} + +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, + QLatin1String("JavaScriptCommand c'tor doesn't take arguments.")); + } + + static JavaScriptCommandPtr commandPrototype = JavaScriptCommand::create(); + QScriptValue cmd = js_CommandBase(context, engine); + cmd.setProperty(QLatin1String("className"), + engine->toScriptValue(QString::fromLatin1("JavaScriptCommand"))); + cmd.setProperty(QLatin1String("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(QLatin1String("JavaScriptCommand"), ctor); +} + +JavaScriptCommand::JavaScriptCommand() +{ +} + +bool JavaScriptCommand::equals(const AbstractCommand *otherAbstractCommand) const +{ + if (!AbstractCommand::equals(otherAbstractCommand)) + return false; + const JavaScriptCommand * const other + = static_cast(otherAbstractCommand); + return m_sourceCode == other->m_sourceCode; +} + +void JavaScriptCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation) +{ + AbstractCommand::fillFromScriptValue(scriptValue, codeLocation); + QScriptValue sourceCode = scriptValue->property(QLatin1String("sourceCode")); + if (sourceCode.isFunction()) + m_sourceCode = QLatin1String("(") + sourceCode.toString() + QLatin1String(")()"); + else + m_sourceCode = sourceCode.toString(); + + m_predefinedProperties << QLatin1String("className") << QLatin1String("sourceCode"); + applyCommandProperties(scriptValue); +} + +void JavaScriptCommand::load(PersistentPool &pool) +{ + AbstractCommand::load(pool); + m_sourceCode = pool.idLoadString(); +} + +void JavaScriptCommand::store(PersistentPool &pool) const +{ + AbstractCommand::store(pool); + pool.storeString(m_sourceCode); +} + +QList loadCommandList(PersistentPool &pool) +{ + QList commands; + int count; + pool.stream() >> count; + commands.reserve(count); + while (--count >= 0) { + int cmdType; + pool.stream() >> cmdType; + AbstractCommandPtr cmd; + switch (cmdType) { + case AbstractCommand::JavaScriptCommandType: + cmd = pool.idLoadS(); + break; + case AbstractCommand::ProcessCommandType: + cmd = pool.idLoadS(); + break; + default: + QBS_CHECK(false); + } + commands += cmd; + } + return commands; +} + +void storeCommandList(const QList &commands, PersistentPool &pool) +{ + pool.stream() << commands.count(); + foreach (const AbstractCommandPtr &cmd, commands) { + pool.stream() << int(cmd->type()); + pool.store(cmd); + } +} + +bool commandListsAreEqual(const QList &l1, const QList &l2) +{ + if (l1.count() != l2.count()) + return false; + for (int i = 0; i < l1.count(); ++i) + if (!l1.at(i)->equals(l2.at(i).data())) + return false; + return true; +} + +} // 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 00000000..79bc9f18 --- /dev/null +++ b/src/lib/corelib/buildgraph/command.h @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_BUILDGRAPH_COMMAND_H +#define QBS_BUILDGRAPH_COMMAND_H + +#include "forward_decls.h" + +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class AbstractCommand : public PersistentObject +{ +public: + virtual ~AbstractCommand(); + + enum CommandType { + ProcessCommandType, + JavaScriptCommandType + }; + + static QString defaultDescription() { return QString(); } + static QString defaultExtendedDescription() { return QString(); } + static QString defaultHighLight() { return QString(); } + static bool defaultIgnoreDryRun() { return false; } + 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); + + const QString description() const { return m_description; } + const QString extendedDescription() const { return m_extendedDescription; } + const QString highlight() const { return m_highlight; } + bool ignoreDryRun() const { return m_ignoreDryRun; } + bool isSilent() const { return m_silent; } + CodeLocation codeLocation() const { return m_codeLocation; } + + const QVariantMap &properties() const { return m_properties; } + +protected: + AbstractCommand(); + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + void applyCommandProperties(const QScriptValue *scriptValue); + + QSet m_predefinedProperties; + + +private: + QString m_description; + QString m_extendedDescription; + QString m_highlight; + bool m_ignoreDryRun; + bool m_silent; + CodeLocation m_codeLocation; + QVariantMap m_properties; +}; + +class ProcessCommand : public AbstractCommand +{ +public: + static ProcessCommandPtr create() { return ProcessCommandPtr(new ProcessCommand); } + static void setupForJavaScript(QScriptValue targetObject); + + CommandType type() const { return ProcessCommandType; } + bool equals(const AbstractCommand *otherAbstractCommand) const; + void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation); + 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; } + int responseFileArgumentIndex() const { return m_responseFileArgumentIndex; } + QString responseFileUsagePrefix() const { return m_responseFileUsagePrefix; } + QProcessEnvironment environment() const { return m_environment; } + QString stdoutFilePath() const { return m_stdoutFilePath; } + QString stderrFilePath() const { return m_stderrFilePath; } + +private: + ProcessCommand(); + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + + 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). + int m_responseFileArgumentIndex; + QString m_responseFileUsagePrefix; + QProcessEnvironment m_environment; + QString m_stdoutFilePath; + QString m_stderrFilePath; +}; + +class JavaScriptCommand : public AbstractCommand +{ +public: + static JavaScriptCommandPtr create() { return JavaScriptCommandPtr(new JavaScriptCommand); } + static void setupForJavaScript(QScriptValue targetObject); + + virtual CommandType type() const { return JavaScriptCommandType; } + bool equals(const AbstractCommand *otherAbstractCommand) const; + void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation); + + const QString &sourceCode() const { return m_sourceCode; } + void setSourceCode(const QString &str) { m_sourceCode = str; } + +private: + JavaScriptCommand(); + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + + QString m_sourceCode; +}; + +QList loadCommandList(PersistentPool &pool); +void storeCommandList(const QList &commands, PersistentPool &pool); + +bool commandListsAreEqual(const QList &l1, const QList &l2); + +} // namespace Internal +} // namespace qbs + +#endif // QBS_BUILDGRAPH_COMMAND_H diff --git a/src/lib/corelib/buildgraph/cycledetector.cpp b/src/lib/corelib/buildgraph/cycledetector.cpp new file mode 100644 index 00000000..9ef1daab --- /dev/null +++ b/src/lib/corelib/buildgraph/cycledetector.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "cycledetector.h" + +#include "artifact.h" +#include "buildgraph.h" +#include "projectbuilddata.h" +#include "rulenode.h" + +#include +#include +#include + +namespace qbs { +namespace Internal { + +CycleDetector::CycleDetector(const Logger &logger) + : m_parent(0), m_logger(logger) +{ +} + +void CycleDetector::visitProject(const TopLevelProjectConstPtr &project) +{ + project->accept(this); +} + +void CycleDetector::visitProduct(const ResolvedProductConstPtr &product) +{ + product->accept(this); +} + +bool CycleDetector::visit(Artifact *artifact) +{ + return visitNode(artifact); +} + +bool CycleDetector::visit(RuleNode *ruleNode) +{ + return visitNode(ruleNode); +} + +bool CycleDetector::visitNode(BuildGraphNode *node) +{ + if (Q_UNLIKELY(m_nodesInCurrentPath.contains(node))) { + ErrorInfo error(Tr::tr("Cycle in build graph detected.")); + foreach (const BuildGraphNode * const n, cycle(node)) + error.append(n->toString()); + throw error; + } + + if (m_allNodes.contains(node)) + return false; + + m_nodesInCurrentPath += node; + m_parent = node; + foreach (BuildGraphNode * const child, node->children) + child->accept(this); + m_nodesInCurrentPath -= node; + m_allNodes += node; + return false; +} + +QList CycleDetector::cycle(BuildGraphNode *doubleEntry) +{ + QList 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 00000000..da844d35 --- /dev/null +++ b/src/lib/corelib/buildgraph/cycledetector.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_CYCLEDETECTOR_H +#define QBS_CYCLEDETECTOR_H + +#include "buildgraphvisitor.h" +#include +#include + +#include + +namespace qbs { +namespace Internal { + +class BuildGraphNode; + +class CycleDetector : private BuildGraphVisitor +{ +public: + CycleDetector(const Logger &logger); + + void visitProject(const TopLevelProjectConstPtr &project); + void visitProduct(const ResolvedProductConstPtr &product); + +private: + bool visit(Artifact *artifact); + bool visit(RuleNode *ruleNode); + + bool visitNode(BuildGraphNode *node); + + QList cycle(BuildGraphNode *doubleEntry); + + QSet m_allNodes; + QSet m_nodesInCurrentPath; + BuildGraphNode *m_parent; + Logger m_logger; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_CYCLEDETECTOR_H diff --git a/src/lib/corelib/buildgraph/depscanner.cpp b/src/lib/corelib/buildgraph/depscanner.cpp new file mode 100644 index 00000000..d88759f3 --- /dev/null +++ b/src/lib/corelib/buildgraph/depscanner.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "depscanner.h" +#include "artifact.h" +#include "projectbuilddata.h" +#include "buildgraph.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +static QStringList collectCppIncludePaths(const QVariantMap &modules) +{ + QStringList result; + const QVariantMap cpp = modules.value(QLatin1String("cpp")).toMap(); + if (cpp.isEmpty()) + return result; + + result + << cpp.value(QLatin1String("includePaths")).toStringList() + << cpp.value(QLatin1String("systemIncludePaths")).toStringList() + << cpp.value(QLatin1String("compilerIncludePaths")).toStringList(); + result.removeDuplicates(); + return result; +} + +PluginDependencyScanner::PluginDependencyScanner(ScannerPlugin *plugin) + : m_plugin(plugin) +{ +} + +QStringList PluginDependencyScanner::collectSearchPaths(Artifact *artifact) +{ + if (m_plugin->flags & ScannerUsesCppIncludePaths) { + QVariantMap modules = artifact->properties->value().value(QLatin1String("modules")).toMap(); + return collectCppIncludePaths(modules); + } else { + return QStringList(); + } +} + +QStringList PluginDependencyScanner::collectDependencies(FileResourceBase *file) +{ + QSet result; + QString baseDirOfInFilePath = file->dirPath(); + const QString &filepath = file->filePath(); + void *scannerHandle = m_plugin->open(filepath.utf16(), ScanForDependenciesFlag); + if (!scannerHandle) + return QStringList(); + forever { + int flags = 0; + int length = 0; + const char *szOutFilePath = m_plugin->next(scannerHandle, &length, &flags); + if (szOutFilePath == 0) + break; + QString outFilePath = QString::fromLocal8Bit(szOutFilePath, length); + if (outFilePath.isEmpty()) + continue; + if (flags & SC_LOCAL_INCLUDE_FLAG) { + QString localFilePath = FileInfo::resolvePath(baseDirOfInFilePath, outFilePath); + if (FileInfo::exists(localFilePath)) + outFilePath = localFilePath; + } + result += outFilePath; + } + m_plugin->close(scannerHandle); + return QStringList(result.toList()); +} + +bool PluginDependencyScanner::recursive() const +{ + return m_plugin->flags & ScannerRecursiveDependencies; +} + +const void *PluginDependencyScanner::key() const +{ + return m_plugin; +} + +UserDependencyScanner::UserDependencyScanner(const ResolvedScannerConstPtr &scanner, + const Logger &logger) + : m_scanner(scanner), + m_logger(logger), + m_engine(new ScriptEngine(m_logger, EvalContext::RuleExecution)), + m_observer(m_engine), + m_product(0) +{ + m_engine->setProcessEventsInterval(-1); // QBS-782 + m_global = m_engine->newObject(); + m_global.setPrototype(m_engine->globalObject()); + setupScriptEngineForFile(m_engine, m_scanner->scanScript->fileContext, m_global); +} + +UserDependencyScanner::~UserDependencyScanner() +{ + delete m_engine; +} + +QStringList UserDependencyScanner::collectSearchPaths(Artifact *artifact) +{ + return evaluate(artifact, m_scanner->searchPathsScript); +} + +QStringList UserDependencyScanner::collectDependencies(FileResourceBase *file) +{ + // ### support user dependency scanners for file deps + Artifact *artifact = dynamic_cast(file); + if (!artifact) + return QStringList(); + return evaluate(artifact, m_scanner->scanScript); +} + +bool UserDependencyScanner::recursive() const +{ + return m_scanner->recursive; +} + +const void *UserDependencyScanner::key() const +{ + return m_scanner.data(); +} + +QStringList UserDependencyScanner::evaluate(Artifact *artifact, const ScriptFunctionPtr &script) +{ + if (artifact->product.data() != m_product) { + m_product = artifact->product.data(); + setupScriptEngineForProduct(m_engine, artifact->product, + m_scanner->module, m_global, &m_observer); + } + + QScriptValue artifactConfig = m_engine->newObject(); + ModuleProperties::init(artifactConfig, artifact); + artifactConfig.setProperty(QLatin1String("fileName"), + FileInfo::fileName(artifact->filePath()), 0); + artifactConfig.setProperty(QLatin1String("filePath"), artifact->filePath(), 0); + const QStringList fileTags = artifact->fileTags().toStringList(); + artifactConfig.setProperty(QLatin1String("fileTags"), m_engine->toScriptValue(fileTags)); + if (!m_scanner->module->name.isEmpty()) + artifactConfig.setProperty(QLatin1String("moduleName"), m_scanner->module->name); + QScriptValueList args; + args.reserve(3); + args.append(m_global.property(QString::fromLatin1("project"))); + args.append(m_global.property(QString::fromLatin1("product"))); + args.append(artifactConfig); + + m_engine->setGlobalObject(m_global); + QScriptValue &function = script->scriptFunction; + if (!function.isValid() || function.engine() != m_engine) { + function = m_engine->evaluate(script->sourceCode); + if (Q_UNLIKELY(!function.isFunction())) + throw ErrorInfo(Tr::tr("Invalid scan script."), script->location); + } + QScriptValue result = function.call(QScriptValue(), args); + m_engine->setGlobalObject(m_global.prototype()); + m_engine->clearRequestedProperties(); + if (Q_UNLIKELY(m_engine->hasErrorOrException(result))) { + QString msg = Tr::tr("evaluating scan script: ") + m_engine->lastErrorString(result); + m_engine->clearExceptions(); + throw ErrorInfo(msg, script->location); + } + QStringList list; + if (result.isArray()) { + const int count = result.property(QLatin1String("length")).toInt32(); + list.reserve(count); + for (qint32 i = 0; i < count; ++i) { + QScriptValue item = result.property(i); + if (item.isValid() && !item.isUndefined()) + list.append(item.toString()); + } + } + return list; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/buildgraph/depscanner.h b/src/lib/corelib/buildgraph/depscanner.h new file mode 100644 index 00000000..342d88b3 --- /dev/null +++ b/src/lib/corelib/buildgraph/depscanner.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_DEPENDENCY_SCANNER_H +#define QBS_DEPENDENCY_SCANNER_H + +#include +#include +#include + +#include +#include + +class ScannerPlugin; + +namespace qbs { +namespace Internal { + +class Artifact; +class FileResourceBase; +class Logger; +class ScriptEngine; + +class DependencyScanner +{ +public: + virtual ~DependencyScanner() {} + + virtual QStringList collectSearchPaths(Artifact *artifact) = 0; + virtual QStringList collectDependencies(FileResourceBase *file) = 0; + virtual bool recursive() const = 0; + virtual const void *key() const = 0; +}; + +class PluginDependencyScanner : public DependencyScanner +{ +public: + PluginDependencyScanner(ScannerPlugin *plugin); + +private: + QStringList collectSearchPaths(Artifact *artifact); + QStringList collectDependencies(FileResourceBase *file); + bool recursive() const; + const void *key() const; + + ScannerPlugin* m_plugin; +}; + +class UserDependencyScanner : public DependencyScanner +{ +public: + UserDependencyScanner(const ResolvedScannerConstPtr &scanner, const Logger &logger); + ~UserDependencyScanner(); + +private: + QStringList collectSearchPaths(Artifact *artifact); + QStringList collectDependencies(FileResourceBase *file); + bool recursive() const; + const void *key() const; + + QStringList evaluate(Artifact *artifact, const ScriptFunctionPtr &script); + + ResolvedScannerConstPtr m_scanner; + Logger m_logger; + ScriptEngine *m_engine; + PrepareScriptObserver m_observer; + QScriptValue m_global; + void *m_product; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_DEPENDENCY_SCANNER_H diff --git a/src/lib/corelib/buildgraph/emptydirectoriesremover.cpp b/src/lib/corelib/buildgraph/emptydirectoriesremover.cpp new file mode 100644 index 00000000..3f96cf35 --- /dev/null +++ b/src/lib/corelib/buildgraph/emptydirectoriesremover.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "emptydirectoriesremover.h" + +#include "artifact.h" + +#include + +#include +#include + +namespace qbs { +namespace Internal { + +EmptyDirectoriesRemover::EmptyDirectoriesRemover(const TopLevelProject *project, + const Logger &logger) + : m_project(project), m_logger(logger) +{ +} + +void EmptyDirectoriesRemover::removeEmptyParentDirectories(const QStringList &artifactFilePaths) +{ + m_dirsToRemove.clear(); + m_handledDirs.clear(); + foreach (const QString &filePath, artifactFilePaths) + insertSorted(QFileInfo(filePath).absolutePath()); + while (!m_dirsToRemove.isEmpty()) + removeDirIfEmpty(); +} + +void EmptyDirectoriesRemover::removeEmptyParentDirectories(const ArtifactSet &artifacts) +{ + QStringList filePaths; + foreach (const Artifact * const a, artifacts) { + if (a->artifactType == Artifact::Generated) + filePaths << a->filePath(); + } + removeEmptyParentDirectories(filePaths); +} + +// List is sorted so that "deeper" directories come first. +void EmptyDirectoriesRemover::insertSorted(const QString &dirPath) +{ + int i; + for (i = 0; i < m_dirsToRemove.count(); ++i) { + const QString &cur = m_dirsToRemove.at(i); + if (dirPath == cur) + return; + if (dirPath.count(QLatin1Char('/')) > cur.count(QLatin1Char('/'))) + break; + } + m_dirsToRemove.insert(i, dirPath); +} + +void EmptyDirectoriesRemover::removeDirIfEmpty() +{ + const QString dirPath = m_dirsToRemove.takeFirst(); + m_handledDirs.insert(dirPath); + QFileInfo fi(dirPath); + if (fi.isSymLink() || !fi.exists() || !dirPath.startsWith(m_project->buildDirectory) + || fi.filePath() == m_project->buildDirectory) { + return; + } + QDir dir(dirPath); + dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + if (dir.count() != 0) + return; + dir.cdUp(); + if (!dir.rmdir(fi.fileName())) { + m_logger.qbsWarning() << QString::fromLatin1("Cannot remove empty directory '%1'.") + .arg(dirPath); + return; + } + const QString parentDir = dir.path(); + if (!m_handledDirs.contains(parentDir)) + insertSorted(parentDir); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/buildgraph/emptydirectoriesremover.h b/src/lib/corelib/buildgraph/emptydirectoriesremover.h new file mode 100644 index 00000000..518da32c --- /dev/null +++ b/src/lib/corelib/buildgraph/emptydirectoriesremover.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_EMPTYDIRECTORIESREMOVER_H +#define QBS_EMPTYDIRECTORIESREMOVER_H + +#include + +#include +#include + +namespace qbs { +namespace Internal { +class ArtifactSet; +class TopLevelProject; + +class EmptyDirectoriesRemover +{ +public: + EmptyDirectoriesRemover(const TopLevelProject *project, const Logger &logger); + void removeEmptyParentDirectories(const QStringList &artifactFilePaths); + void removeEmptyParentDirectories(const ArtifactSet &artifacts); + +private: + void insertSorted(const QString &dirPath); + void removeDirIfEmpty(); + + const TopLevelProject * const m_project; + Logger m_logger; + QStringList m_dirsToRemove; + QSet m_handledDirs; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp new file mode 100644 index 00000000..c1f3371e --- /dev/null +++ b/src/lib/corelib/buildgraph/executor.cpp @@ -0,0 +1,1182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "executor.h" + +#include "buildgraph.h" +#include "command.h" +#include "emptydirectoriesremover.h" +#include "productbuilddata.h" +#include "projectbuilddata.h" +#include "cycledetector.h" +#include "executorjob.h" +#include "inputartifactscanner.h" +#include "productinstaller.h" +#include "rescuableartifactdata.h" +#include "rulenode.h" +#include "rulesevaluationcontext.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace qbs { +namespace Internal { + +bool Executor::ComparePriority::operator() (const BuildGraphNode *x, const BuildGraphNode *y) const +{ + return x->product->buildData->buildPriority < y->product->buildData->buildPriority; +} + + +Executor::Executor(const Logger &logger, QObject *parent) + : QObject(parent) + , m_productInstaller(0) + , m_logger(logger) + , m_progressObserver(0) + , m_state(ExecutorIdle) + , m_cancelationTimer(new QTimer(this)) + , m_doTrace(logger.traceEnabled()) + , m_doDebug(logger.debugEnabled()) +{ + m_inputArtifactScanContext = new InputArtifactScannerContext(&m_scanResultCache); + m_cancelationTimer->setSingleShot(false); + m_cancelationTimer->setInterval(1000); + connect(m_cancelationTimer, &QTimer::timeout, this, &Executor::checkForCancellation); +} + +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_inputArtifactScanContext; + delete m_productInstaller; +} + +FileTime Executor::recursiveFileTime(const QString &filePath) const +{ + FileTime 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); + + if (m_buildOptions.changedFiles().isEmpty()) + artifact->setTimestamp(recursiveFileTime(artifact->filePath())); + else if (m_buildOptions.changedFiles().contains(artifact->filePath())) + artifact->setTimestamp(FileTime::currentTime()); + else if (!artifact->timestamp().isValid()) + artifact->setTimestamp(recursiveFileTime(artifact->filePath())); + + artifact->timestampRetrieved = true; +} + +void Executor::build() +{ + try { + m_partialBuild = m_productsToBuild.count() != m_project->allProducts().count(); + doBuild(); + } catch (const ErrorInfo &e) { + handleError(e); + } +} + +void Executor::setProject(const TopLevelProjectPtr &project) +{ + m_project = project; +} + +void Executor::setProducts(const QList &productsToBuild) +{ + m_productsToBuild = productsToBuild; +} + +class ProductPrioritySetter +{ + const TopLevelProject *m_topLevelProject; + unsigned int m_priority; + QSet m_seenProducts; +public: + ProductPrioritySetter(const TopLevelProject *tlp) + : m_topLevelProject(tlp) + { + } + + void apply() + { + QList allProducts = m_topLevelProject->allProducts(); + QSet allDependencies; + foreach (const ResolvedProductPtr &product, allProducts) + allDependencies += product->dependencies; + QSet 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_changedSourceArtifacts.clear(); + m_error.clear(); + m_explicitlyCanceled = false; + m_activeFileTags = FileTags::fromStringList(m_buildOptions.activeFileTags()); + m_artifactsRemovedFromDisk.clear(); + setState(ExecutorRunning); + + if (m_productsToBuild.isEmpty()) { + m_logger.qbsTrace() << "No products to build, finishing."; + QTimer::singleShot(0, this, &Executor::finish); // Don't call back on the caller. + return; + } + + doSanityChecks(); + QBS_CHECK(!m_project->buildData->evaluationContext); + m_project->buildData->evaluationContext + = RulesEvaluationContextPtr(new RulesEvaluationContext(m_logger)); + m_evalContext = m_project->buildData->evaluationContext; + + m_elapsedTimeRules = m_elapsedTimeScanners = m_elapsedTimeInstalling = 0; + m_evalContext->engine()->enableProfiling(m_buildOptions.logElapsedTime()); + + InstallOptions installOptions; + installOptions.setDryRun(m_buildOptions.dryRun()); + installOptions.setInstallRoot(m_productsToBuild.first()->moduleProperties + ->qbsPropertyValue(QLatin1String("installRoot")).toString()); + installOptions.setKeepGoing(m_buildOptions.keepGoing()); + m_productInstaller = new ProductInstaller(m_project, m_productsToBuild, installOptions, + m_progressObserver, m_logger); + if (m_buildOptions.removeExistingInstallation()) + m_productInstaller->removeInstallRoot(); + + addExecutorJobs(); + prepareAllNodes(); + prepareProducts(); + setupRootNodes(); + prepareReachableNodes(); + setupProgressObserver(); + initLeaves(); + if (!scheduleJobs()) { + m_logger.qbsTrace() << "Nothing to do at all, finishing."; + QTimer::singleShot(0, this, &Executor::finish); // Don't call back on the caller. + } + if (m_progressObserver) + m_cancelationTimer->start(); +} + +void Executor::setBuildOptions(const BuildOptions &buildOptions) +{ + m_buildOptions = buildOptions; +} + + +void Executor::initLeaves() +{ + updateLeaves(m_roots); +} + +void Executor::updateLeaves(const NodeSet &nodes) +{ + NodeSet seenNodes; + foreach (BuildGraphNode * const node, nodes) + updateLeaves(node, seenNodes); +} + +void Executor::updateLeaves(BuildGraphNode *node, NodeSet &seenNodes) +{ + if (seenNodes.contains(node)) + return; + seenNodes += node; + + // Artifacts that appear in the build graph after + // prepareBuildGraph() has been called, must be initialized. + if (node->buildState == BuildGraphNode::Untouched) { + node->buildState = BuildGraphNode::Buildable; + Artifact *artifact = dynamic_cast(node); + if (artifact && artifact->artifactType == Artifact::SourceFile) + retrieveSourceFileTimestamp(artifact); + } + + bool isLeaf = true; + foreach (BuildGraphNode *child, node->children) { + if (child->buildState != BuildGraphNode::Built) { + isLeaf = false; + updateLeaves(child, seenNodes); + } + } + + if (isLeaf) { + if (m_doDebug) + m_logger.qbsDebug() << "[EXEC] adding leaf " << node->toString(); + m_leaves.push(node); + } +} + +// 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()) { + BuildGraphNode * const nodeToBuild = m_leaves.top(); + m_leaves.pop(); + + switch (nodeToBuild->buildState) { + case BuildGraphNode::Untouched: + QBS_ASSERT(!"untouched node in leaves list", + qDebug("%s", qPrintable(nodeToBuild->toString()))); + break; + case BuildGraphNode::Buildable: + // This is the only state in which we want to build a node. + nodeToBuild->accept(this); + break; + case BuildGraphNode::Building: + if (m_doDebug) { + m_logger.qbsDebug() << "[EXEC] " << nodeToBuild->toString(); + m_logger.qbsDebug() << "[EXEC] node is currently being built. Skipping."; + } + break; + case BuildGraphNode::Built: + if (m_doDebug) { + m_logger.qbsDebug() << "[EXEC] " << nodeToBuild->toString(); + m_logger.qbsDebug() << "[EXEC] node already built. Skipping."; + } + break; + } + } + return !m_leaves.empty() || !m_processingJobs.isEmpty(); +} + +bool Executor::isUpToDate(Artifact *artifact) const +{ + QBS_CHECK(artifact->artifactType == Artifact::Generated); + + if (m_doDebug) { + m_logger.qbsDebug() << "[UTD] check " << artifact->filePath() << " " + << artifact->timestamp().toString(); + } + + if (m_buildOptions.forceTimestampCheck()) { + artifact->setTimestamp(FileInfo(artifact->filePath()).lastModified()); + if (m_doDebug) { + m_logger.qbsDebug() << "[UTD] timestamp retrieved from filesystem: " + << artifact->timestamp().toString(); + } + } + + if (!artifact->timestamp().isValid()) { + if (m_doDebug) + m_logger.qbsDebug() << "[UTD] invalid timestamp. Out of date."; + return false; + } + + for (Artifact *childArtifact : filterByType(artifact->children)) { + QBS_CHECK(childArtifact->timestamp().isValid()); + if (m_doDebug) { + m_logger.qbsDebug() << "[UTD] child timestamp " + << childArtifact->timestamp().toString() << " " + << childArtifact->filePath(); + } + if (artifact->timestamp() < childArtifact->timestamp()) + return false; + } + + foreach (FileDependency *fileDependency, artifact->fileDependencies) { + if (!fileDependency->timestamp().isValid()) { + FileInfo fi(fileDependency->filePath()); + fileDependency->setTimestamp(fi.lastModified()); + if (!fileDependency->timestamp().isValid()) { + if (m_doDebug) { + m_logger.qbsDebug() << "[UTD] file dependency doesn't exist " + << fileDependency->filePath(); + } + return false; + } + } + if (m_doDebug) { + m_logger.qbsDebug() << "[UTD] file dependency timestamp " + << fileDependency->timestamp().toString() << " " + << fileDependency->filePath(); + } + if (artifact->timestamp() < fileDependency->timestamp()) + return false; + } + + return true; +} + +bool Executor::mustExecuteTransformer(const TransformerPtr &transformer) const +{ + if (transformer->alwaysRun) + return true; + bool hasAlwaysUpdatedArtifacts = false; + foreach (Artifact *artifact, transformer->outputs) { + if (artifact->alwaysUpdated) + hasAlwaysUpdatedArtifacts = true; + else if (!m_buildOptions.forceTimestampCheck()) + continue; + if (!isUpToDate(artifact)) + return true; + } + + // If all artifacts in a transformer have "alwaysUpdated" set to false, that transformer + // is always run. + return !hasAlwaysUpdatedArtifacts; +} + +void Executor::buildArtifact(Artifact *artifact) +{ + if (m_doDebug) + m_logger.qbsDebug() << "[EXEC] " << relativeArtifactFileName(artifact); + + QBS_CHECK(artifact->buildState == BuildGraphNode::Buildable); + + if (artifact->artifactType != Artifact::SourceFile && !checkNodeProduct(artifact)) + 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::fromLatin1("[EXEC] artifact type %1. Skipping.") + .arg(toString(artifact->artifactType)); + finishArtifact(artifact); + return; + } + + // Every generated artifact must have a transformer. + QBS_CHECK(artifact->transformer); + potentiallyRunTransformer(artifact->transformer); +} + +void Executor::executeRuleNode(RuleNode *ruleNode) +{ + AccumulatingTimer rulesTimer(m_buildOptions.logElapsedTime() ? &m_elapsedTimeRules : nullptr); + + if (!checkNodeProduct(ruleNode)) + return; + + QBS_CHECK(!m_evalContext->isActive()); + ArtifactSet changedInputArtifacts; + if (ruleNode->rule()->isDynamic()) { + foreach (Artifact *artifact, m_changedSourceArtifacts) { + if (artifact->product != ruleNode->product) + continue; + if (ruleNode->rule()->acceptsAsInput(artifact)) + changedInputArtifacts += artifact; + } + for (Artifact *artifact : filterByType(ruleNode->product->buildData->nodes)) { + if (artifact->artifactType == Artifact::SourceFile) + continue; + if (ruleNode->rule()->acceptsAsInput(artifact)) { + for (const Artifact * const parent : artifact->parentArtifacts()) { + if (parent->transformer->rule != ruleNode->rule()) + continue; + if (!parent->alwaysUpdated) + continue; + if (parent->timestamp() < artifact->timestamp()) { + changedInputArtifacts += artifact; + break; + } + } + } + } + } + + RuleNode::ApplicationResult result; + ruleNode->apply(m_logger, changedInputArtifacts, &result); + + if (result.upToDate) { + if (m_doDebug) + m_logger.qbsDebug() << "[EXEC] " << ruleNode->toString() + << " is up to date. Skipping."; + } else { + if (m_doDebug) + m_logger.qbsDebug() << "[EXEC] " << ruleNode->toString(); + const WeakPointer &product = ruleNode->product; + QSet parentRules; + if (!result.createdNodes.isEmpty()) { + foreach (BuildGraphNode *parent, ruleNode->parents) { + if (RuleNode *parentRule = dynamic_cast(parent)) + parentRules += parentRule; + } + } + foreach (BuildGraphNode *node, result.createdNodes) { + if (m_doDebug) + m_logger.qbsDebug() << "[EXEC] rule created " << node->toString(); + loggedConnect(node, ruleNode, m_logger); + Artifact *outputArtifact = dynamic_cast(node); + if (!outputArtifact) + continue; + if (outputArtifact->fileTags().matches(product->fileTags)) + product->buildData->roots += outputArtifact; + + foreach (Artifact *inputArtifact, outputArtifact->transformer->inputs) + loggedConnect(ruleNode, inputArtifact, m_logger); + + foreach (RuleNode *parentRule, parentRules) + loggedConnect(parentRule, outputArtifact, m_logger); + } + updateLeaves(result.createdNodes); + updateLeaves(result.invalidatedNodes); + } + finishNode(ruleNode); + if (m_progressObserver) + m_progressObserver->incrementProgressValue(); +} + +void Executor::finishJob(ExecutorJob *job, bool success) +{ + QBS_CHECK(job); + QBS_CHECK(m_state != ExecutorIdle); + + const JobMap::Iterator it = m_processingJobs.find(job); + QBS_CHECK(it != m_processingJobs.end()); + const TransformerPtr transformer = it.value(); + m_processingJobs.erase(it); + m_availableJobs.append(job); + if (success) { + m_project->buildData->isDirty = true; + foreach (Artifact *artifact, transformer->outputs) { + if (artifact->alwaysUpdated) { + artifact->setTimestamp(FileTime::currentTime()); + if (m_buildOptions.forceOutputCheck() + && !m_buildOptions.dryRun() && !FileInfo(artifact->filePath()).exists()) { + if (transformer->rule) { + if (!transformer->rule->name.isEmpty()) { + throw ErrorInfo(tr("Rule '%1' declares artifact '%2', " + "but the artifact was not produced.") + .arg(transformer->rule->name, artifact->filePath())); + } + throw ErrorInfo(tr("Rule declares artifact '%1', " + "but the artifact was not produced.") + .arg(artifact->filePath())); + } + throw ErrorInfo(tr("Transformer declares artifact '%1', " + "but the artifact was not produced.") + .arg(artifact->filePath())); + } + } else { + artifact->setTimestamp(FileInfo(artifact->filePath()).lastModified()); + } + } + finishTransformer(transformer); + } + + 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(BuildGraphNode *node) +{ + foreach (BuildGraphNode *child, node->children) + if (child->buildState != BuildGraphNode::Built) + return false; + return true; +} + +void Executor::finishNode(BuildGraphNode *leaf) +{ + leaf->buildState = BuildGraphNode::Built; + foreach (BuildGraphNode *parent, leaf->parents) { + if (parent->buildState != BuildGraphNode::Buildable) { + if (m_doTrace) { + m_logger.qbsTrace() << "[EXEC] parent " << parent->toString() + << " build state: " << toString(parent->buildState); + } + continue; + } + + if (allChildrenBuilt(parent)) { + m_leaves.push(parent); + if (m_doTrace) { + m_logger.qbsTrace() << "[EXEC] finishNode adds leaf " + << parent->toString() << " " << toString(parent->buildState); + } + } else { + if (m_doTrace) { + m_logger.qbsTrace() << "[EXEC] parent " << parent->toString() + << " build state: " << toString(parent->buildState); + } + } + } +} + +void Executor::finishArtifact(Artifact *leaf) +{ + QBS_CHECK(leaf); + if (m_doTrace) + m_logger.qbsTrace() << "[EXEC] finishArtifact " << relativeArtifactFileName(leaf); + + finishNode(leaf); + m_scanResultCache.remove(leaf->filePath()); +} + +QString Executor::configString() const +{ + return tr(" for configuration %1").arg(m_project->id()); +} + +bool Executor::transformerHasMatchingOutputTags(const TransformerConstPtr &transformer) const +{ + if (m_activeFileTags.isEmpty()) + return true; // No filtering requested. + + foreach (Artifact * const output, transformer->outputs) { + if (artifactHasMatchingOutputTags(output)) + return true; + } + + return false; +} + +bool Executor::artifactHasMatchingOutputTags(const Artifact *artifact) const +{ + return m_activeFileTags.matches(artifact->fileTags()); +} + +bool Executor::transformerHasMatchingInputFiles(const TransformerConstPtr &transformer) const +{ + if (m_buildOptions.filesToConsider().isEmpty()) + return true; // No filtering requested. + + foreach (const Artifact * const input, transformer->inputs) { + foreach (const QString &filePath, m_buildOptions.filesToConsider()) { + if (input->filePath() == filePath) + return true; + } + } + + return false; +} + +void Executor::cancelJobs() +{ + m_logger.qbsTrace() << "Canceling all jobs."; + setState(ExecutorCanceling); + QList jobs = m_processingJobs.keys(); + foreach (ExecutorJob *job, jobs) + job->cancel(); +} + +void Executor::setupProgressObserver() +{ + if (!m_progressObserver) + return; + int totalEffort = 1; // For the effort after the last rule application; + foreach (const ResolvedProductConstPtr &product, m_productsToBuild) { + QBS_CHECK(product->buildData); + foreach (const BuildGraphNode * const node, product->buildData->nodes) { + const RuleNode * const ruleNode = dynamic_cast(node); + if (ruleNode) + ++totalEffort; + } + } + 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.append(error.toString()); + if (m_processingJobs.isEmpty()) + finish(); + else + cancelJobs(); +} + +void Executor::addExecutorJobs() +{ + m_logger.qbsDebug() << QString::fromLatin1("[EXEC] preparing executor for %1 jobs in parallel") + .arg(m_buildOptions.maxJobCount()); + for (int i = 1; i <= m_buildOptions.maxJobCount(); i++) { + ExecutorJob *job = new ExecutorJob(m_logger, this); + job->setMainThreadScriptEngine(m_evalContext->engine()); + job->setObjectName(QString::fromLatin1("J%1").arg(i)); + job->setDryRun(m_buildOptions.dryRun()); + job->setEchoMode(m_buildOptions.echoMode()); + m_availableJobs.append(job); + connect(job, &ExecutorJob::reportCommandDescription, + this, &Executor::reportCommandDescription, Qt::QueuedConnection); + connect(job, &ExecutorJob::reportProcessResult, + this, &Executor::reportProcessResult, Qt::QueuedConnection); + connect(job, &ExecutorJob::finished, + this, &Executor::onJobFinished, Qt::QueuedConnection); + } +} + +void Executor::rescueOldBuildData(Artifact *artifact, bool *childrenAdded = 0) +{ + if (childrenAdded) + *childrenAdded = false; + if (!artifact->oldDataPossiblyPresent) + return; + artifact->oldDataPossiblyPresent = false; + if (artifact->artifactType != Artifact::Generated) + return; + + ResolvedProduct * const product = artifact->product.data(); + AllRescuableArtifactData::Iterator it + = product->buildData->rescuableArtifactData.find(artifact->filePath()); + if (it == product->buildData->rescuableArtifactData.end()) + return; + + const RescuableArtifactData &rad = it.value(); + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << QString::fromLatin1("[BG] Attempting to rescue data of " + "artifact '%1'").arg(artifact->fileName()); + } + + typedef QPair ChildArtifactData; + QList childrenToConnect; + bool canRescue = commandListsAreEqual(artifact->transformer->commands, rad.commands); + if (canRescue) { + ResolvedProductPtr pseudoProduct = ResolvedProduct::create(); + foreach (const RescuableArtifactData::ChildData &cd, rad.children) { + pseudoProduct->name = cd.productName; + pseudoProduct->profile = cd.productProfile; + Artifact * const child = lookupArtifact(pseudoProduct, m_project->buildData.data(), + cd.childFilePath, true); + if (artifact->children.contains(child)) + continue; + if (!child) { + // If a child has disappeared, we must re-build even if the commands + // are the same. Example: Header file included in cpp file does not exist anymore. + canRescue = false; + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "Former child artifact '" << cd.childFilePath + << "' does not exist anymore"; + break; + } + // TODO: Shouldn't addedByScanner always be true here? Otherwise the child would be + // in the list already, no? + childrenToConnect << qMakePair(child, cd.addedByScanner); + } + + if (canRescue) { + const TypeFilter childArtifacts(artifact->children); + const int newChildCount = childrenToConnect.count() + + std::distance(childArtifacts.begin(), childArtifacts.end()); + QBS_CHECK(newChildCount >= rad.children.count()); + if (newChildCount > rad.children.count()) { + canRescue = false; + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "Artifact has children not present in rescue data"; + } + } + } else { + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "Transformer commands changed."; + } + + if (canRescue) { + artifact->transformer->propertiesRequestedInPrepareScript + = rad.propertiesRequestedInPrepareScript; + artifact->transformer->propertiesRequestedInCommands + = rad.propertiesRequestedInCommands; + artifact->transformer->propertiesRequestedFromArtifactInPrepareScript + = rad.propertiesRequestedFromArtifactInPrepareScript; + artifact->transformer->propertiesRequestedFromArtifactInCommands + = rad.propertiesRequestedFromArtifactInCommands; + artifact->setTimestamp(rad.timeStamp); + if (childrenAdded && !childrenToConnect.isEmpty()) + *childrenAdded = true; + foreach (const ChildArtifactData &cad, childrenToConnect) { + safeConnect(artifact, cad.first, m_logger); + if (cad.second) + artifact->childrenAddedByScanner << cad.first; + } + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "Data was rescued."; + } else { + removeGeneratedArtifactFromDisk(artifact, m_logger); + m_artifactsRemovedFromDisk << artifact->filePath(); + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "Data not rescued."; + } + product->buildData->rescuableArtifactData.erase(it); +} + +bool Executor::checkForUnbuiltDependencies(Artifact *artifact) +{ + bool buildingDependenciesFound = false; + NodeSet unbuiltDependencies; + foreach (BuildGraphNode *dependency, artifact->children) { + switch (dependency->buildState) { + case BuildGraphNode::Untouched: + case BuildGraphNode::Buildable: + if (m_logger.debugEnabled()) { + m_logger.qbsDebug() << "[EXEC] unbuilt dependency: " + << dependency->toString(); + } + unbuiltDependencies += dependency; + break; + case BuildGraphNode::Building: { + if (m_logger.debugEnabled()) { + m_logger.qbsDebug() << "[EXEC] dependency in state 'Building': " + << dependency->toString(); + } + buildingDependenciesFound = true; + break; + } + case BuildGraphNode::Built: + // do nothing + break; + } + } + if (!unbuiltDependencies.isEmpty()) { + artifact->inputsScanned = false; + updateLeaves(unbuiltDependencies); + return true; + } + if (buildingDependenciesFound) { + artifact->inputsScanned = false; + return true; + } + return false; +} + +void Executor::potentiallyRunTransformer(const TransformerPtr &transformer) +{ + foreach (Artifact * const output, transformer->outputs) { + // Rescuing build data can introduce new dependencies, potentially delaying execution of + // this transformer. + bool childrenAddedDueToRescue; + rescueOldBuildData(output, &childrenAddedDueToRescue); + if (childrenAddedDueToRescue && checkForUnbuiltDependencies(output)) + return; + } + + if (!transformerHasMatchingOutputTags(transformer)) { + if (m_doDebug) + m_logger.qbsDebug() << "[EXEC] file tags do not match. Skipping."; + finishTransformer(transformer); + return; + } + + if (!transformerHasMatchingInputFiles(transformer)) { + if (m_doDebug) + m_logger.qbsDebug() << "[EXEC] input files do not match. Skipping."; + finishTransformer(transformer); + return; + } + + if (!mustExecuteTransformer(transformer)) { + if (m_doDebug) + m_logger.qbsDebug() << "[EXEC] Up to date. Skipping."; + finishTransformer(transformer); + return; + } + + foreach (Artifact * const output, transformer->outputs) { + // Scan all input artifacts. If new dependencies were found during scanning, delay + // execution of this transformer. + InputArtifactScanner scanner(output, m_inputArtifactScanContext, m_logger); + AccumulatingTimer scanTimer(m_buildOptions.logElapsedTime() + ? &m_elapsedTimeScanners : nullptr); + scanner.scan(); + scanTimer.stop(); + if (scanner.newDependencyAdded() && checkForUnbuiltDependencies(output)) + return; + } + + if (m_buildOptions.executeRulesOnly()) + finishTransformer(transformer); + else + runTransformer(transformer); +} + +void Executor::runTransformer(const TransformerPtr &transformer) +{ + QBS_CHECK(transformer); + + // create the output directories + if (!m_buildOptions.dryRun()) { + ArtifactSet::const_iterator it = transformer->outputs.begin(); + for (; it != transformer->outputs.end(); ++it) { + Artifact *output = *it; + QDir outDir = QFileInfo(output->filePath()).absoluteDir(); + if (!outDir.exists() && !outDir.mkpath(QLatin1String("."))) { + throw ErrorInfo(tr("Failed to create directory '%1'.") + .arg(QDir::toNativeSeparators(outDir.absolutePath()))); + } + } + } + + QBS_CHECK(!m_availableJobs.isEmpty()); + ExecutorJob *job = m_availableJobs.takeFirst(); + foreach (Artifact * const artifact, transformer->outputs) + artifact->buildState = BuildGraphNode::Building; + m_processingJobs.insert(job, transformer); + job->run(transformer.data()); +} + +void Executor::finishTransformer(const TransformerPtr &transformer) +{ + foreach (Artifact * const artifact, transformer->outputs) { + possiblyInstallArtifact(artifact); + finishArtifact(artifact); + } +} + +void Executor::possiblyInstallArtifact(const Artifact *artifact) +{ + AccumulatingTimer installTimer(m_buildOptions.logElapsedTime() + ? &m_elapsedTimeInstalling : nullptr); + + if (m_buildOptions.install() && !m_buildOptions.executeRulesOnly() + && (m_activeFileTags.isEmpty() || artifactHasMatchingOutputTags(artifact)) + && artifact->properties->qbsPropertyValue(QLatin1String("install")).toBool()) { + m_productInstaller->copyFile(artifact); + } +} + +void Executor::onJobFinished(const qbs::ErrorInfo &err) +{ + try { + ExecutorJob * const job = qobject_cast(sender()); + QBS_CHECK(job); + if (m_evalContext->isActive()) { + m_logger.qbsDebug() << "Executor job finished while rule execution is pausing. " + "Delaying slot execution."; + QTimer::singleShot(0, job, [job, err] { job->finished(err); }); + return; + } + + if (err.hasError()) { + if (m_buildOptions.keepGoing()) { + ErrorInfo fullWarning(err); + fullWarning.prepend(Tr::tr("Ignoring the following errors on user request:")); + m_logger.printWarning(fullWarning); + } else { + if (!m_error.hasError()) + m_error = err; // All but the first one could be due to canceling. + } + } + + finishJob(job, !err.hasError()); + } catch (const ErrorInfo &error) { + handleError(error); + } +} + +void Executor::checkForUnbuiltProducts() +{ + if (m_buildOptions.executeRulesOnly()) + return; + QList unbuiltProducts; + foreach (const ResolvedProductPtr &product, m_productsToBuild) { + bool productBuilt = true; + foreach (BuildGraphNode *rootNode, product->buildData->roots) { + if (rootNode->buildState != BuildGraphNode::Built) { + productBuilt = false; + unbuiltProducts += product; + break; + } + } + if (productBuilt) { + // Any element still left after a successful build has not been re-created + // by any rule and therefore does not exist anymore as an artifact. + foreach (const QString &filePath, product->buildData->rescuableArtifactData.keys()) { + removeGeneratedArtifactFromDisk(filePath, m_logger); + m_artifactsRemovedFromDisk << filePath; + } + product->buildData->rescuableArtifactData.clear(); + } + } + + if (unbuiltProducts.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:").arg(configString())); + foreach (const ResolvedProductConstPtr &p, unbuiltProducts) { + QString errorMessage = Tr::tr("\t%1").arg(p->name); + if (p->profile != m_project->profile()) + errorMessage += Tr::tr(" (for profile '%1')").arg(p->profile); + m_error.append(errorMessage); + } + } +} + +bool Executor::checkNodeProduct(BuildGraphNode *node) +{ + if (!m_partialBuild || m_productsToBuild.contains(node->product)) + return true; + + // TODO: Turn this into a warning once we have a reliable C++ scanner. + m_logger.qbsTrace() << "Ignoring node " << node->toString() << ", which belongs to a " + "product that is not part of the list of products to build. " + "Possible reasons are an erroneous project design or a false " + "positive from a dependency scanner."; + finishNode(node); + return false; +} + +void Executor::finish() +{ + QBS_ASSERT(m_state != ExecutorIdle, /* ignore */); + QBS_ASSERT(!m_evalContext || !m_evalContext->isActive(), /* ignore */); + + checkForUnbuiltProducts(); + if (m_explicitlyCanceled) { + QString message = Tr::tr(m_buildOptions.executeRulesOnly() + ? "Rule execution canceled" : "Build canceled"); + m_error.append(Tr::tr("%1%2.").arg(message, configString())); + } + setState(ExecutorIdle); + if (m_progressObserver) { + m_progressObserver->setFinished(); + m_cancelationTimer->stop(); + } + + EmptyDirectoriesRemover(m_project.data(), m_logger) + .removeEmptyParentDirectories(m_artifactsRemovedFromDisk); + + if (m_buildOptions.logElapsedTime()) { + m_logger.qbsLog(LoggerInfo, true) << "\t" << Tr::tr("Rule execution took %1.") + .arg(elapsedTimeString(m_elapsedTimeRules)); + m_logger.qbsLog(LoggerInfo, true) << "\t" << Tr::tr("Artifact scanning took %1.") + .arg(elapsedTimeString(m_elapsedTimeScanners)); + m_logger.qbsLog(LoggerInfo, true) << "\t" << Tr::tr("Installing artifacts took %1.") + .arg(elapsedTimeString(m_elapsedTimeInstalling)); + } + + emit finished(); +} + +void Executor::checkForCancellation() +{ + QBS_ASSERT(m_progressObserver, return); + if (m_state == ExecutorRunning && m_progressObserver->canceled()) { + cancelJobs(); + if (m_evalContext->isActive()) + m_evalContext->engine()->cancel(); + } +} + +bool Executor::visit(Artifact *artifact) +{ + QBS_CHECK(artifact->buildState != BuildGraphNode::Untouched); + buildArtifact(artifact); + return false; +} + +bool Executor::visit(RuleNode *ruleNode) +{ + QBS_CHECK(ruleNode->buildState != BuildGraphNode::Untouched); + executeRuleNode(ruleNode); + return false; +} + +/** + * 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 also fills the list of changed source files. + */ +void Executor::prepareAllNodes() +{ + foreach (const ResolvedProductPtr &product, m_project->allProducts()) { + if (product->enabled) { + QBS_CHECK(product->buildData); + foreach (BuildGraphNode * const node, product->buildData->nodes) + node->buildState = BuildGraphNode::Untouched; + } + } + foreach (const ResolvedProductPtr &product, m_productsToBuild) { + QBS_CHECK(product->buildData); + for (Artifact * const artifact : filterByType(product->buildData->nodes)) + prepareArtifact(artifact); + } +} + +void Executor::prepareArtifact(Artifact *artifact) +{ + artifact->inputsScanned = false; + artifact->timestampRetrieved = false; + + if (artifact->artifactType == Artifact::SourceFile) { + const FileTime oldTimestamp = artifact->timestamp(); + retrieveSourceFileTimestamp(artifact); + if (oldTimestamp != artifact->timestamp()) + m_changedSourceArtifacts.append(artifact); + possiblyInstallArtifact(artifact); + } + + // 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::prepareReachableNodes() +{ + foreach (BuildGraphNode *root, m_roots) + prepareReachableNodes_impl(root); +} + +void Executor::prepareReachableNodes_impl(BuildGraphNode *node) +{ + if (node->buildState != BuildGraphNode::Untouched) + return; + + node->buildState = BuildGraphNode::Buildable; + foreach (BuildGraphNode *child, node->children) + prepareReachableNodes_impl(child); +} + +void Executor::prepareProducts() +{ + ProductPrioritySetter prioritySetter(m_project.data()); + prioritySetter.apply(); + foreach (ResolvedProductPtr product, m_productsToBuild) + product->setupBuildEnvironment(m_evalContext->engine(), m_project->environment); +} + +void Executor::setupRootNodes() +{ + m_roots.clear(); + foreach (const ResolvedProductPtr &product, m_productsToBuild) { + foreach (BuildGraphNode *root, product->buildData->roots) + m_roots += root; + } +} + +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 00000000..a1bb26c0 --- /dev/null +++ b/src/lib/corelib/buildgraph/executor.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_BUILDGRAPHEXECUTOR_H +#define QBS_BUILDGRAPHEXECUTOR_H + +#include "forward_decls.h" +#include "buildgraphvisitor.h" +#include +#include +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + +namespace qbs { +class ProcessResult; + +namespace Internal { +class ExecutorJob; +class FileTime; +class InputArtifactScannerContext; +class ProductInstaller; +class ProgressObserver; +class RuleNode; + +class Executor : public QObject, private BuildGraphVisitor +{ + Q_OBJECT + +public: + void build(); + + Executor(const Logger &logger, QObject *parent = 0); + ~Executor(); + + void setProject(const TopLevelProjectPtr &project); + void setProducts(const QList &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: + void onJobFinished(const qbs::ErrorInfo &err); + void finish(); + void checkForCancellation(); + + // BuildGraphVisitor implementation + bool visit(Artifact *artifact); + bool visit(RuleNode *ruleNode); + + enum ExecutorState { ExecutorIdle, ExecutorRunning, ExecutorCanceling }; + + struct ComparePriority + { + bool operator() (const BuildGraphNode *x, const BuildGraphNode *y) const; + }; + + typedef std::priority_queue, + ComparePriority> Leaves; + + void doBuild(); + void prepareAllNodes(); + void prepareArtifact(Artifact *artifact); + void prepareReachableNodes(); + void prepareReachableNodes_impl(BuildGraphNode *node); + void prepareProducts(); + void setupRootNodes(); + void initLeaves(); + void updateLeaves(const NodeSet &nodes); + void updateLeaves(BuildGraphNode *node, NodeSet &seenNodes); + bool scheduleJobs(); + void buildArtifact(Artifact *artifact); + void executeRuleNode(RuleNode *ruleNode); + void finishJob(ExecutorJob *job, bool success); + void finishNode(BuildGraphNode *leaf); + void finishArtifact(Artifact *artifact); + void setState(ExecutorState); + void addExecutorJobs(); + void cancelJobs(); + void setupProgressObserver(); + void doSanityChecks(); + void handleError(const ErrorInfo &error); + void rescueOldBuildData(Artifact *artifact, bool *childrenAdded); + bool checkForUnbuiltDependencies(Artifact *artifact); + void potentiallyRunTransformer(const TransformerPtr &transformer); + void runTransformer(const TransformerPtr &transformer); + void finishTransformer(const TransformerPtr &transformer); + void possiblyInstallArtifact(const Artifact *artifact); + void checkForUnbuiltProducts(); + bool checkNodeProduct(BuildGraphNode *node); + + bool mustExecuteTransformer(const TransformerPtr &transformer) const; + bool isUpToDate(Artifact *artifact) const; + void retrieveSourceFileTimestamp(Artifact *artifact) const; + FileTime recursiveFileTime(const QString &filePath) const; + QString configString() const; + bool transformerHasMatchingOutputTags(const TransformerConstPtr &transformer) const; + bool artifactHasMatchingOutputTags(const Artifact *artifact) const; + bool transformerHasMatchingInputFiles(const TransformerConstPtr &transformer) const; + + typedef QHash JobMap; + JobMap m_processingJobs; + + ProductInstaller *m_productInstaller; + RulesEvaluationContextPtr m_evalContext; + BuildOptions m_buildOptions; + Logger m_logger; + ProgressObserver *m_progressObserver; + QList m_availableJobs; + ExecutorState m_state; + TopLevelProjectPtr m_project; + QList m_productsToBuild; + NodeSet m_roots; + Leaves m_leaves; + QList m_changedSourceArtifacts; + ScanResultCache m_scanResultCache; + InputArtifactScannerContext *m_inputArtifactScanContext; + ErrorInfo m_error; + bool m_explicitlyCanceled; + FileTags m_activeFileTags; + QTimer * const m_cancelationTimer; + QStringList m_artifactsRemovedFromDisk; + bool m_partialBuild; + qint64 m_elapsedTimeRules; + qint64 m_elapsedTimeScanners; + qint64 m_elapsedTimeInstalling; + 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 00000000..5586e75f --- /dev/null +++ b/src/lib/corelib/buildgraph/executorjob.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "executorjob.h" + +#include "artifact.h" +#include "command.h" +#include "jscommandexecutor.h" +#include "processcommandexecutor.h" +#include "transformer.h" +#include +#include +#include + +#include + +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, &AbstractCommandExecutor::reportCommandDescription, + this, &ExecutorJob::reportCommandDescription); + connect(m_processCommandExecutor, &ProcessCommandExecutor::reportProcessResult, + this, &ExecutorJob::reportProcessResult); + connect(m_processCommandExecutor, &AbstractCommandExecutor::finished, + this, &ExecutorJob::onCommandFinished); + connect(m_jsCommandExecutor, &AbstractCommandExecutor::reportCommandDescription, + this, &ExecutorJob::reportCommandDescription); + connect(m_jsCommandExecutor, &AbstractCommandExecutor::finished, + this, &ExecutorJob::onCommandFinished); + reset(); +} + +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::setEchoMode(CommandEchoMode echoMode) +{ + m_processCommandExecutor->setEchoMode(echoMode); + m_jsCommandExecutor->setEchoMode(echoMode); +} + +void ExecutorJob::run(Transformer *t) +{ + QBS_ASSERT(m_currentCommandIdx == -1, return); + + if (t->commands.isEmpty()) { + setFinished(); + return; + } + + t->propertiesRequestedInCommands.clear(); + QBS_CHECK(!t->outputs.isEmpty()); + m_processCommandExecutor->setProcessEnvironment( + (*t->outputs.begin())->product->buildEnvironment); + m_transformer = t; + runNextCommand(); +} + +void ExecutorJob::cancel() +{ + if (!m_currentCommandExecutor) + return; + m_error = ErrorInfo(tr("Transformer execution canceled.")); + m_currentCommandExecutor->cancel(); +} + +void ExecutorJob::runNextCommand() +{ + QBS_ASSERT(m_currentCommandIdx <= m_transformer->commands.count(), return); + ++m_currentCommandIdx; + if (m_currentCommandIdx >= m_transformer->commands.count()) { + setFinished(); + return; + } + + const AbstractCommandPtr &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.data()); +} + +void ExecutorJob::onCommandFinished(const ErrorInfo &err) +{ + QBS_ASSERT(m_transformer, return); + if (m_error.hasError()) { // Canceled? + setFinished(); + } else if (err.hasError()) { + m_error = err; + setFinished(); + } else { + runNextCommand(); + } +} + +void ExecutorJob::setFinished() +{ + const ErrorInfo err = m_error; + reset(); + emit finished(err); +} + +void ExecutorJob::reset() +{ + m_transformer = 0; + m_currentCommandExecutor = 0; + m_currentCommandIdx = -1; + m_error.clear(); +} + +} // 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 00000000..b5a76c23 --- /dev/null +++ b/src/lib/corelib/buildgraph/executorjob.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_EXECUTORJOB_H +#define QBS_EXECUTORJOB_H + +#include +#include +#include + +#include + +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 setEchoMode(CommandEchoMode echoMode); + void run(Transformer *t); + void cancel(); + +signals: + void reportCommandDescription(const QString &highlight, const QString &message); + void reportProcessResult(const qbs::ProcessResult &result); + void finished(const qbs::ErrorInfo &error = ErrorInfo()); // !hasError() <=> command successful + +private: + void runNextCommand(); + void onCommandFinished(const qbs::ErrorInfo &err); + + void setFinished(); + void reset(); + + AbstractCommandExecutor *m_currentCommandExecutor; + ProcessCommandExecutor *m_processCommandExecutor; + JsCommandExecutor *m_jsCommandExecutor; + Transformer *m_transformer; + int m_currentCommandIdx; + ErrorInfo m_error; +}; + +} // 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 00000000..d57db17c --- /dev/null +++ b/src/lib/corelib/buildgraph/filedependency.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filedependency.h" + +#include +#include + +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 00000000..b4ff9381 --- /dev/null +++ b/src/lib/corelib/buildgraph/filedependency.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_FILEDEPENDENCY_H +#define QBS_FILEDEPENDENCY_H + +#include +#include + +namespace qbs { +namespace Internal { + +class FileResourceBase : public virtual PersistentObject +{ +protected: + FileResourceBase(); + +public: + ~FileResourceBase(); + + void setTimestamp(const FileTime &t); + const FileTime ×tamp() 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 00000000..5c8f3120 --- /dev/null +++ b/src/lib/corelib/buildgraph/forward_decls.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BG_FORWARD_DECLS_H +#define QBS_BG_FORWARD_DECLS_H + +#include + +namespace qbs { +namespace Internal { + +class Artifact; +class ArtifactSet; +class ProjectBuildData; +class ProductBuildData; +class Node; +class NodeSet; + +class Transformer; +typedef QSharedPointer TransformerPtr; +typedef QSharedPointer TransformerConstPtr; + +class RulesEvaluationContext; +typedef QSharedPointer RulesEvaluationContextPtr; + +class AbstractCommand; +typedef QSharedPointer AbstractCommandPtr; + +class ProcessCommand; +typedef QSharedPointer ProcessCommandPtr; + +class JavaScriptCommand; +typedef QSharedPointer JavaScriptCommandPtr; + +} // 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 00000000..e24d603f --- /dev/null +++ b/src/lib/corelib/buildgraph/inputartifactscanner.cpp @@ -0,0 +1,407 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "inputartifactscanner.h" + +#include "artifact.h" +#include "buildgraph.h" +#include "productbuilddata.h" +#include "projectbuilddata.h" +#include "transformer.h" +#include "depscanner.h" +#include "rulesevaluationcontext.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +InputArtifactScannerContext::InputArtifactScannerContext(ScanResultCache *scanResultCache) + : scanResultCache(scanResultCache) +{ +} + +InputArtifactScannerContext::~InputArtifactScannerContext() +{ +} + +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(lookupResult))) + continue; + Artifact * const foundArtifact = dynamic_cast(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(); + + // TODO: We probably need a flag that tells us whether directories are allowed. + const FileInfo fi(absFilePath); + if (fi.exists(absFilePath) && !fi.isDir()) + result->filePath = absFilePath; +} + +static void resolveAbsolutePath(const ScanResultCache::Dependency &dependency, + const ResolvedProduct *product, ResolvedDependency *result) +{ + QString absDirPath = 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(lookupResult))) + continue; + Artifact * const foundArtifact = dynamic_cast(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; + } + + if (FileInfo::exists(dependency.filePath())) + result->filePath = dependency.filePath(); +} + +static void scanWithScannerPlugin(DependencyScanner *scanner, + FileResourceBase *fileToBeScanned, + ScanResultCache::Result *scanResult) +{ + QStringList dependencies = scanner->collectDependencies(fileToBeScanned); + foreach (const QString &s, dependencies) + scanResult->deps += ScanResultCache::Dependency(s); + scanResult->valid = 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; + + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() + << QString::fromLatin1("[DEPSCAN] inputs for %1 [%2] in product '%3'") + .arg(m_artifact->filePath(), + m_artifact->fileTags().toStringList().join(QLatin1String(", ")), + m_artifact->product->name); + } + + 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); + + ArtifactSet::const_iterator it = m_artifact->transformer->inputs.begin(); + for (; it != m_artifact->transformer->inputs.end(); ++it) { + Artifact *inputArtifact = *it; + scanForFileDependencies(inputArtifact); + } +} + +void InputArtifactScanner::scanForFileDependencies(Artifact *inputArtifact) +{ + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() + << QString::fromLatin1("[DEPSCAN] input artifact %1 [%2]") + .arg(inputArtifact->filePath(), + inputArtifact->fileTags().toStringList().join(QLatin1String(", "))); + } + + InputArtifactScannerContext::CacheItem &cacheItem = m_context->cache[inputArtifact->properties]; + QSet visitedFilePaths; + QList filesToScan; + filesToScan.append(inputArtifact); + const QSet scanners = scannersForArtifact(inputArtifact); + while (!filesToScan.isEmpty()) { + FileResourceBase *fileToBeScanned = filesToScan.takeFirst(); + const QString &filePathToBeScanned = fileToBeScanned->filePath(); + if (visitedFilePaths.contains(filePathToBeScanned)) + continue; + visitedFilePaths.insert(filePathToBeScanned); + + foreach (DependencyScanner *scanner, scanners) { + scanForScannerFileDependencies(scanner, inputArtifact, fileToBeScanned, + scanner->recursive() ? &filesToScan : 0, cacheItem[scanner->key()]); + } + } +} + +QSet InputArtifactScanner::scannersForArtifact(const Artifact *artifact) const +{ + QSet scanners; + ResolvedProduct *product = artifact->product.data(); + QHash &scannerCache + = m_context->scannersCache[product]; + foreach (const FileTag &fileTag, artifact->fileTags()) { + InputArtifactScannerContext::DependencyScannerCacheItem &cache = scannerCache[fileTag]; + if (!cache.valid) { + cache.valid = true; + foreach (ScannerPlugin *scanner, ScannerPluginManager::scannersForFileTag(fileTag)) { + PluginDependencyScanner *pluginScanner = new PluginDependencyScanner(scanner); + cache.scanners += DependencyScannerPtr(pluginScanner); + } + foreach (const ResolvedScannerConstPtr &scanner, product->scanners) { + if (scanner->inputs.contains(fileTag)) { + cache.scanners += DependencyScannerPtr( + new UserDependencyScanner(scanner, m_logger)); + break; + } + } + } + foreach (const DependencyScannerPtr &scanner, cache.scanners) { + scanners += scanner.data(); + } + } + return scanners; +} + +void InputArtifactScanner::scanForScannerFileDependencies(DependencyScanner *scanner, + Artifact *inputArtifact, FileResourceBase *fileToBeScanned, + QList *filesToScan, + InputArtifactScannerContext::ScannerResolvedDependenciesCache &cache) +{ + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << QString::fromLatin1("[DEPSCAN] file %1") + .arg(fileToBeScanned->filePath()); + } + + const bool cacheHit = cache.valid; + if (!cacheHit) { + cache.valid = true; + cache.searchPaths = scanner->collectSearchPaths(inputArtifact); + } + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() + << "[DEPSCAN] include paths (cache " << (cacheHit ? "hit)" : "miss)"); + foreach (const QString &s, cache.searchPaths) + m_logger.qbsTrace() << " " << s; + } + + const QString &filePathToBeScanned = fileToBeScanned->filePath(); + ScanResultCache::Result scanResult = m_context->scanResultCache->value(scanner->key(), filePathToBeScanned); + if (!scanResult.valid) { + try { + scanWithScannerPlugin(scanner, fileToBeScanned, &scanResult); + } catch (const ErrorInfo &error) { + m_logger.printWarning(error); + return; + } + m_context->scanResultCache->insert(scanner->key(), filePathToBeScanned, scanResult); + } + + resolveScanResultDependencies(inputArtifact, scanResult, filesToScan, cache); +} + +void InputArtifactScanner::resolveScanResultDependencies(const Artifact *inputArtifact, + const ScanResultCache::Result &scanResult, QList *artifactsToScan, + InputArtifactScannerContext::ScannerResolvedDependenciesCache &cache) +{ + foreach (const ScanResultCache::Dependency &dependency, scanResult.deps) { + const QString &dependencyFilePath = dependency.filePath(); + InputArtifactScannerContext::ResolvedDependencyCacheItem &cachedResolvedDependencyItem + = cache.resolvedDependenciesCache[dependency.dirPath()][dependency.fileName()]; + ResolvedDependency &resolvedDependency = cachedResolvedDependencyItem.resolvedDependency; + if (cachedResolvedDependencyItem.valid) { + if (resolvedDependency.filePath.isEmpty()) + goto unresolved; + goto resolved; + } + cachedResolvedDependencyItem.valid = true; + + if (FileInfo::isAbsolute(dependencyFilePath)) { + resolveAbsolutePath(dependency, inputArtifact->product.data(), + &resolvedDependency); + goto resolved; + } + + // try include paths + foreach (const QString &includePath, cache.searchPaths) { + resolveWithIncludePath(includePath, dependency, inputArtifact->product.data(), + &resolvedDependency); + if (resolvedDependency.isValid()) + goto resolved; + } + +unresolved: + if (m_logger.traceEnabled()) + m_logger.qbsWarning() << "[DEPSCAN] unresolved dependency " << dependencyFilePath; + continue; + +resolved: + handleDependency(resolvedDependency); + if (artifactsToScan && resolvedDependency.file) { + if (Artifact *artifactDependency = dynamic_cast(resolvedDependency.file)) { + // Do not scan artifacts that are being built. Otherwise we might read an incomplete + // file or conflict with the writing process. + if (artifactDependency->buildState != BuildGraphNode::Building) + artifactsToScan->append(artifactDependency); + } else { + // Add file dependency to the next round of scanning. + artifactsToScan->append(resolvedDependency.file); + } + } + } +} + +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(dependency.file); + FileDependency *fileDependency + = artifactDependency ? 0 : dynamic_cast(dependency.file); + QBS_CHECK(!dependency.file || artifactDependency || fileDependency); + + if (!dependency.file) { + // The dependency is an existing file but does not exist in the build graph. + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[DEPSCAN] add new file dependency " << 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() << "[DEPSCAN] add existing file dependency " + << dependency.filePath; + } + } else if (artifactDependency->product == product) { + // The dependency is in our product. + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << "[DEPSCAN] add artifact dependency " << dependency.filePath + << " (from this product)"; + } + insertIntoProduct = false; + } else { + // The dependency is in some other product. + ResolvedProduct * const otherProduct = artifactDependency->product; + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << "[DEPSCAN] add artifact dependency " << dependency.filePath + << " (from product " << otherProduct->uniqueName() << ')'; + } + 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->nodes.contains(artifactDependency)) + insertArtifact(product, artifactDependency, m_logger); + safeConnect(m_artifact, artifactDependency, m_logger); + m_artifact->childrenAddedByScanner += artifactDependency; + m_newDependencyAdded = true; + } +} + +InputArtifactScannerContext::DependencyScannerCacheItem::DependencyScannerCacheItem() : valid(false) +{ +} + +InputArtifactScannerContext::DependencyScannerCacheItem::~DependencyScannerCacheItem() +{ +} + +} // 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 00000000..62f976d5 --- /dev/null +++ b/src/lib/corelib/buildgraph/inputartifactscanner.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_INPUTARTIFACTSCANNER_H +#define QBS_INPUTARTIFACTSCANNER_H + +#include "scanresultcache.h" +#include +#include + +#include +#include + +class ScannerPlugin; + +namespace qbs { +namespace Internal { + +class Artifact; +class FileResourceBase; +class PropertyMapInternal; + +class DependencyScanner; +typedef QSharedPointer DependencyScannerPtr; + +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 > ResolvedDependenciesCache; + + struct ScannerResolvedDependenciesCache + { + ScannerResolvedDependenciesCache() : + valid(false) + {} + + bool valid; + QStringList searchPaths; + ResolvedDependenciesCache resolvedDependenciesCache; + }; + + struct DependencyScannerCacheItem + { + DependencyScannerCacheItem(); + ~DependencyScannerCacheItem(); + + bool valid; + QList scanners; + }; + + typedef QHash CacheItem; + + QHash cache; + QHash > scannersCache; + + 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(Artifact *inputArtifact); + QSet scannersForArtifact(const Artifact *artifact) const; + void scanForScannerFileDependencies(DependencyScanner *scanner, + Artifact *inputArtifact, FileResourceBase *fileToBeScanned, + QList *filesToScan, + InputArtifactScannerContext::ScannerResolvedDependenciesCache &cache); + void resolveScanResultDependencies(const Artifact *inputArtifact, + const ScanResultCache::Result &scanResult, QList *artifactsToScan, + InputArtifactScannerContext::ScannerResolvedDependenciesCache &cache); + 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 00000000..3fdebc43 --- /dev/null +++ b/src/lib/corelib/buildgraph/jscommandexecutor.cpp @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "jscommandexecutor.h" + +#include "artifact.h" +#include "buildgraph.h" +#include "command.h" +#include "transformer.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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; + } + + void cancel() + { + QBS_ASSERT(m_scriptEngine, return); + m_scriptEngine->abortEvaluation(); + } + +signals: + void finished(); + +public: + void start(const JavaScriptCommand *cmd, Transformer *transformer) + { + try { + doStart(cmd, transformer); + } catch (const qbs::ErrorInfo &error) { + setError(error.toString(), cmd->codeLocation()); + } + + emit finished(); + } + +private: + void doStart(const JavaScriptCommand *cmd, Transformer *transformer) + { + m_result.success = true; + m_result.errorMessage.clear(); + ScriptEngine * const scriptEngine = provideScriptEngine(); + QScriptValue scope = scriptEngine->newObject(); + scope.setPrototype(scriptEngine->globalObject()); + PrepareScriptObserver observer(scriptEngine); + setupScriptEngineForFile(scriptEngine, transformer->rule->prepareScript->fileContext, scope); + setupScriptEngineForProduct(scriptEngine, transformer->product(), transformer->rule->module, scope, + &observer); + transformer->setupInputs(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())); + } + + scriptEngine->setGlobalObject(scope); + scriptEngine->evaluate(cmd->sourceCode()); + scriptEngine->setGlobalObject(scope.prototype()); + transformer->propertiesRequestedInCommands + += scriptEngine->propertiesRequestedInScript(); + transformer->propertiesRequestedFromArtifactInCommands + = scriptEngine->propertiesRequestedFromArtifact(); + scriptEngine->clearRequestedProperties(); + if (scriptEngine->hasUncaughtException()) { + // ### We don't know the line number of the command's sourceCode property assignment. + setError(scriptEngine->uncaughtException().toString(), cmd->codeLocation()); + } + } + + void setError(const QString &errorMessage, const CodeLocation &codeLocation) + { + m_result.success = false; + m_result.errorMessage = errorMessage; + m_result.errorLocation = codeLocation; + } + + ScriptEngine *provideScriptEngine() + { + if (!m_scriptEngine) + m_scriptEngine = new ScriptEngine(m_logger, EvalContext::JsCommand, 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, &JsCommandExecutorThreadObject::finished, + this, &JsCommandExecutor::onJavaScriptCommandFinished); + connect(this, &JsCommandExecutor::startRequested, + m_objectInThread, &JsCommandExecutorThreadObject::start); +} + +JsCommandExecutor::~JsCommandExecutor() +{ + waitForFinished(); + delete m_objectInThread; + m_thread->quit(); + m_thread->wait(); +} + +void JsCommandExecutor::doReportCommandDescription() +{ + if ((m_echoMode == CommandEchoModeCommandLine + || m_echoMode == CommandEchoModeCommandLineWithEnvironment) + && !command()->extendedDescription().isEmpty()) { + emit reportCommandDescription(command()->highlight(), command()->extendedDescription()); + return; + } + + AbstractCommandExecutor::doReportCommandDescription(); +} + +void JsCommandExecutor::waitForFinished() +{ + if (!m_running) + return; + QEventLoop loop; + connect(m_objectInThread, &JsCommandExecutorThreadObject::finished, &loop, &QEventLoop::quit); + loop.exec(); +} + +void JsCommandExecutor::doStart() +{ + QBS_ASSERT(!m_running, return); + m_thread->start(); + + if (dryRun() && !command()->ignoreDryRun()) { + QTimer::singleShot(0, this, [this] { emit finished(); }); // Don't call back on the caller. + return; + } + + m_running = true; + emit startRequested(jsCommand(), transformer()); +} + +void JsCommandExecutor::cancel() +{ + if (!dryRun()) + QTimer::singleShot(0, m_objectInThread, [this] { m_objectInThread->cancel(); }); +} + +void JsCommandExecutor::onJavaScriptCommandFinished() +{ + m_running = false; + const JavaScriptCommandResult &result = m_objectInThread->result(); + ErrorInfo err; + if (!result.success) { + logger().qbsDebug() << "JS context:\n" << jsCommand()->properties(); + logger().qbsDebug() << "JS code:\n" << jsCommand()->sourceCode(); + err.append(result.errorMessage); + // ### We don't know the line number of the command's sourceCode property assignment. + err.appendBacktrace(QStringLiteral("JavaScriptCommand.sourceCode")); + err.appendBacktrace(QStringLiteral("Rule.prepare"), result.errorLocation); + } + emit finished(err); +} + +const JavaScriptCommand *JsCommandExecutor::jsCommand() const +{ + return static_cast(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 00000000..2229f760 --- /dev/null +++ b/src/lib/corelib/buildgraph/jscommandexecutor.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_JSCOMMANDEXECUTOR_H +#define QBS_JSCOMMANDEXECUTOR_H + +#include "abstractcommandexecutor.h" + +#include + +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: + void onJavaScriptCommandFinished(); + + void doReportCommandDescription(); + void doStart(); + void cancel(); + + 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/nodeset.cpp b/src/lib/corelib/buildgraph/nodeset.cpp new file mode 100644 index 00000000..3ed799fa --- /dev/null +++ b/src/lib/corelib/buildgraph/nodeset.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "nodeset.h" + +#include "artifact.h" +#include "rulenode.h" +#include // because of RulePtr +#include +#include + +namespace qbs { +namespace Internal { + +NodeSet::NodeSet() + : d(new NodeSetData) +{ +} + +NodeSet &NodeSet::unite(const NodeSet &other) +{ + d->m_data.insert(other.begin(), other.end()); + return *this; +} + +void NodeSet::remove(BuildGraphNode *node) +{ + d->m_data.erase(node); +} + +void NodeSet::load(PersistentPool &pool) +{ + clear(); + int i; + pool.stream() >> i; + for (; --i >= 0;) { + int t; + pool.stream() >> t; + BuildGraphNode *node = 0; + switch (static_cast(t)) { + case BuildGraphNode::ArtifactNodeType: + node = pool.idLoad(); + break; + case BuildGraphNode::RuleNodeType: + node = pool.idLoad(); + break; + } + QBS_CHECK(node); + insert(node); + } +} + +void NodeSet::store(PersistentPool &pool) const +{ + pool.stream() << count(); + for (NodeSet::const_iterator it = constBegin(); it != constEnd(); ++it) { + pool.stream() << int((*it)->type()); + pool.store(*it); + } +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/buildgraph/nodeset.h b/src/lib/corelib/buildgraph/nodeset.h new file mode 100644 index 00000000..573338fe --- /dev/null +++ b/src/lib/corelib/buildgraph/nodeset.h @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_NODESET_H +#define QBS_NODESET_H + +#include +#include + +#include + +namespace qbs { +namespace Internal { + +class BuildGraphNode; + +class NodeSetData : public QSharedData +{ +public: + std::set m_data; +}; + +class PersistentPool; + +/** + * Set of build graph nodes. + * This is faster than QSet when iterating over the container. + */ +class NodeSet +{ +public: + NodeSet(); + + NodeSet &unite(const NodeSet &other); + + typedef std::set::const_iterator const_iterator; + typedef std::set::iterator iterator; + typedef BuildGraphNode * value_type; + + iterator begin() { return d->m_data.begin(); } + iterator end() { return d->m_data.end(); } + const_iterator begin() const { return d->m_data.begin(); } + const_iterator end() const { return d->m_data.end(); } + const_iterator constBegin() const { return d->m_data.begin(); } + const_iterator constEnd() const { return d->m_data.end(); } + + void insert(BuildGraphNode *node) + { + d->m_data.insert(node); + } + + void operator+=(BuildGraphNode *node) + { + d->m_data.insert(node); + } + + NodeSet &operator<<(BuildGraphNode *node) + { + d->m_data.insert(node); + return *this; + } + + void remove(BuildGraphNode *node); + + bool contains(BuildGraphNode *node) const + { + return d->m_data.find(node) != d->m_data.end(); + } + + void clear() + { + d->m_data.clear(); + } + + bool isEmpty() const + { + return d->m_data.empty(); + } + + int count() const + { + return (int)d->m_data.size(); + } + + void reserve(int) + { + // no-op + } + + bool operator==(const NodeSet &other) const { return d->m_data == other.d->m_data; } + bool operator!=(const NodeSet &other) const { return !(*this == other); } + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + + +private: + QSharedDataPointer d; +}; + +template +class TypeFilter +{ + const NodeSet &m_nodes; +public: + TypeFilter(const NodeSet &nodes) + : m_nodes(nodes) + { + } + + class const_iterator : public std::iterator + { + const NodeSet &m_nodes; + NodeSet::const_iterator m_it; + public: + const_iterator(const NodeSet &nodes, const NodeSet::const_iterator &it) + : m_nodes(nodes), m_it(it) + { + while (m_it != m_nodes.constEnd() && dynamic_cast(*m_it) == 0) + ++m_it; + } + + bool operator==(const const_iterator &rhs) + { + return m_it == rhs.m_it; + } + + bool operator!=(const const_iterator &rhs) + { + return !(*this == rhs); + } + + const_iterator &operator++() + { + for (;;) { + ++m_it; + if (m_it == m_nodes.constEnd() || dynamic_cast(*m_it)) + return *this; + } + } + + T *operator*() const + { + return static_cast(*m_it); + } + }; + + const_iterator begin() const + { + return const_iterator(m_nodes, m_nodes.constBegin()); + } + + const_iterator end() const + { + return const_iterator(m_nodes, m_nodes.constEnd()); + } +}; + +template +const TypeFilter filterByType(const NodeSet &nodes) +{ + return TypeFilter(nodes); +} + +} // namespace Internal +} // namespace qbs + +#endif // QBS_NODESET_H diff --git a/src/lib/corelib/buildgraph/nodetreedumper.cpp b/src/lib/corelib/buildgraph/nodetreedumper.cpp new file mode 100644 index 00000000..f77e79c5 --- /dev/null +++ b/src/lib/corelib/buildgraph/nodetreedumper.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "nodetreedumper.h" + +#include "artifact.h" +#include "productbuilddata.h" +#include "rulenode.h" + +#include +#include + +#include + +namespace qbs { +namespace Internal { + +static unsigned int indentWidth() { return 4; } + +NodeTreeDumper::NodeTreeDumper(QIODevice &outDevice) : m_outDevice(outDevice) +{ +} + +void NodeTreeDumper::start(const QList &products) +{ + m_indentation = 0; + foreach (const ResolvedProductPtr &p, products) { + if (!p->buildData) + continue; + m_currentProduct = p; + foreach (Artifact * const root, p->buildData->rootArtifacts()) + root->accept(this); + m_visited.clear(); + QBS_CHECK(m_indentation == 0); + } +} + +bool NodeTreeDumper::visit(Artifact *artifact) +{ + m_outDevice.write(indentation()); + m_outDevice.write(artifact->fileName().toLocal8Bit()); + indent(); + const bool wasVisited = m_visited.contains(artifact); + m_visited.insert(artifact); + return !wasVisited && artifact->product == m_currentProduct; +} + +void NodeTreeDumper::endVisit(Artifact *artifact) +{ + Q_UNUSED(artifact); + doEndVisit(); +} + +bool NodeTreeDumper::visit(RuleNode *rule) +{ + m_outDevice.write(indentation()); + m_outDevice.write(rule->toString().toLocal8Bit()); + indent(); + return true; +} + +void NodeTreeDumper::endVisit(RuleNode *rule) +{ + Q_UNUSED(rule); + doEndVisit(); +} + +void NodeTreeDumper::doEndVisit() +{ + unindent(); +} + +void NodeTreeDumper::indent() +{ + m_outDevice.write("\n"); + m_indentation += indentWidth(); +} + +void NodeTreeDumper::unindent() +{ + m_indentation -= indentWidth(); +} + +QByteArray NodeTreeDumper::indentation() const +{ + return QByteArray(m_indentation, ' '); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/buildgraph/nodetreedumper.h b/src/lib/corelib/buildgraph/nodetreedumper.h new file mode 100644 index 00000000..86d14e72 --- /dev/null +++ b/src/lib/corelib/buildgraph/nodetreedumper.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_NODETREEDUMPER_H +#define QBS_NODETREEDUMPER_H + +#include "artifactset.h" +#include "buildgraphvisitor.h" +#include + +#include + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { + +class NodeTreeDumper : public BuildGraphVisitor +{ +public: + NodeTreeDumper(QIODevice &outDevice); + + void start(const QList &products); + +private: + bool visit(Artifact *artifact) Q_DECL_OVERRIDE; + void endVisit(Artifact *artifact) Q_DECL_OVERRIDE; + bool visit(RuleNode *rule) Q_DECL_OVERRIDE; + void endVisit(RuleNode *rule) Q_DECL_OVERRIDE; + + void doEndVisit(); + void indent(); + void unindent(); + QByteArray indentation() const; + + QIODevice &m_outDevice; + ResolvedProductPtr m_currentProduct; + ArtifactSet m_visited; + unsigned int m_indentation; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.cpp b/src/lib/corelib/buildgraph/processcommandexecutor.cpp new file mode 100644 index 00000000..49c2cf2f --- /dev/null +++ b/src/lib/corelib/buildgraph/processcommandexecutor.cpp @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "processcommandexecutor.h" + +#include "artifact.h" +#include "command.h" +#include "transformer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +ProcessCommandExecutor::ProcessCommandExecutor(const Logger &logger, QObject *parent) + : AbstractCommandExecutor(logger, parent) +{ + connect(&m_process, static_cast(&QProcess::error), + this, &ProcessCommandExecutor::onProcessError); + connect(&m_process, static_cast(&QProcess::finished), + this, &ProcessCommandExecutor::onProcessFinished); +} + +void ProcessCommandExecutor::doSetup() +{ + const ProcessCommand * const cmd = processCommand(); + const QString program = ExecutableFinder(transformer()->product(), + transformer()->product()->buildEnvironment, logger()) + .findExecutable(cmd->program(), cmd->workingDir()); + + QProcessEnvironment env = m_buildEnvironment; + const QProcessEnvironment &additionalVariables = cmd->environment(); + foreach (const QString &key, additionalVariables.keys()) + env.insert(key, additionalVariables.value(key)); + m_commandEnvironment = env; + + m_program = program; + m_arguments = cmd->arguments(); + m_shellInvocation = shellQuote(QDir::toNativeSeparators(m_program), m_arguments); +} + +void ProcessCommandExecutor::doStart() +{ + QBS_ASSERT(m_process.state() == QProcess::NotRunning, return); + + const ProcessCommand * const cmd = processCommand(); + + m_process.setProcessEnvironment(m_commandEnvironment); + + QStringList arguments = m_arguments; + + if (dryRun() && !cmd->ignoreDryRun()) { + QTimer::singleShot(0, this, [this] { emit finished(); }); // Don't call back on the caller. + return; + } + + const QString workingDir = QDir::fromNativeSeparators(cmd->workingDir()); + if (!workingDir.isEmpty()) { + FileInfo fi(workingDir); + if (!fi.exists() || !fi.isDir()) { + emit finished(ErrorInfo(Tr::tr("The working directory '%1' for process '%2' " + "is invalid.").arg(QDir::toNativeSeparators(workingDir), + QDir::toNativeSeparators(m_program)), + cmd->codeLocation())); + return; + } + } + + // Automatically use response files, if the command line gets to long. + if (!cmd->responseFileUsagePrefix().isEmpty()) { + const int commandLineLength = m_shellInvocation.length(); + if (cmd->responseFileThreshold() >= 0 && commandLineLength > cmd->responseFileThreshold()) { + if (logger().debugEnabled()) { + logger().qbsDebug() << QString::fromLatin1("[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() + QLatin1String("/qbsresp")); + if (!responseFile.open()) { + emit finished(ErrorInfo(Tr::tr("Cannot create response file '%1'.") + .arg(responseFile.fileName()))); + return; + } + for (int i = cmd->responseFileArgumentIndex(); i < cmd->arguments().count(); ++i) { + responseFile.write(qbs::Internal::shellQuote(cmd->arguments().at(i)).toLocal8Bit()); + responseFile.write("\n"); + } + responseFile.close(); + m_responseFileName = responseFile.fileName(); + arguments = arguments.mid(0, + std::min(cmd->responseFileArgumentIndex(), arguments.size())); + arguments += QDir::toNativeSeparators(cmd->responseFileUsagePrefix() + + responseFile.fileName()); + } + } + + logger().qbsDebug() << "[EXEC] Running external process; full command line is: " + << m_shellInvocation; + const QProcessEnvironment &additionalVariables = cmd->environment(); + logger().qbsTrace() << "[EXEC] Additional environment:" << additionalVariables.toStringList(); + m_process.setWorkingDirectory(workingDir); + m_process.start(m_program, arguments); +} + +void ProcessCommandExecutor::cancel() +{ + // We don't want this command to be reported as failing, since we explicitly terminated it. + disconnect(this, &ProcessCommandExecutor::reportProcessResult, 0, 0); + + m_process.terminate(); + if (!m_process.waitForFinished(1000)) + m_process.kill(); +} + +QString ProcessCommandExecutor::filterProcessOutput(const QByteArray &_output, + const QString &filterFunctionSource) +{ + const QString output = QString::fromLocal8Bit(_output); + if (filterFunctionSource.isEmpty()) + return output; + + QScriptValue scope = scriptEngine()->newObject(); + scope.setPrototype(scriptEngine()->globalObject()); + for (QVariantMap::const_iterator it = command()->properties().constBegin(); + it != command()->properties().constEnd(); ++it) { + scope.setProperty(it.key(), scriptEngine()->toScriptValue(it.value())); + } + + TemporaryGlobalObjectSetter tgos(scope); + QScriptValue filterFunction = scriptEngine()->evaluate(QLatin1String("var f = ") + + filterFunctionSource + + QLatin1String("; f")); + if (!filterFunction.isFunction()) { + logger().printWarning(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)) { + logger().printWarning(ErrorInfo(Tr::tr("Error when calling output filter function: %1") + .arg(scriptEngine()->lastErrorString(filteredOutput)))); + return output; + } + + return filteredOutput.toString(); +} + +static QProcess::ProcessError saveToFile(const QString &filePath, const QByteArray &content) +{ + QBS_ASSERT(!filePath.isEmpty(), return QProcess::WriteError); + + QFile f(filePath); + if (!f.open(QIODevice::WriteOnly)) + return QProcess::WriteError; + + if (f.write(content) != content.size()) + return QProcess::WriteError; + f.close(); + return f.error() == QFileDevice::NoError ? QProcess::UnknownError : QProcess::WriteError; +} + +void ProcessCommandExecutor::getProcessOutput(bool stdOut, ProcessResult &result) +{ + QByteArray content; + QString filterFunction; + QString redirectPath; + QStringList *target; + if (stdOut) { + content = m_process.readAllStandardOutput(); + filterFunction = processCommand()->stdoutFilterFunction(); + redirectPath = processCommand()->stdoutFilePath(); + target = &result.d->stdOut; + } else { + content = m_process.readAllStandardError(); + filterFunction = processCommand()->stderrFilterFunction(); + redirectPath = processCommand()->stderrFilePath(); + target = &result.d->stdErr; + } + QString contentString = filterProcessOutput(content, filterFunction); + if (!redirectPath.isEmpty()) { + const QProcess::ProcessError error = saveToFile(redirectPath, contentString.toLocal8Bit()); + if (result.error() == QProcess::UnknownError && error != QProcess::UnknownError) + result.d->error = error; + } else { + if (!contentString.isEmpty() && contentString.endsWith(QLatin1Char('\n'))) + contentString.chop(1); + *target = contentString.split(QLatin1Char('\n'), QString::SkipEmptyParts); + } +} + +void ProcessCommandExecutor::sendProcessOutput() +{ + 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->error = m_process.error(); + QString errorString = m_process.errorString(); + + getProcessOutput(true, result); + getProcessOutput(false, result); + + const bool processError = result.error() != QProcess::UnknownError; + const bool failureExit = quint32(m_process.exitCode()) + > quint32(processCommand()->maxExitCode()); + result.d->success = !processError && !failureExit; + emit reportProcessResult(result); + + if (Q_UNLIKELY(processError)) { + emit finished(ErrorInfo(errorString)); + } else if (Q_UNLIKELY(failureExit)) { + emit finished(ErrorInfo(Tr::tr("Process failed with exit code %1.") + .arg(m_process.exitCode()))); + } else { + emit finished(); + } +} + +void ProcessCommandExecutor::onProcessError() +{ + switch (m_process.error()) { + case QProcess::FailedToStart: { + removeResponseFile(); + const QString binary = QDir::toNativeSeparators(processCommand()->program()); + QString errorPrefixString; +#ifdef Q_OS_UNIX + if (QFileInfo(binary).isExecutable()) { + const QString interpreter(shellInterpreter(binary)); + if (!interpreter.isEmpty()) { + errorPrefixString = Tr::tr("%1: bad interpreter: ").arg(interpreter); + } + } +#endif + emit finished(ErrorInfo(Tr::tr("The process '%1' could not be started: %2. " + "The full command line invocation was: %3") + .arg(binary, errorPrefixString + m_process.errorString(), + m_shellInvocation))); + break; + } + case QProcess::Crashed: + break; // Ignore. Will be handled by onProcessFinished(). + default: + logger().qbsWarning() << "QProcess error: " << m_process.errorString(); + } +} + +void ProcessCommandExecutor::onProcessFinished() +{ + removeResponseFile(); + sendProcessOutput(); +} + +static QString environmentVariableString(const QString &key, const QString &value) +{ + QString str; + if (HostOsInfo::isAnyUnixHost()) + str += QStringLiteral("export "); + if (HostOsInfo::isWindowsHost()) + str += QStringLiteral("set "); + return str + shellQuote(key + QLatin1Char('=') + value) + QLatin1Char('\n'); +} + +void ProcessCommandExecutor::doReportCommandDescription() +{ + if (m_echoMode == CommandEchoModeCommandLine || + m_echoMode == CommandEchoModeCommandLineWithEnvironment) { + QString fullInvocation; + if (m_echoMode == CommandEchoModeCommandLineWithEnvironment) { + QStringList keys = m_commandEnvironment.keys(); + keys.sort(); + for (const QString &key : keys) + fullInvocation += environmentVariableString(key, m_commandEnvironment.value(key)); + } + fullInvocation += m_shellInvocation; + emit reportCommandDescription(command()->highlight(), + !command()->extendedDescription().isEmpty() + ? command()->extendedDescription() + : fullInvocation); + return; + } + + AbstractCommandExecutor::doReportCommandDescription(); +} + +void ProcessCommandExecutor::removeResponseFile() +{ + if (m_responseFileName.isEmpty()) + return; + QFile::remove(m_responseFileName); + m_responseFileName.clear(); +} + +const ProcessCommand *ProcessCommandExecutor::processCommand() const +{ + return static_cast(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 00000000..89bc03af --- /dev/null +++ b/src/lib/corelib/buildgraph/processcommandexecutor.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PROCESSCOMMANDEXECUTOR_H +#define QBS_PROCESSCOMMANDEXECUTOR_H + +#include "abstractcommandexecutor.h" + +#include +#include +#include + +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: + void onProcessError(); + void onProcessFinished(); + + void doSetup(); + void doReportCommandDescription(); + void doStart(); + void cancel(); + + void startProcessCommand(); + QString filterProcessOutput(const QByteArray &output, const QString &filterFunctionSource); + void getProcessOutput(bool stdOut, ProcessResult &result); + + void sendProcessOutput(); + void removeResponseFile(); + const ProcessCommand *processCommand() const; + +private: + QString m_program; + QStringList m_arguments; + QString m_shellInvocation; + + QProcess m_process; + QProcessEnvironment m_buildEnvironment; + QProcessEnvironment m_commandEnvironment; + 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 00000000..7bd88e57 --- /dev/null +++ b/src/lib/corelib/buildgraph/productbuilddata.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "productbuilddata.h" + +#include "artifact.h" +#include "command.h" +#include "projectbuilddata.h" +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +ProductBuildData::~ProductBuildData() +{ + qDeleteAll(nodes); +} + +const TypeFilter ProductBuildData::rootArtifacts() const +{ + return TypeFilter(roots); +} + +static void loadArtifactSetByFileTag(PersistentPool &pool, + ProductBuildData::ArtifactSetByFileTag &s) +{ + int elemCount; + pool.stream() >> elemCount; + for (int i = 0; i < elemCount; ++i) { + const QVariant fileTag = pool.loadVariant(); + ArtifactSet artifacts; + pool.loadContainer(artifacts); + s.insert(FileTag::fromSetting(fileTag), artifacts); + } +} + +void ProductBuildData::load(PersistentPool &pool) +{ + nodes.load(pool); + roots.load(pool); + int count; + pool.stream() >> count; + rescuableArtifactData.reserve(count); + for (int i = 0; i < count; ++i) { + const QString filePath = pool.idLoadString(); + RescuableArtifactData elem; + elem.load(pool); + rescuableArtifactData.insert(filePath, elem); + } + loadArtifactSetByFileTag(pool, artifactsByFileTag); + + pool.stream() >> count; + for (int i = 0; i < count; ++i) { + const RulePtr r = pool.idLoadS(); + ArtifactSet s; + pool.loadContainer(s); + artifactsWithChangedInputsPerRule.insert(r, s); + } +} + +static void storeArtifactSetByFileTag(PersistentPool &pool, + const ProductBuildData::ArtifactSetByFileTag &s) +{ + pool.stream() << s.count(); + ProductBuildData::ArtifactSetByFileTag::ConstIterator it; + for (it = s.constBegin(); it != s.constEnd(); ++it) { + pool.store(it.key().toSetting()); + pool.storeContainer(it.value()); + } +} + +void ProductBuildData::store(PersistentPool &pool) const +{ + nodes.store(pool); + roots.store(pool); + pool.stream() << rescuableArtifactData.count(); + for (AllRescuableArtifactData::ConstIterator it = rescuableArtifactData.constBegin(); + it != rescuableArtifactData.constEnd(); ++it) { + pool.storeString(it.key()); + it.value().store(pool); + } + storeArtifactSetByFileTag(pool, artifactsByFileTag); + + pool.stream() << artifactsWithChangedInputsPerRule.count(); + for (ArtifactSetByRule::ConstIterator it = artifactsWithChangedInputsPerRule.constBegin(); + it != artifactsWithChangedInputsPerRule.constEnd(); ++it) { + pool.store(it.key()); + pool.storeContainer(it.value()); + } +} + +void addArtifactToSet(Artifact *artifact, ProductBuildData::ArtifactSetByFileTag &container) +{ + foreach (const FileTag &tag, artifact->fileTags()) + container[tag] += artifact; +} + +void removeArtifactFromSetByFileTag(Artifact *artifact, const FileTag &fileTag, + ProductBuildData::ArtifactSetByFileTag &container) +{ + ProductBuildData::ArtifactSetByFileTag::iterator it = container.find(fileTag); + if (it == container.end()) + return; + it->remove(artifact); + if (it->isEmpty()) + container.erase(it); +} + +void removeArtifactFromSet(Artifact *artifact, ProductBuildData::ArtifactSetByFileTag &container) +{ + foreach (const FileTag &t, artifact->fileTags()) + removeArtifactFromSetByFileTag(artifact, t, container); +} + +} // 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 00000000..80f89207 --- /dev/null +++ b/src/lib/corelib/buildgraph/productbuilddata.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PRODUCTBUILDDATA_H +#define QBS_PRODUCTBUILDDATA_H + +#include "artifactset.h" +#include "nodeset.h" +#include "rescuableartifactdata.h" +#include +#include +#include + +#include +#include + +namespace qbs { +namespace Internal { + +class Logger; + +class ProductBuildData : public PersistentObject +{ +public: + ~ProductBuildData(); + + const TypeFilter rootArtifacts() const; + NodeSet nodes; + NodeSet roots; + + // After change tracking, this is the relevant data of artifacts that were in the build data + // of the restored product, and will potentially be re-created by our rules. + // If and when that happens, the relevant data will be copied over to the newly created + // artifact. + AllRescuableArtifactData rescuableArtifactData; + + // Do not store, initialized in executor. Higher prioritized artifacts are built first. + unsigned int buildPriority; + + typedef QHash ArtifactSetByFileTag; + ArtifactSetByFileTag artifactsByFileTag; + + typedef QHash ArtifactSetByRule; + ArtifactSetByRule artifactsWithChangedInputsPerRule; + +private: + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; +}; + +void addArtifactToSet(Artifact *artifact, ProductBuildData::ArtifactSetByFileTag &container); +void removeArtifactFromSetByFileTag(Artifact *artifact, const FileTag &fileTag, + ProductBuildData::ArtifactSetByFileTag &container); +void removeArtifactFromSet(Artifact *artifact, ProductBuildData::ArtifactSetByFileTag &container); + +} // 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 00000000..b59ad1c4 --- /dev/null +++ b/src/lib/corelib/buildgraph/productinstaller.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "productinstaller.h" + +#include "artifact.h" +#include "productbuilddata.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace qbs { +namespace Internal { + +ProductInstaller::ProductInstaller(const TopLevelProjectPtr &project, + const QList &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() +{ + m_targetFilePathsMap.clear(); + + if (m_options.removeExistingInstallation()) + removeInstallRoot(); + + QList artifactsToInstall; + foreach (const ResolvedProductConstPtr &product, m_products) { + QBS_CHECK(product->buildData); + for (const Artifact *artifact : filterByType(product->buildData->nodes)) { + 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 &productSourceDir, + const QString &sourceFilePath, const PropertyMapConstPtr &properties, + InstallOptions &options) +{ + if (!properties->qbsPropertyValue(QLatin1String("install")).toBool()) + return QString(); + const QString relativeInstallDir + = properties->qbsPropertyValue(QLatin1String("installDir")).toString(); + const QString installPrefix + = properties->qbsPropertyValue(QLatin1String("installPrefix")).toString(); + const QString installSourceBase + = properties->qbsPropertyValue(QLatin1String("installSourceBase")).toString(); + initInstallRoot(project, options); + QString targetDir = options.installRoot(); + targetDir.append(QLatin1Char('/')).append(installPrefix) + .append(QLatin1Char('/')).append(relativeInstallDir); + targetDir = QDir::cleanPath(targetDir); + + QString targetFilePath; + if (installSourceBase.isEmpty()) { + // This has the same effect as if installSourceBase would equal the directory of the file. + targetFilePath = FileInfo::fileName(sourceFilePath); + } else { + const QString localAbsBasePath = FileInfo::resolvePath(QDir::cleanPath(productSourceDir), + QDir::cleanPath(installSourceBase)); + targetFilePath = sourceFilePath; + if (!targetFilePath.startsWith(localAbsBasePath)) { + throw ErrorInfo(Tr::tr("Cannot install '%1', because it doesn't start with the" + " value of qbs.installSourceBase '%2'.").arg(sourceFilePath, + localAbsBasePath)); + } + + targetFilePath.remove(0, localAbsBasePath.length() + 1); + } + + targetFilePath.prepend(targetDir + QLatin1Char('/')); + return targetFilePath; +} + +void ProductInstaller::initInstallRoot(const TopLevelProject *project, + InstallOptions &options) +{ + if (!options.installRoot().isEmpty()) + return; + + options.setInstallRoot(effectiveInstallRoot(options, project)); +} + +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::fromLatin1("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())); + } + + const QString targetFilePath = this->targetFilePath(m_project.data(), + artifact->product->sourceDirectory, artifact->filePath(), + artifact->properties, m_options); + const QString targetDir = FileInfo::path(targetFilePath); + const QString nativeFilePath = QDir::toNativeSeparators(artifact->filePath()); + const QString nativeTargetDir = QDir::toNativeSeparators(targetDir); + if (m_options.dryRun()) { + m_logger.qbsDebug() << Tr::tr("Would copy file '%1' into target directory '%2'.") + .arg(nativeFilePath, nativeTargetDir); + return; + } + m_logger.qbsDebug() << QString::fromLatin1("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; + } + QFileInfo fi(artifact->filePath()); + if (fi.isDir() && !(HostOsInfo::isAnyUnixHost() && fi.isSymLink())) { + m_logger.qbsWarning() << Tr::tr("Not recursively copying directory '%1' into target " + "directory '%2'. Install the individual file artifacts " + "instead.") + .arg(nativeFilePath, nativeTargetDir); + } + + if (m_targetFilePathsMap.contains(targetFilePath)) { + handleError(Tr::tr("Cannot install files '%1' and '%2' to the same location '%3'. If you " + "are attempting to install a directory hierarchy, consider using " + "the qbs.installSourceBase property.") + .arg(artifact->filePath(), m_targetFilePathsMap[targetFilePath], + targetFilePath)); + } + m_targetFilePathsMap.insert(targetFilePath, artifact->filePath()); + + QString errorMessage; + if (!copyFileRecursion(artifact->filePath(), targetFilePath, true, false, &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 00000000..12ae69a0 --- /dev/null +++ b/src/lib/corelib/buildgraph/productinstaller.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PRODUCT_INSTALLER_H +#define QBS_PRODUCT_INSTALLER_H + +#include "forward_decls.h" + +#include +#include +#include + +#include +#include + +namespace qbs { +namespace Internal { +class ProgressObserver; + +class ProductInstaller +{ +public: + ProductInstaller(const TopLevelProjectPtr &project, const QList &products, + const InstallOptions &options, ProgressObserver *observer, const Logger &logger); + void install(); + + static QString targetFilePath(const TopLevelProject *project, const QString &productSourceDir, + const QString &sourceFilePath, const PropertyMapConstPtr &properties, + InstallOptions &options); + static void initInstallRoot(const TopLevelProject *project, InstallOptions &options); + + void removeInstallRoot(); + void copyFile(const Artifact *artifact); + +private: + void handleError(const QString &message); + + const TopLevelProjectConstPtr m_project; + const QList m_products; + InstallOptions m_options; + ProgressObserver * const m_observer; + Logger m_logger; + QHash m_targetFilePathsMap; +}; + +} // 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 00000000..0ffb43fe --- /dev/null +++ b/src/lib/corelib/buildgraph/projectbuilddata.cpp @@ -0,0 +1,550 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "projectbuilddata.h" + +#include "artifact.h" +#include "buildgraph.h" +#include "buildgraphvisitor.h" +#include "productbuilddata.h" +#include "command.h" +#include "rulegraph.h" +#include "rulenode.h" +#include "rulesevaluationcontext.h" +#include "transformer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +static QSet findDependentProducts(const ResolvedProductPtr &product) +{ + QSet result; + foreach (const ResolvedProductPtr &parent, product->topLevelProject()->allProducts()) { + if (parent->dependencies.contains(product)) + result += parent; + } + return result; +} + +class FindLeafRules : public BuildGraphVisitor +{ +public: + FindLeafRules() + { + } + + const QSet &apply(const ResolvedProductPtr &product) + { + m_result.clear(); + m_product = product; + QBS_CHECK(product->buildData); + foreach (BuildGraphNode *n, product->buildData->nodes) + n->accept(this); + return m_result; + } + +private: + virtual bool visit(Artifact *) + { + return false; + } + + virtual bool visit(RuleNode *node) + { + if (!hasChildRuleInThisProduct(node)) + m_result << node; + return false; + } + + bool hasChildRuleInThisProduct(const RuleNode *node) const + { + foreach (BuildGraphNode *c, node->children) { + if (c->product == m_product && c->type() == BuildGraphNode::RuleNodeType) + return true; + } + return false; + } + + ResolvedProductPtr m_product; + QSet m_result; +}; + +class FindRootRules : public BuildGraphVisitor +{ +public: + FindRootRules() + { + } + + const QList &apply(const ResolvedProductPtr &product) + { + m_result.clear(); + foreach (BuildGraphNode *n, product->buildData->roots) + n->accept(this); + return m_result; + } + +private: + bool visit(Artifact *) + { + return false; + } + + bool visit(RuleNode *node) + { + m_result << node; + return false; + } + + QList m_result; +}; + +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"); +} + +static QString productNameForErrorMessage(const ResolvedProduct *product) +{ + return product->profile == product->topLevelProject()->profile() + ? product->name : product->uniqueName(); +} + +void ProjectBuildData::insertIntoLookupTable(FileResourceBase *fileres) +{ + QList &lst + = m_artifactLookupTable[fileres->fileName()][fileres->dirPath()]; + const auto * const artifact = dynamic_cast(fileres); + if (artifact && artifact->artifactType == Artifact::Generated) { + foreach (const auto *file, lst) { + const auto * const otherArtifact = dynamic_cast(file); + if (otherArtifact) { + ErrorInfo error; + error.append(Tr::tr("Conflicting artifacts for file path '%1'.") + .arg(artifact->filePath())); + error.append(Tr::tr("The first artifact comes from product '%1'.") + .arg(productNameForErrorMessage(otherArtifact->product.data())), + otherArtifact->product->location); + error.append(Tr::tr("The second artifact comes from product '%1'.") + .arg(productNameForErrorMessage(artifact->product.data())), + otherArtifact->product->location); + throw error; + } + } + } + QBS_CHECK(!lst.contains(fileres)); + lst.append(fileres); +} + +void ProjectBuildData::removeFromLookupTable(FileResourceBase *fileres) +{ + m_artifactLookupTable[fileres->fileName()][fileres->dirPath()].removeOne(fileres); +} + +QList ProjectBuildData::lookupFiles(const QString &filePath) const +{ + QString dirPath, fileName; + FileInfo::splitIntoDirectoryAndFileName(filePath, &dirPath, &fileName); + return lookupFiles(dirPath, fileName); +} + +QList ProjectBuildData::lookupFiles(const QString &dirPath, + const QString &fileName) const +{ + return m_artifactLookupTable.value(fileName).value(dirPath); +} + +QList 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::fromLatin1("[BG] disconnectChildren: '%1'") + .arg(relativeArtifactFileName(artifact)); + } + foreach (BuildGraphNode * const child, artifact->children) + child->parents.remove(artifact); + artifact->children.clear(); + artifact->childrenAddedByScanner.clear(); +} + +static void disconnectArtifactParents(Artifact *artifact, const Logger &logger) +{ + if (logger.traceEnabled()) { + logger.qbsTrace() << QString::fromLatin1("[BG] disconnectParents: '%1'") + .arg(relativeArtifactFileName(artifact)); + } + foreach (BuildGraphNode * const parent, artifact->parents) { + parent->children.remove(artifact); + Artifact *parentArtifact = dynamic_cast(parent); + if (parentArtifact) { + QBS_CHECK(parentArtifact->transformer); + parentArtifact->childrenAddedByScanner.remove(artifact); + parentArtifact->transformer->inputs.remove(artifact); + parentArtifact->product->registerArtifactWithChangedInputs(parentArtifact); + } + } + + artifact->parents.clear(); +} + +static void disconnectArtifact(Artifact *artifact, const Logger &logger) +{ + disconnectArtifactChildren(artifact, logger); + disconnectArtifactParents(artifact, 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, + ArtifactSet *removedArtifacts) +{ + if (removedArtifacts) + removedArtifacts->insert(artifact); + + // Iterate over a copy of the artifact's parents, because we'll change + // artifact->parents with the disconnect call. + const NodeSet parentsCopy = artifact->parents; + for (Artifact *parent : filterByType(parentsCopy)) { + bool removeParent = false; + disconnect(parent, artifact, logger); + if (parent->children.isEmpty()) { + removeParent = true; + } else if (parent->transformer) { + parent->product->registerArtifactWithChangedInputs(parent); + parent->transformer->inputs.remove(artifact); + removeParent = parent->transformer->inputs.isEmpty(); + } + if (removeParent) { + removeArtifactAndExclusiveDependents(parent, logger, removeFromProduct, + removedArtifacts); + } else { + parent->clearTimestamp(); + } + } + const bool removeFromDisk = artifact->artifactType == Artifact::Generated; + removeArtifact(artifact, logger, removeFromDisk, removeFromProduct); +} + +static void removeFromRuleNodes(Artifact *artifact, const Logger &logger) +{ + foreach (const ResolvedProductPtr &product, + artifact->product->topLevelProject()->allProducts()) { + if (!product->buildData) + continue; + foreach (BuildGraphNode *n, product->buildData->nodes) { + if (n->type() != BuildGraphNode::RuleNodeType) + continue; + RuleNode * const ruleNode = static_cast(n); + if (logger.traceEnabled()) { + logger.qbsTrace() << "[BG] remove old input " << artifact->filePath() + << " from rule " << ruleNode->rule()->toString(); + } + ruleNode->removeOldInputArtifact(artifact); + } + } +} + +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); + removeFromRuleNodes(artifact, logger); + disconnectArtifact(artifact, logger); + if (artifact->transformer) { + artifact->product->unregisterArtifactWithChangedInputs(artifact); + artifact->transformer->outputs.remove(artifact); + } + if (removeFromProduct) { + artifact->product->buildData->nodes.remove(artifact); + artifact->product->buildData->roots.remove(artifact); + removeArtifactFromSet(artifact, artifact->product->buildData->artifactsByFileTag); + } + isDirty = true; +} + +void ProjectBuildData::load(PersistentPool &pool) +{ + int count; + pool.stream() >> count; + fileDependencies.clear(); + fileDependencies.reserve(count); + for (; --count >= 0;) { + FileDependency *fileDependency = pool.idLoad(); + 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 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 &freshProducts) +{ + m_project = project; + foreach (const ResolvedProductPtr &product, freshProducts) { + if (product->enabled) + resolveProductBuildData(product); + } + + // Connect the leaf rules of all dependent products to the root rules of the dependency. + foreach (const ResolvedProductPtr &product, freshProducts) { + if (!product->enabled) + continue; + QBS_CHECK(product->buildData); + const QList rootRules = FindRootRules().apply(product); + QSet dependents = findDependentProducts(product); + foreach (const ResolvedProductPtr &dependentProduct, dependents) { + if (!dependentProduct->enabled) + continue; + foreach (RuleNode *leaf, FindLeafRules().apply(dependentProduct)) { + foreach (RuleNode *root, rootRules) { + loggedConnect(leaf, root, m_logger); + } + } + } + } +} + +class CreateRuleNodes : public RuleGraphVisitor +{ +public: + CreateRuleNodes(const ResolvedProductPtr &product, const Logger &logger) + : m_product(product), m_logger(logger) + { + } + + const QSet &leaves() const + { + return m_leaves; + } + +private: + const ResolvedProductPtr &m_product; + const Logger &m_logger; + QHash m_nodePerRule; + QSet m_rulesOnPath; + QList m_rulePath; + QSet m_leaves; + + void visit(const RuleConstPtr &parentRule, const RuleConstPtr &rule) + { + if (m_rulesOnPath.contains(rule.data())) { + QString pathstr; + foreach (const Rule *r, m_rulePath) { + pathstr += QLatin1Char('\n') + r->toString() + QLatin1Char('\t') + + r->prepareScript->location.toString(); + } + throw ErrorInfo(Tr::tr("Cycle detected in rule dependencies: %1").arg(pathstr)); + } + m_rulesOnPath.insert(rule.data()); + m_rulePath.append(rule.data()); + RuleNode *node = m_nodePerRule.value(rule); + if (!node) { + node = new RuleNode; + m_leaves.insert(node); + m_nodePerRule.insert(rule, node); + node->product = m_product; + node->setRule(rule); + m_product->buildData->nodes += node; + if (m_logger.debugEnabled()) { + m_logger.qbsDebug() << "[BG] create " << node->toString() + << " for product " << m_product->uniqueName(); + } + } + if (parentRule) { + RuleNode *parent = m_nodePerRule.value(parentRule); + QBS_CHECK(parent); + loggedConnect(parent, node, m_logger); + m_leaves.remove(parent); + } else { + m_product->buildData->roots += node; + } + } + + void endVisit(const RuleConstPtr &rule) + { + m_rulesOnPath.remove(rule.data()); + m_rulePath.removeLast(); + } +}; + +void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &product) +{ + if (product->buildData) + return; + + evalContext()->checkForCancelation(); + + product->buildData.reset(new ProductBuildData); + ProductBuildData::ArtifactSetByFileTag artifactsPerFileTag; + + foreach (ResolvedProductPtr dependency, product->dependencies) { + QBS_CHECK(dependency->enabled); + resolveProductBuildData(dependency); + } + + //add qbsFile artifact + Artifact *qbsFileArtifact = lookupArtifact(product, product->location.filePath()); + if (!qbsFileArtifact) { + qbsFileArtifact = new Artifact; + qbsFileArtifact->artifactType = Artifact::SourceFile; + qbsFileArtifact->setFilePath(product->location.filePath()); + qbsFileArtifact->properties = product->moduleProperties; + insertArtifact(product, qbsFileArtifact, m_logger); + } + qbsFileArtifact->addFileTag("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); + } + + RuleGraph ruleGraph; + ruleGraph.build(product->rules, product->fileTags); + CreateRuleNodes crn(product, m_logger); + ruleGraph.accept(&crn); + + // Connect the leaf rules of this product to the root rules of all product dependencies. + foreach (const ResolvedProductConstPtr &dep, product->dependencies) { + if (!dep->buildData) + continue; + foreach (BuildGraphNode *depRoot, dep->buildData->roots) { + RuleNode *depRootRule = dynamic_cast(depRoot); + if (!depRootRule) + continue; + foreach (RuleNode *leafRule, crn.leaves()) + loggedConnect(leafRule, depRootRule, 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 00000000..09126385 --- /dev/null +++ b/src/lib/corelib/buildgraph/projectbuilddata.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROJECTBUILDDATA_H +#define QBS_PROJECTBUILDDATA_H + +#include "forward_decls.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { +class ArtifactSet; +class BuildGraphNode; +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 lookupFiles(const QString &filePath) const; + QList lookupFiles(const QString &dirPath, const QString &fileName) const; + QList lookupFiles(const Artifact *artifact) const; + void insertFileDependency(FileDependency *dependency); + void removeArtifactAndExclusiveDependents(Artifact *artifact, const Logger &logger, + bool removeFromProduct = true, ArtifactSet *removedArtifacts = 0); + void removeArtifact(Artifact *artifact, const Logger &logger, bool removeFromDisk = true, + bool removeFromProduct = true); + + + QSet fileDependencies; + + // do not serialize: + RulesEvaluationContextPtr evaluationContext; + bool isDirty; + +private: + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + + typedef QHash > ResultsPerDirectory; + typedef QHash 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 &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/qtmocscanner.cpp b/src/lib/corelib/buildgraph/qtmocscanner.cpp new file mode 100644 index 00000000..be133630 --- /dev/null +++ b/src/lib/corelib/buildgraph/qtmocscanner.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtmocscanner.h" + +#include "artifact.h" +#include "productbuilddata.h" +#include "scanresultcache.h" +#include +#include +#include +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue, + const Logger &logger) + : m_product(product) + , m_targetScriptValue(targetScriptValue) + , m_logger(logger) + , m_scanResultCache(new ScanResultCache) + , m_cppScanner(0) + , m_hppScanner(0) +{ + QScriptEngine *engine = targetScriptValue.engine(); + QScriptValue scannerObj = engine->newObject(); + targetScriptValue.setProperty(QLatin1String("QtMocScanner"), scannerObj); + QScriptValue applyFunction = engine->newFunction(&js_apply, this); + scannerObj.setProperty(QLatin1String("apply"), applyFunction); +} + +QtMocScanner::~QtMocScanner() +{ + m_targetScriptValue.setProperty(QLatin1String("QtMocScanner"), QScriptValue()); + delete m_scanResultCache; +} + +ScannerPlugin *QtMocScanner::scannerPluginForFileTags(const FileTags &ft) +{ + if (ft.contains("objcpp")) + return m_objcppScanner; + if (ft.contains("cpp")) + return m_cppScanner; + return m_hppScanner; +} + +static ScanResultCache::Result runScanner(ScannerPlugin *scanner, const Artifact *artifact, + ScanResultCache *scanResultCache) +{ + const QString &filepath = artifact->filePath(); + ScanResultCache::Result scanResult = scanResultCache->value(scanner, filepath); + if (!scanResult.valid) { + scanResult.valid = true; + void *opaq = scanner->open(filepath.utf16(), + ScanForDependenciesFlag | ScanForFileTagsFlag); + if (!opaq || !scanner->additionalFileTags) + return scanResult; + + int length = 0; + const char **szFileTagsFromScanner = scanner->additionalFileTags(opaq, &length); + if (szFileTagsFromScanner) { + for (int i = length; --i >= 0;) + scanResult.additionalFileTags += szFileTagsFromScanner[i]; + } + + QString baseDirOfInFilePath = artifact->dirPath(); + 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); + if (isLocalInclude) { + QString localFilePath = FileInfo::resolvePath(baseDirOfInFilePath, includedFilePath); + if (FileInfo::exists(localFilePath)) + includedFilePath = localFilePath; + } + scanResult.deps += ScanResultCache::Dependency(includedFilePath); + } + + scanner->close(opaq); + scanResultCache->insert(scanner, filepath, scanResult); + } + return scanResult; +} + +void QtMocScanner::findIncludedMocCppFiles() +{ + if (!m_includedMocCppFiles.isEmpty()) + return; + + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[QtMocScanner] looking for included moc_XXX.cpp files"; + + static const FileTags mocCppTags = FileTags::fromStringList(QStringList() + << QStringLiteral("cpp") + << QStringLiteral("objcpp")); + foreach (Artifact *artifact, m_product->lookupArtifactsByFileTags(mocCppTags)) { + const ScanResultCache::Result scanResult + = runScanner(scannerPluginForFileTags(artifact->fileTags()), + artifact, m_scanResultCache); + foreach (const ScanResultCache::Dependency &dependency, scanResult.deps) { + QString includedFileName = dependency.fileName(); + if (includedFileName.startsWith(QLatin1String("moc_")) + && includedFileName.endsWith(QLatin1String(".cpp"))) { + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[QtMocScanner] " << artifact->fileName() + << " includes " << includedFileName; + includedFileName.remove(0, 4); + includedFileName.chop(4); + m_includedMocCppFiles.insert(includedFileName, artifact->fileName()); + } + } + } +} + +QScriptValue QtMocScanner::js_apply(QScriptContext *ctx, QScriptEngine *engine, void *data) +{ + QtMocScanner *that = reinterpret_cast(data); + QScriptValue input = ctx->argument(0); + return that->apply(engine, attachedPointer(input)); +} + +static QScriptValue scannerCountError(QScriptEngine *engine, int scannerCount, + const QString &fileTag) +{ + return engine->currentContext()->throwError( + Tr::tr("There are %1 scanners for the file tag %2. " + "Expected is exactly one.").arg(scannerCount).arg(fileTag)); +} + +QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact) +{ + if (!m_cppScanner) { + QList scanners = ScannerPluginManager::scannersForFileTag("cpp"); + if (scanners.count() != 1) + return scannerCountError(engine, scanners.count(), QLatin1String("cpp")); + m_cppScanner = scanners.first(); + scanners = ScannerPluginManager::scannersForFileTag("objcpp"); + if (scanners.count() != 1) + return scannerCountError(engine, scanners.count(), QLatin1String("objcpp")); + m_objcppScanner = scanners.first(); + scanners = ScannerPluginManager::scannersForFileTag("hpp"); + if (scanners.count() != 1) + return scannerCountError(engine, scanners.count(), QLatin1String("hpp")); + m_hppScanner = scanners.first(); + } + + findIncludedMocCppFiles(); + + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[QtMocScanner] scanning " << artifact->toString(); + + bool hasQObjectMacro = false; + bool mustCompile = false; + bool hasPluginMetaDataMacro = false; + const bool isHeaderFile = artifact->fileTags().contains("hpp"); + + ScannerPlugin * const scanner = scannerPluginForFileTags(artifact->fileTags()); + + const ScanResultCache::Result scanResult = runScanner(scanner, artifact, m_scanResultCache); + if (!scanResult.additionalFileTags.isEmpty()) { + if (isHeaderFile) { + if (scanResult.additionalFileTags.contains("moc_hpp")) + hasQObjectMacro = true; + if (scanResult.additionalFileTags.contains("moc_hpp_plugin")) { + hasQObjectMacro = true; + hasPluginMetaDataMacro = true; + } + if (!m_includedMocCppFiles.contains(FileInfo::completeBaseName(artifact->fileName()))) + mustCompile = true; + } else { + if (scanResult.additionalFileTags.contains("moc_cpp")) + hasQObjectMacro = true; + } + } + + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << "[QtMocScanner] hasQObjectMacro: " << hasQObjectMacro + << " mustCompile: " << mustCompile + << " hasPluginMetaDataMacro: " << hasPluginMetaDataMacro; + } + + QScriptValue obj = engine->newObject(); + obj.setProperty(QLatin1String("hasQObjectMacro"), hasQObjectMacro); + obj.setProperty(QLatin1String("mustCompile"), mustCompile); + obj.setProperty(QLatin1String("hasPluginMetaDataMacro"), hasPluginMetaDataMacro); + return obj; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/buildgraph/qtmocscanner.h b/src/lib/corelib/buildgraph/qtmocscanner.h new file mode 100644 index 00000000..cc457944 --- /dev/null +++ b/src/lib/corelib/buildgraph/qtmocscanner.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_QTMOCSCANNER_H +#define QBS_QTMOCSCANNER_H + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QScriptContext; +QT_END_NAMESPACE + +class ScannerPlugin; + +namespace qbs { +namespace Internal { + +class Artifact; +class ScanResultCache; + +class QtMocScanner +{ +public: + explicit QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue, + const Logger &logger); + ~QtMocScanner(); + +private: + ScannerPlugin *scannerPluginForFileTags(const FileTags &ft); + void findIncludedMocCppFiles(); + static QScriptValue js_apply(QScriptContext *ctx, QScriptEngine *engine, void *data); + QScriptValue apply(QScriptEngine *engine, const Artifact *artifact); + + const ResolvedProductPtr &m_product; + QScriptValue m_targetScriptValue; + const Logger &m_logger; + ScanResultCache *m_scanResultCache; + QHash m_includedMocCppFiles; + ScannerPlugin *m_cppScanner; + ScannerPlugin *m_objcppScanner; + ScannerPlugin *m_hppScanner; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_QTMOCSCANNER_H diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.cpp b/src/lib/corelib/buildgraph/rescuableartifactdata.cpp new file mode 100644 index 00000000..d73c20b2 --- /dev/null +++ b/src/lib/corelib/buildgraph/rescuableartifactdata.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "rescuableartifactdata.h" + +#include "command.h" + +#include + +namespace qbs { +namespace Internal { + +RescuableArtifactData::~RescuableArtifactData() +{ +} + +void RescuableArtifactData::load(PersistentPool &pool) +{ + pool.stream() >> timeStamp; + + int c; + pool.stream() >> c; + for (int i = 0; i < c; ++i) { + ChildData cd; + cd.productName = pool.idLoadString(); + cd.productProfile = pool.idLoadString(); + cd.childFilePath = pool.idLoadString(); + pool.stream() >> cd.addedByScanner; + children << cd; + } + + propertiesRequestedInPrepareScript = restorePropertySet(pool); + propertiesRequestedInCommands = restorePropertySet(pool); + propertiesRequestedFromArtifactInPrepareScript = restorePropertyHash(pool); + propertiesRequestedFromArtifactInCommands = restorePropertyHash(pool); + commands = loadCommandList(pool); + fileTags.load(pool); + properties = pool.loadVariantMap(); +} + +void RescuableArtifactData::store(PersistentPool &pool) const +{ + pool.stream() << timeStamp; + + pool.stream() << children.count(); + foreach (const ChildData &cd, children) { + pool.storeString(cd.productName); + pool.storeString(cd.productProfile); + pool.storeString(cd.childFilePath); + pool.stream() << cd.addedByScanner; + } + + storePropertySet(pool, propertiesRequestedInPrepareScript); + storePropertySet(pool, propertiesRequestedInCommands); + storePropertyHash(pool, propertiesRequestedFromArtifactInPrepareScript); + storePropertyHash(pool, propertiesRequestedFromArtifactInCommands); + storeCommandList(commands, pool); + fileTags.store(pool); + pool.store(properties); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.h b/src/lib/corelib/buildgraph/rescuableartifactdata.h new file mode 100644 index 00000000..0f6d8ae4 --- /dev/null +++ b/src/lib/corelib/buildgraph/rescuableartifactdata.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_RESCUABLEARTIFACTDATA_H +#define QBS_RESCUABLEARTIFACTDATA_H + +#include "forward_decls.h" + +#include +#include +#include + +#include +#include + +namespace qbs { +namespace Internal { +class PersistentPool; + +class RescuableArtifactData +{ +public: + ~RescuableArtifactData(); + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + + struct ChildData + { + ChildData(const QString &n = QString(), const QString &p = QString(), + const QString &c = QString(), bool byScanner = false) + : productName(n), productProfile(p), childFilePath(c), addedByScanner(byScanner) + {} + QString productName; + QString productProfile; + QString childFilePath; + bool addedByScanner; + }; + + FileTime timeStamp; + QList children; + + // Per-Transformer data + QList commands; + PropertySet propertiesRequestedInPrepareScript; + PropertySet propertiesRequestedInCommands; + PropertyHash propertiesRequestedFromArtifactInPrepareScript; + PropertyHash propertiesRequestedFromArtifactInCommands; + + // Only needed for API purposes + FileTags fileTags; + QVariantMap properties; + +}; +typedef QHash AllRescuableArtifactData; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/buildgraph/rulegraph.cpp b/src/lib/corelib/buildgraph/rulegraph.cpp new file mode 100644 index 00000000..7e13b8ef --- /dev/null +++ b/src/lib/corelib/buildgraph/rulegraph.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "rulegraph.h" +#include +#include +#include + +namespace qbs { +namespace Internal { + +RuleGraph::RuleGraph() +{ +} + +void RuleGraph::build(const QSet &rules, const FileTags &productFileTags) +{ + QMap > inputFileTagToRule; + m_rules.reserve(rules.count()); + foreach (const RulePtr &rule, rules) { + foreach (const FileTag &fileTag, rule->collectedOutputFileTags()) + m_outputFileTagToRule[fileTag].append(rule.data()); + insert(rule); + } + + m_parents.resize(rules.count()); + m_children.resize(rules.count()); + + foreach (const RuleConstPtr &rule, m_rules) { + FileTags inFileTags = rule->inputs; + inFileTags += rule->auxiliaryInputs; + inFileTags += rule->explicitlyDependsOn; + foreach (const FileTag &fileTag, inFileTags) { + inputFileTagToRule[fileTag].append(rule.data()); + foreach (const Rule * const producingRule, m_outputFileTagToRule.value(fileTag)) { + if (!producingRule->collectedOutputFileTags().matches( + rule->excludedAuxiliaryInputs)) { + connect(rule.data(), producingRule); + } + } + } + } + + QList productRules; + foreach (const FileTag &productFileTag, productFileTags) { + QList 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; +} + +void RuleGraph::accept(RuleGraphVisitor *visitor) const +{ + const RuleConstPtr nullParent; + foreach (int rootIndex, m_rootRules) + traverse(visitor, nullParent, m_rules.at(rootIndex)); +} + +void RuleGraph::dump() const +{ + QByteArray indent; + printf("---rule graph dump:\n"); + QSet rootRules; + foreach (const RuleConstPtr &rule, m_rules) + 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_rules[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_rules.count(); + m_rules.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); +} + +void RuleGraph::traverse(RuleGraphVisitor *visitor, const RuleConstPtr &parentRule, + const RuleConstPtr &rule) const +{ + visitor->visit(parentRule, rule); + foreach (int childIndex, m_children.at(rule->ruleGraphId)) + traverse(visitor, rule, m_rules.at(childIndex)); + visitor->endVisit(rule); +} + +} // 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 00000000..b29abeb2 --- /dev/null +++ b/src/lib/corelib/buildgraph/rulegraph.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_RULEGRAPH_H +#define QBS_RULEGRAPH_H + +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class RuleGraphVisitor +{ +public: + virtual void visit(const RuleConstPtr &parentRule, const RuleConstPtr &rule) = 0; + virtual void endVisit(const RuleConstPtr &rule) { Q_UNUSED(rule); } +}; + +class RuleGraph +{ +public: + RuleGraph(); + + void build(const QSet &rules, const FileTags &productFileTag); + void accept(RuleGraphVisitor *visitor) const; + + 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); + void traverse(RuleGraphVisitor *visitor, const RuleConstPtr &parentRule, + const RuleConstPtr &rule) const; + +private: + QMap > m_outputFileTagToRule; + QVector m_rules; + QVector< QVector > m_parents; + QVector< QVector > m_children; + QSet m_rootRules; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_RULEGRAPH_H diff --git a/src/lib/corelib/buildgraph/rulenode.cpp b/src/lib/corelib/buildgraph/rulenode.cpp new file mode 100644 index 00000000..ebe573e2 --- /dev/null +++ b/src/lib/corelib/buildgraph/rulenode.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "rulenode.h" + +#include "artifact.h" +#include "buildgraph.h" +#include "buildgraphvisitor.h" +#include "productbuilddata.h" +#include "projectbuilddata.h" +#include "rulesapplicator.h" +#include "transformer.h" + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +RuleNode::RuleNode() +{ +} + +RuleNode::~RuleNode() +{ +} + +void RuleNode::accept(BuildGraphVisitor *visitor) +{ + if (visitor->visit(this)) + acceptChildren(visitor); + visitor->endVisit(this); +} + +QString RuleNode::toString() const +{ + return QLatin1String("RULE ") + m_rule->toString() + QLatin1String(" [") + + (!product.isNull() ? product->name : QLatin1String("")) + QLatin1Char(']'); +} + +void RuleNode::apply(const Logger &logger, const ArtifactSet &changedInputs, + ApplicationResult *result) +{ + ArtifactSet allCompatibleInputs = currentInputArtifacts(); + const ArtifactSet addedInputs = allCompatibleInputs - m_oldInputArtifacts; + const ArtifactSet removedInputs = m_oldInputArtifacts - allCompatibleInputs; + result->upToDate = changedInputs.isEmpty() && addedInputs.isEmpty() && removedInputs.isEmpty() + && m_rule->requiresInputs(); + + if (logger.traceEnabled()) { + logger.qbsTrace() + << "[BG] consider " << (m_rule->isDynamic() ? "dynamic " : "") + << (m_rule->multiplex ? "multiplex " : "") + << "rule node " << m_rule->toString() + << "\n\tchanged: " << changedInputs.toString() + << "\n\tcompatible: " << allCompatibleInputs.toString() + << "\n\tadded: " << addedInputs.toString() + << "\n\tremoved: " << removedInputs.toString(); + } + + ArtifactSet inputs = changedInputs; + if (product->isMarkedForReapplication(m_rule)) { + QBS_CHECK(m_rule->multiplex); + result->upToDate = false; + product->unmarkForReapplication(m_rule); + if (logger.traceEnabled()) + logger.qbsTrace() << "[BG] rule is marked for reapplication " << m_rule->toString(); + } + + if (m_rule->multiplex) + inputs = allCompatibleInputs; + else + inputs += addedInputs; + + if (result->upToDate) + return; + if (!removedInputs.isEmpty()) { + ArtifactSet outputArtifactsToRemove; + foreach (Artifact *artifact, removedInputs) { + for (Artifact *parent : filterByType(artifact->parents)) { + if (parent->transformer->rule != m_rule) { + // parent was not created by our rule. + continue; + } + + // parent must always have a transformer, because it's generated. + QBS_CHECK(parent->transformer); + + // artifact is a former input of m_rule and parent was created by m_rule + // the inputs of the transformer must contain artifact + QBS_CHECK(parent->transformer->inputs.contains(artifact)); + + outputArtifactsToRemove += parent; + } + } + RulesApplicator::handleRemovedRuleOutputs(inputs, outputArtifactsToRemove, logger); + } + if (!inputs.isEmpty() || !m_rule->requiresInputs()) { + RulesApplicator applicator(product, logger); + applicator.applyRule(m_rule, inputs); + result->createdNodes = applicator.createdArtifacts(); + result->invalidatedNodes = applicator.invalidatedArtifacts(); + m_oldInputArtifacts.unite(inputs); + } +} + +void RuleNode::load(PersistentPool &pool) +{ + BuildGraphNode::load(pool); + m_rule = pool.idLoadS(); + pool.loadContainer(m_oldInputArtifacts); +} + +void RuleNode::store(PersistentPool &pool) const +{ + BuildGraphNode::store(pool); + pool.store(m_rule); + pool.storeContainer(m_oldInputArtifacts); +} + +ArtifactSet RuleNode::currentInputArtifacts() const +{ + ArtifactSet s; + foreach (const FileTag &t, m_rule->inputs) { + foreach (Artifact *artifact, product->lookupArtifactsByFileTag(t)) { + if (artifact->transformer && artifact->transformer->rule == m_rule) { + // Do not add compatible artifacts as inputs that were created by this rule. + // This can e.g. happen for the ["cpp", "hpp"] -> ["hpp", "cpp", "unmocable"] rule. + continue; + } + s += artifact; + } + } + + foreach (const ResolvedProductConstPtr &dep, product->dependencies) { + if (!dep->buildData) + continue; + if (m_rule->inputsFromDependencies.isEmpty()) + continue; + for (Artifact * const a : filterByType(dep->buildData->nodes)) { + if (a->fileTags().matches(m_rule->inputsFromDependencies)) + s += a; + } + } + + return s; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/buildgraph/rulenode.h b/src/lib/corelib/buildgraph/rulenode.h new file mode 100644 index 00000000..83f3eeeb --- /dev/null +++ b/src/lib/corelib/buildgraph/rulenode.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_RULENODE_H +#define QBS_RULENODE_H + +#include "artifactset.h" +#include "buildgraphnode.h" +#include "forward_decls.h" +#include + +namespace qbs { +namespace Internal { + +class Logger; + +class RuleNode : public BuildGraphNode +{ +public: + RuleNode(); + ~RuleNode(); + + void setRule(const RuleConstPtr &rule) { m_rule = rule; } + const RuleConstPtr &rule() const { return m_rule; } + + Type type() const { return RuleNodeType; } + void accept(BuildGraphVisitor *visitor); + QString toString() const; + + struct ApplicationResult + { + bool upToDate; + NodeSet createdNodes; + NodeSet invalidatedNodes; + }; + + void apply(const Logger &logger, const ArtifactSet &changedInputs, ApplicationResult *result); + void removeOldInputArtifact(Artifact *artifact) { m_oldInputArtifacts.remove(artifact); } + +protected: + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + +private: + ArtifactSet currentInputArtifacts() const; + + RuleConstPtr m_rule; + ArtifactSet m_oldInputArtifacts; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_RULENODE_H diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp new file mode 100644 index 00000000..7b062f4c --- /dev/null +++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp @@ -0,0 +1,561 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "rulesapplicator.h" + +#include "artifact.h" +#include "buildgraph.h" +#include "emptydirectoriesremover.h" +#include "productbuilddata.h" +#include "projectbuilddata.h" +#include "qtmocscanner.h" +#include "rulesevaluationcontext.h" +#include "transformer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +RulesApplicator::RulesApplicator(const ResolvedProductPtr &product, const Logger &logger) + : m_product(product) + , m_mocScanner(0) + , m_logger(logger) +{ +} + +RulesApplicator::~RulesApplicator() +{ + delete m_mocScanner; +} + +void RulesApplicator::applyRule(const RuleConstPtr &rule, const ArtifactSet &inputArtifacts) +{ + if (inputArtifacts.isEmpty() && rule->requiresInputs()) + return; + + m_createdArtifacts.clear(); + m_invalidatedArtifacts.clear(); + RulesEvaluationContext::Scope s(evalContext().data()); + + m_rule = rule; + m_completeInputSet = inputArtifacts; + if (rule->name == QLatin1String("QtCoreMocRule")) { + delete m_mocScanner; + m_mocScanner = new QtMocScanner(m_product, scope(), m_logger); + } + QScriptValue prepareScriptContext = engine()->newObject(); + prepareScriptContext.setPrototype(engine()->globalObject()); + PrepareScriptObserver observer(engine()); + setupScriptEngineForFile(engine(), m_rule->prepareScript->fileContext, scope()); + setupScriptEngineForProduct(engine(), m_product, m_rule->module, prepareScriptContext, &observer); + + if (m_rule->multiplex) { // apply the rule once for a set of inputs + doApply(inputArtifacts, prepareScriptContext); + } else { // apply the rule once for each input + foreach (Artifact * const inputArtifact, inputArtifacts) { + ArtifactSet lst; + lst += inputArtifact; + doApply(lst, prepareScriptContext); + } + } +} + +void RulesApplicator::handleRemovedRuleOutputs(const ArtifactSet &inputArtifacts, + ArtifactSet outputArtifactsToRemove, const Logger &logger) +{ + ArtifactSet artifactsToRemove; + const TopLevelProject *project = 0; + foreach (Artifact *removedArtifact, outputArtifactsToRemove) { + if (logger.traceEnabled()) { + logger.qbsTrace() << "[BG] dynamic rule removed output artifact " + << removedArtifact->toString(); + } + if (!project) + project = removedArtifact->product->topLevelProject(); + project->buildData->removeArtifactAndExclusiveDependents(removedArtifact, logger, true, + &artifactsToRemove); + } + // parents of removed artifacts must update their transformers + foreach (Artifact *removedArtifact, artifactsToRemove) { + for (Artifact *parent : removedArtifact->parentArtifacts()) + parent->product->registerArtifactWithChangedInputs(parent); + } + EmptyDirectoriesRemover(project, logger).removeEmptyParentDirectories(artifactsToRemove); + foreach (Artifact *artifact, artifactsToRemove) { + QBS_CHECK(!inputArtifacts.contains(artifact)); + delete artifact; + } +} + +static void copyProperty(const QString &name, const QScriptValue &src, QScriptValue dst) +{ + dst.setProperty(name, src.property(name)); +} + +static QStringList toStringList(const ArtifactSet &artifacts) +{ + QStringList lst; + foreach (const Artifact *artifact, artifacts) { + const QString str = artifact->filePath() + QLatin1String(" [") + + artifact->fileTags().toStringList().join(QLatin1String(", ")) + QLatin1Char(']'); + lst << str; + } + return lst; +} + +void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext) +{ + evalContext()->checkForCancelation(); + + if (m_logger.debugEnabled()) { + m_logger.qbsDebug() << QString::fromLatin1("[BG] apply rule ") << m_rule->toString() + << QString::fromLatin1(" ") + << toStringList(inputArtifacts).join(QLatin1String(",\n ")); + } + + QList > ruleArtifactArtifactMap; + QList outputArtifacts; + + m_transformer = Transformer::create(); + m_transformer->rule = m_rule; + m_transformer->inputs = inputArtifacts; + m_transformer->alwaysRun = m_rule->alwaysRun; + + // create the output artifacts from the set of input artifacts + Transformer::setupInputs(prepareScriptContext, inputArtifacts, m_rule->module->name); + copyProperty(QLatin1String("inputs"), prepareScriptContext, scope()); + copyProperty(QLatin1String("input"), prepareScriptContext, scope()); + copyProperty(QLatin1String("product"), prepareScriptContext, scope()); + copyProperty(QLatin1String("project"), prepareScriptContext, scope()); + if (m_rule->isDynamic()) { + outputArtifacts = runOutputArtifactsScript(inputArtifacts, + ScriptEngine::argumentList(m_rule->outputArtifactsScript->argumentNames, + scope())); + ArtifactSet newOutputs = ArtifactSet::fromNodeList(outputArtifacts); + const ArtifactSet oldOutputs = collectOldOutputArtifacts(inputArtifacts); + handleRemovedRuleOutputs(m_completeInputSet, oldOutputs - newOutputs, m_logger); + } else { + QSet outputFilePaths; + foreach (const RuleArtifactConstPtr &ruleArtifact, m_rule->artifacts) { + Artifact * const outputArtifact + = createOutputArtifactFromRuleArtifact(ruleArtifact, inputArtifacts, + &outputFilePaths); + if (!outputArtifact) + continue; + outputArtifacts << outputArtifact; + ruleArtifactArtifactMap << qMakePair(ruleArtifact.data(), outputArtifact); + } + } + + if (outputArtifacts.isEmpty()) + return; + + foreach (Artifact *outputArtifact, outputArtifacts) { + // connect artifacts that match the file tags in explicitlyDependsOn + foreach (const FileTag &fileTag, m_rule->explicitlyDependsOn) + foreach (Artifact *dependency, m_product->lookupArtifactsByFileTag(fileTag)) + loggedConnect(outputArtifact, dependency, m_logger); + + outputArtifact->product->unregisterArtifactWithChangedInputs(outputArtifact); + } + + if (inputArtifacts != m_transformer->inputs) + m_transformer->setupInputs(prepareScriptContext); + + // change the transformer outputs according to the bindings in Artifact + QScriptValue scriptValue; + if (!ruleArtifactArtifactMap.isEmpty()) + engine()->setGlobalObject(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(QLatin1String("fileName"), + engine()->toScriptValue(outputArtifact->filePath())); + scope().setProperty(QLatin1String("fileTags"), + toScriptValue(engine(), outputArtifact->fileTags().toStringList())); + + QVariantMap artifactModulesCfg = outputArtifact->properties->value() + .value(QLatin1String("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(QLatin1Char('.')), + engine()->lastErrorString(scriptValue)), binding.location); + } + setConfigProperty(artifactModulesCfg, binding.name, scriptValue.toVariant()); + } + QVariantMap outputArtifactConfig = outputArtifact->properties->value(); + outputArtifactConfig.insert(QLatin1String("modules"), artifactModulesCfg); + outputArtifact->properties->setValue(outputArtifactConfig); + } + if (!ruleArtifactArtifactMap.isEmpty()) + engine()->setGlobalObject(prepareScriptContext.prototype()); + + m_transformer->setupOutputs(engine(), prepareScriptContext); + m_transformer->createCommands(m_rule->prepareScript, evalContext(), + ScriptEngine::argumentList(m_rule->prepareScript->argumentNames, prepareScriptContext)); + if (Q_UNLIKELY(m_transformer->commands.isEmpty())) + throw ErrorInfo(Tr::tr("There is a rule without commands: %1.") + .arg(m_rule->toString()), m_rule->prepareScript->location); +} + +ArtifactSet RulesApplicator::collectOldOutputArtifacts(const ArtifactSet &inputArtifacts) const +{ + ArtifactSet result; + foreach (Artifact *a, inputArtifacts) { + for (Artifact *p : a->parentArtifacts()) { + QBS_CHECK(p->transformer); + if (p->transformer->rule == m_rule && p->transformer->inputs.contains(a)) + result += p; + } + } + return result; +} + +Artifact *RulesApplicator::createOutputArtifactFromRuleArtifact( + const RuleArtifactConstPtr &ruleArtifact, const ArtifactSet &inputArtifacts, + QSet *outputFilePaths) +{ + QScriptValue scriptValue = engine()->evaluate(ruleArtifact->filePath, + ruleArtifact->filePathLocation.filePath(), + ruleArtifact->filePathLocation.line()); + if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue))) { + ErrorInfo errorInfo(engine()->lastErrorString(scriptValue), + engine()->uncaughtExceptionBacktraceOrEmpty()); + errorInfo.append(QStringLiteral("Artifact.filePath"), ruleArtifact->filePathLocation); + throw errorInfo; + } + QString outputPath = FileInfo::resolvePath(m_product->buildDirectory(), scriptValue.toString()); + if (Q_UNLIKELY(outputFilePaths->contains(outputPath))) { + throw ErrorInfo(Tr::tr("Rule %1 already created '%2'.") + .arg(m_rule->toString(), outputPath)); + } + outputFilePaths->insert(outputPath); + return createOutputArtifact(outputPath, ruleArtifact->fileTags, ruleArtifact->alwaysUpdated, + inputArtifacts); +} + +Artifact *RulesApplicator::createOutputArtifact(const QString &filePath, const FileTags &fileTags, + bool alwaysUpdated, const ArtifactSet &inputArtifacts) +{ + QString outputPath = filePath; + // don't let the output artifact "escape" its build dir + outputPath.replace(QLatin1String(".."), QLatin1String("dotdot")); + outputPath = resolveOutPath(outputPath); + + Artifact *outputArtifact = lookupArtifact(m_product, outputPath); + if (outputArtifact) { + const Transformer * const transformer = outputArtifact->transformer.data(); + if (transformer && transformer->rule != m_rule) { + QString e = Tr::tr("Conflicting rules for producing %1 %2 \n") + .arg(outputArtifact->filePath(), + QLatin1Char('[') + + outputArtifact->fileTags().toStringList().join(QLatin1String(", ")) + + QLatin1Char(']')); + QString str = QLatin1Char('[') + m_rule->inputs.toStringList().join(QLatin1String(", ")) + + QLatin1String("] -> [") + outputArtifact->fileTags().toStringList() + .join(QLatin1String(", ")) + QLatin1Char(']'); + + e += QString::fromLatin1(" while trying to apply: %1:%2:%3 %4\n") + .arg(m_rule->prepareScript->location.filePath()) + .arg(m_rule->prepareScript->location.line()) + .arg(m_rule->prepareScript->location.column()) + .arg(str); + + e += QString::fromLatin1(" was already defined in: %1:%2:%3 %4\n") + .arg(transformer->rule->prepareScript->location.filePath()) + .arg(transformer->rule->prepareScript->location.line()) + .arg(transformer->rule->prepareScript->location.column()) + .arg(str); + + throw ErrorInfo(e); + } + if (transformer && !m_rule->multiplex && transformer->inputs != inputArtifacts) { + QBS_CHECK(inputArtifacts.count() == 1); + QBS_CHECK(transformer->inputs.count() == 1); + ErrorInfo error(Tr::tr("Conflicting instances of rule '%1':").arg(m_rule->toString()), + m_rule->prepareScript->location); + error.append(Tr::tr("Output artifact '%1' is to be produced from input " + "artifacts '%2' and '%3', but the rule is not a multiplex rule.") + .arg(outputArtifact->filePath(), + (*transformer->inputs.begin())->filePath(), + (*inputArtifacts.begin())->filePath())); + throw error; + } + if (m_rule->requiresInputs()) + outputArtifact->clearTimestamp(); + m_invalidatedArtifacts += outputArtifact; + } else { + QScopedPointer newArtifact(new Artifact); + newArtifact->artifactType = Artifact::Generated; + newArtifact->setFilePath(outputPath); + insertArtifact(m_product, newArtifact.data(), m_logger); + m_createdArtifacts += newArtifact.data(); + outputArtifact = newArtifact.take(); + } + + outputArtifact->setFileTags( + fileTags.isEmpty() ? m_product->fileTagsForFileName(outputArtifact->fileName()) + : fileTags); + outputArtifact->alwaysUpdated = alwaysUpdated; + outputArtifact->properties = m_product->moduleProperties; + + 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; + } + } + + // Let a positive value of qbs.install imply the file tag "installable". + if (outputArtifact->properties->qbsPropertyValue(QLatin1String("install")).toBool()) + outputArtifact->addFileTag("installable"); + + foreach (Artifact *inputArtifact, inputArtifacts) { + QBS_CHECK(outputArtifact != inputArtifact); + loggedConnect(outputArtifact, inputArtifact, m_logger); + } + + outputArtifact->transformer = m_transformer; + m_transformer->outputs.insert(outputArtifact); + QBS_CHECK(m_rule->multiplex || m_transformer->inputs.count() == 1); + + return outputArtifact; +} + +QList RulesApplicator::runOutputArtifactsScript(const ArtifactSet &inputArtifacts, + const QScriptValueList &args) +{ + QList lst; + QScriptValue fun = engine()->evaluate(m_rule->outputArtifactsScript->sourceCode, + m_rule->outputArtifactsScript->location.filePath(), + m_rule->outputArtifactsScript->location.line()); + if (!fun.isFunction()) + throw ErrorInfo(QLatin1String("Function expected."), + m_rule->outputArtifactsScript->location); + QScriptValue res = fun.call(QScriptValue(), args); + if (engine()->hasErrorOrException(res)) { + ErrorInfo errorInfo(engine()->lastErrorString(res), + engine()->uncaughtExceptionBacktraceOrEmpty()); + errorInfo.append(QStringLiteral("Rule.outputArtifacts"), + m_rule->outputArtifactsScript->location); + throw errorInfo; + } + if (!res.isArray()) + throw ErrorInfo(Tr::tr("Rule.outputArtifacts must return an array of objects."), + m_rule->outputArtifactsScript->location); + const quint32 c = res.property(QLatin1String("length")).toUInt32(); + for (quint32 i = 0; i < c; ++i) + lst += createOutputArtifactFromScriptValue(res.property(i), inputArtifacts); + return lst; +} + +class ArtifactBindingsExtractor +{ + typedef QPair NameValuePair; + QList m_propertyValues; + + static QSet getArtifactItemPropertyNames() + { + QSet s; + foreach (const PropertyDeclaration &pd, + BuiltinDeclarations::instance().declarationsForType( + ItemType::Artifact).properties()) { + s.insert(pd.name()); + } + s.insert(QLatin1String("explicitlyDependsOn")); + return s; + } + + void extractPropertyValues(const QScriptValue &obj, QStringList fullName = QStringList()) + { + QScriptValueIterator svit(obj); + while (svit.hasNext()) { + svit.next(); + const QString name = svit.name(); + if (fullName.isEmpty()) { + // Ignore property names that are part of the Artifact item. + static const QSet artifactItemPropertyNames + = getArtifactItemPropertyNames(); + if (artifactItemPropertyNames.contains(name)) + continue; + } + + const QScriptValue value = svit.value(); + fullName.append(name); + if (value.isObject() && !value.isArray() && !value.isError() && !value.isRegExp()) + extractPropertyValues(value, fullName); + else + m_propertyValues.append(NameValuePair(fullName, value.toVariant())); + fullName.removeLast(); + } + } +public: + ArtifactBindingsExtractor() + { + } + + void apply(Artifact *outputArtifact, const QScriptValue &obj) + { + extractPropertyValues(obj); + if (m_propertyValues.isEmpty()) + return; + + outputArtifact->properties = outputArtifact->properties->clone(); + QVariantMap artifactCfg = outputArtifact->properties->value(); + foreach (const NameValuePair &nvp, m_propertyValues) { + const QStringList valuePath = findValuePath(artifactCfg, nvp.first); + if (valuePath.isEmpty()) { + throw ErrorInfo(Tr::tr("Cannot set module property %1 on artifact %2.") + .arg(nvp.first.join(QLatin1Char('.')), + outputArtifact->filePath())); + } + setConfigProperty(artifactCfg, valuePath, nvp.second); + } + outputArtifact->properties->setValue(artifactCfg); + } + + QStringList findValuePath(const QVariantMap &cfg, const QStringList &nameParts) + { + QStringList tmp = nameParts; + const QString propertyName = tmp.takeLast(); + const QString moduleName = tmp.join(QLatin1Char('.')); + const QStringList modulePath = findModulePath(cfg, moduleName); + if (modulePath.isEmpty()) + return modulePath; + return QStringList(modulePath) << propertyName; + } + + QStringList findModulePath(const QVariantMap &cfg, const QString &moduleName) + { + typedef QPair MapAndPath; + QQueue q; + q.enqueue(MapAndPath(cfg.value(QLatin1String("modules")).toMap(), + QStringList(QLatin1String("modules")))); + do { + const MapAndPath current = q.takeFirst(); + const QVariantMap &mod = current.first; + for (QVariantMap::const_iterator it = mod.constBegin(); it != mod.constEnd(); ++it) { + const QVariantMap m = it.value().toMap(); + const QStringList currentPath = QStringList(current.second) << it.key(); + if (it.key() == moduleName) + return currentPath; + q.enqueue(MapAndPath(m.value(QLatin1String("modules")).toMap(), + QStringList(currentPath) << QLatin1String("modules"))); + } + } while (!q.isEmpty()); + return QStringList(); + } +}; + +Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValue &obj, + const ArtifactSet &inputArtifacts) +{ + if (!obj.isObject()) { + throw ErrorInfo(Tr::tr("Elements of the Rule.outputArtifacts array must be " + "of Object type."), m_rule->outputArtifactsScript->location); + } + const QString filePath = FileInfo::resolvePath(m_product->buildDirectory(), + obj.property(QLatin1String("filePath")).toVariant().toString()); + const FileTags fileTags = FileTags::fromStringList( + obj.property(QLatin1String("fileTags")).toVariant().toStringList()); + const QVariant alwaysUpdatedVar = obj.property(QLatin1String("alwaysUpdated")).toVariant(); + const bool alwaysUpdated = alwaysUpdatedVar.isValid() ? alwaysUpdatedVar.toBool() : true; + Artifact *output = createOutputArtifact(filePath, fileTags, alwaysUpdated, inputArtifacts); + const FileTags explicitlyDependsOn = FileTags::fromStringList( + obj.property(QLatin1String("explicitlyDependsOn")).toVariant().toStringList()); + foreach (const FileTag &tag, explicitlyDependsOn) { + foreach (Artifact *dependency, m_product->lookupArtifactsByFileTag(tag)) { + loggedConnect(output, dependency, m_logger); + } + } + ArtifactBindingsExtractor().apply(output, obj); + return output; +} + +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; +} + +const 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 00000000..66550195 --- /dev/null +++ b/src/lib/corelib/buildgraph/rulesapplicator.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_RULESAPPLICATOR_H +#define QBS_RULESAPPLICATOR_H + +#include "artifactset.h" +#include "forward_decls.h" +#include "nodeset.h" +#include +#include +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { +class BuildGraphNode; +class QtMocScanner; +class ScriptEngine; + +class RulesApplicator +{ +public: + RulesApplicator(const ResolvedProductPtr &product, const Logger &logger); + ~RulesApplicator(); + + const NodeSet &createdArtifacts() const { return m_createdArtifacts; } + const NodeSet &invalidatedArtifacts() const { return m_invalidatedArtifacts; } + + void applyRule(const RuleConstPtr &rule, const ArtifactSet &inputArtifacts); + static void handleRemovedRuleOutputs(const ArtifactSet &inputArtifacts, + ArtifactSet artifactsToRemove, const Logger &logger); + +private: + void doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext); + ArtifactSet collectOldOutputArtifacts(const ArtifactSet &inputArtifacts) const; + Artifact *createOutputArtifactFromRuleArtifact(const RuleArtifactConstPtr &ruleArtifact, + const ArtifactSet &inputArtifacts, QSet *outputFilePaths); + Artifact *createOutputArtifact(const QString &filePath, const FileTags &fileTags, + bool alwaysUpdated, const ArtifactSet &inputArtifacts); + QList runOutputArtifactsScript(const ArtifactSet &inputArtifacts, + const QScriptValueList &args); + Artifact *createOutputArtifactFromScriptValue(const QScriptValue &obj, + const ArtifactSet &inputArtifacts); + QString resolveOutPath(const QString &path) const; + const RulesEvaluationContextPtr &evalContext() const; + ScriptEngine *engine() const; + QScriptValue scope() const; + + const ResolvedProductPtr m_product; + NodeSet m_createdArtifacts; + NodeSet m_invalidatedArtifacts; + RuleConstPtr m_rule; + ArtifactSet m_completeInputSet; + TransformerPtr m_transformer; + QtMocScanner *m_mocScanner; + 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 00000000..efbb6b65 --- /dev/null +++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "rulesevaluationcontext.h" + +#include "artifact.h" +#include "command.h" +#include "transformer.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { + +RulesEvaluationContext::RulesEvaluationContext(const Logger &logger) + : m_logger(logger), + m_engine(new ScriptEngine(m_logger, EvalContext::RuleExecution)), + m_observer(0), + m_initScopeCalls(0) +{ + m_prepareScriptScope = m_engine->newObject(); + m_prepareScriptScope.setPrototype(m_engine->globalObject()); + ProcessCommand::setupForJavaScript(m_prepareScriptScope); + JavaScriptCommand::setupForJavaScript(m_prepareScriptScope); +} + +RulesEvaluationContext::~RulesEvaluationContext() +{ + delete m_engine; +} + +bool RulesEvaluationContext::isActive() const +{ + return m_initScopeCalls > 0; +} + +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_scope = m_engine->newObject(); + m_scope.setPrototype(m_prepareScriptScope); + m_engine->setGlobalObject(m_scope); +} + +void RulesEvaluationContext::cleanupScope() +{ + QBS_CHECK(m_initScopeCalls > 0); + if (--m_initScopeCalls > 0) + return; + + m_scope = QScriptValue(); + m_engine->setGlobalObject(m_prepareScriptScope.prototype()); +} + +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 00000000..db475296 --- /dev/null +++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_RULESEVALUATIONCONTEXT_H +#define QBS_RULESEVALUATIONCONTEXT_H + +#include +#include + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { +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; } + bool isActive() const; + + 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(); + + Logger m_logger; + 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 00000000..93986264 --- /dev/null +++ b/src/lib/corelib/buildgraph/scanresultcache.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scanresultcache.h" +#include + +namespace qbs { +namespace Internal { + +ScanResultCache::Dependency::Dependency(const QString &filePath) +{ + FileInfo::splitIntoDirectoryAndFileName(filePath, &m_dirPath, &m_fileName); + + m_isClean = !m_dirPath.contains(QLatin1Char('.')) + && !m_dirPath.contains(QLatin1String("//")); +} + +ScanResultCache::Result ScanResultCache::value(const void *scanner, const QString &fileName) const +{ + return m_data[scanner][fileName]; +} + +void ScanResultCache::insert(const void *scanner, const QString &fileName, const ScanResultCache::Result &value) +{ + m_data[scanner].insert(fileName, value); +} + +void ScanResultCache::remove(const QString &fileName) +{ + ScanResultCacheData::iterator i = m_data.begin(); + while (i != m_data.end()) { + i.value().remove(fileName); + ++i; + } +} + +} // 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 00000000..a5c670c7 --- /dev/null +++ b/src/lib/corelib/buildgraph/scanresultcache.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SCANRESULTCACHE_H +#define QBS_SCANRESULTCACHE_H + +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +class ScanResultCache +{ +public: + class Dependency + { + public: + Dependency() : m_isClean(true) {} + Dependency(const QString &filePath); + + 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 isClean() const { return m_isClean; } + + private: + QString m_dirPath; + QString m_fileName; + bool m_isClean; + }; + + class Result + { + public: + Result() + : valid(false) + {} + + QVector deps; + FileTags additionalFileTags; + bool valid; + }; + + Result value(const void* scanner, const QString &fileName) const; + void insert(const void* scanner, const QString &fileName, const Result &value); + void remove(const QString &fileName); + +private: + typedef QHash > ScanResultCacheData; + ScanResultCacheData 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 00000000..4af716a2 --- /dev/null +++ b/src/lib/corelib/buildgraph/timestampsupdater.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "timestampsupdater.h" + +#include "artifact.h" +#include "artifactvisitor.h" +#include "productbuilddata.h" +#include "projectbuilddata.h" + +#include +#include +#include +#include + +#include + +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->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 &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 00000000..cc92f0e4 --- /dev/null +++ b/src/lib/corelib/buildgraph/timestampsupdater.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TIMESTAMPSUPDATER_H +#define TIMESTAMPSUPDATER_H + +#include + +#include + +namespace qbs { +namespace Internal { +class Logger; + +class TimestampsUpdater +{ +public: + void updateTimestamps(const TopLevelProjectPtr &project, + const QList &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 00000000..3d0eedbf --- /dev/null +++ b/src/lib/corelib/buildgraph/transformer.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "transformer.h" + +#include "artifact.h" +#include "command.h" +#include "rulesevaluationcontext.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace qbs { +namespace Internal { + +Transformer::Transformer() : alwaysRun(false) +{ +} + +Transformer::~Transformer() +{ +} + +static QScriptValue js_baseName(QScriptContext *ctx, QScriptEngine *engine, void *arg) +{ + Q_UNUSED(ctx); + Q_UNUSED(engine); + Artifact *artifact = reinterpret_cast(arg); + return QScriptValue(FileInfo::baseName(artifact->filePath())); +} + +static QScriptValue js_completeBaseName(QScriptContext *ctx, QScriptEngine *engine, void *arg) +{ + Q_UNUSED(ctx); + Q_UNUSED(engine); + Artifact *artifact = reinterpret_cast(arg); + return QScriptValue(FileInfo::completeBaseName(artifact->filePath())); +} + +static QScriptValue js_baseDir(QScriptContext *ctx, QScriptEngine *engine, void *arg) +{ + Q_UNUSED(ctx); + Q_UNUSED(engine); + Artifact *artifact = reinterpret_cast(arg); + QString basedir; + if (artifact->artifactType == Artifact::SourceFile) { + QDir sourceDir(artifact->product->sourceDirectory); + basedir = FileInfo::path(sourceDir.relativeFilePath(artifact->filePath())); + } else { + QDir buildDir(artifact->product->buildDirectory()); + basedir = FileInfo::path(buildDir.relativeFilePath(artifact->filePath())); + } + return basedir; +} + +static void setArtifactProperty(QScriptValue &obj, const QString &name, + QScriptEngine::FunctionWithArgSignature func, Artifact *artifact) +{ + obj.setProperty(name, obj.engine()->newFunction(func, (void *)artifact), + QScriptValue::PropertyGetter); +} + +QScriptValue Transformer::translateFileConfig(QScriptEngine *scriptEngine, Artifact *artifact, const QString &defaultModuleName) +{ + QScriptValue obj = scriptEngine->newObject(); + attachPointerTo(obj, artifact); + ModuleProperties::init(obj, artifact); + obj.setProperty(QLatin1String("fileName"), artifact->fileName()); + obj.setProperty(QLatin1String("filePath"), artifact->filePath()); + setArtifactProperty(obj, QLatin1String("baseName"), js_baseName, artifact); + setArtifactProperty(obj, QLatin1String("completeBaseName"), js_completeBaseName, artifact); + setArtifactProperty(obj, QLatin1String("baseDir"), js_baseDir, artifact); + const QStringList fileTags = artifact->fileTags().toStringList(); + obj.setProperty(QLatin1String("fileTags"), scriptEngine->toScriptValue(fileTags)); + if (!defaultModuleName.isEmpty()) + obj.setProperty(QLatin1String("moduleName"), defaultModuleName); + return obj; +} + +static bool compareByFilePath(const Artifact *a1, const Artifact *a2) +{ + return a1->filePath() < a2->filePath(); +} + +QScriptValue Transformer::translateInOutputs(QScriptEngine *scriptEngine, const ArtifactSet &artifacts, const QString &defaultModuleName) +{ + typedef QMap > TagArtifactsMap; + TagArtifactsMap tagArtifactsMap; + foreach (Artifact *artifact, artifacts) + foreach (const FileTag &fileTag, artifact->fileTags()) + tagArtifactsMap[fileTag.toString()].append(artifact); + for (TagArtifactsMap::Iterator it = tagArtifactsMap.begin(); it != tagArtifactsMap.end(); ++it) + std::sort(it.value().begin(), it.value().end(), compareByFilePath); + + QScriptValue jsTagFiles = scriptEngine->newObject(); + for (TagArtifactsMap::const_iterator tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) { + const QList &artifacts = tag.value(); + QScriptValue jsFileConfig = scriptEngine->newArray(artifacts.count()); + int i=0; + foreach (Artifact *artifact, artifacts) { + 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(QScriptValue targetScriptValue, const ArtifactSet &inputs, + const QString &defaultModuleName) +{ + QScriptEngine *const scriptEngine = targetScriptValue.engine(); + QScriptValue scriptValue = translateInOutputs(scriptEngine, inputs, defaultModuleName); + targetScriptValue.setProperty(QLatin1String("inputs"), scriptValue); + QScriptValue inputScriptValue; + 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()); + inputScriptValue = inputsForFileTag.property(0); + } + targetScriptValue.setProperty(QLatin1String("input"), inputScriptValue); +} + +void Transformer::setupInputs(QScriptValue targetScriptValue) +{ + setupInputs(targetScriptValue, inputs, rule->module->name); +} + +void Transformer::setupOutputs(QScriptEngine *scriptEngine, QScriptValue targetScriptValue) +{ + const QString &defaultModuleName = rule->module->name; + QScriptValue scriptValue = translateInOutputs(scriptEngine, outputs, defaultModuleName); + targetScriptValue.setProperty(QLatin1String("outputs"), scriptValue); + QScriptValue outputScriptValue; + 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()); + outputScriptValue = outputsForFileTag.property(0); + } + targetScriptValue.setProperty(QLatin1String("output"), outputScriptValue); +} + +static AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scriptValue, + const CodeLocation &codeLocation) +{ + AbstractCommandPtr cmdBase; + if (scriptValue.isUndefined() || !scriptValue.isValid()) + return cmdBase; + QString className = scriptValue.property(QLatin1String("className")).toString(); + if (className == QLatin1String("Command")) + cmdBase = ProcessCommand::create(); + else if (className == QLatin1String("JavaScriptCommand")) + cmdBase = JavaScriptCommand::create(); + 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, + script->location.filePath(), + script->location.line()); + 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))) { + ErrorInfo errorInfo(engine->lastErrorString(scriptValue), + engine->uncaughtExceptionBacktraceOrEmpty()); + if (rule) + errorInfo.append(QStringLiteral("Rule.prepare"), script->location); + else + errorInfo.append(QStringLiteral("Transformer.prepare"), script->location); + throw errorInfo; + } + + commands.clear(); + if (scriptValue.isArray()) { + const int count = scriptValue.property(QLatin1String("length")).toInt32(); + for (qint32 i = 0; i < count; ++i) { + QScriptValue item = scriptValue.property(i); + if (item.isValid() && !item.isUndefined()) { + const AbstractCommandPtr cmd = createCommandFromScriptValue(item, script->location); + if (cmd) + commands += cmd; + } + } + } else { + const AbstractCommandPtr cmd = createCommandFromScriptValue(scriptValue, script->location); + if (cmd) + commands += cmd; + } +} + +void Transformer::load(PersistentPool &pool) +{ + rule = pool.idLoadS(); + pool.loadContainer(inputs); + pool.loadContainer(outputs); + propertiesRequestedInPrepareScript = restorePropertySet(pool); + propertiesRequestedInCommands = restorePropertySet(pool); + propertiesRequestedFromArtifactInPrepareScript = restorePropertyHash(pool); + propertiesRequestedFromArtifactInCommands = restorePropertyHash(pool); + commands = loadCommandList(pool); + pool.stream() >> alwaysRun; +} + +void Transformer::store(PersistentPool &pool) const +{ + pool.store(rule); + pool.storeContainer(inputs); + pool.storeContainer(outputs); + storePropertySet(pool, propertiesRequestedInPrepareScript); + storePropertySet(pool, propertiesRequestedInCommands); + storePropertyHash(pool, propertiesRequestedFromArtifactInPrepareScript); + storePropertyHash(pool, propertiesRequestedFromArtifactInCommands); + storeCommandList(commands, pool); + pool.stream() << alwaysRun; +} + +} // 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 00000000..844ffc42 --- /dev/null +++ b/src/lib/corelib/buildgraph/transformer.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_TRANSFORMER_H +#define QBS_TRANSFORMER_H + +#include "artifactset.h" +#include "forward_decls.h" +#include +#include +#include + +#include +#include + +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(); + + ArtifactSet inputs; // Subset of "children of all outputs". + ArtifactSet outputs; + RuleConstPtr rule; + QList commands; + PropertySet propertiesRequestedInPrepareScript; + PropertySet propertiesRequestedInCommands; + QHash propertiesRequestedFromArtifactInPrepareScript; + QHash propertiesRequestedFromArtifactInCommands; + bool alwaysRun; + + static QScriptValue translateFileConfig(QScriptEngine *scriptEngine, + Artifact *artifact, + const QString &defaultModuleName); + static QScriptValue translateInOutputs(QScriptEngine *scriptEngine, + const ArtifactSet &artifacts, + const QString &defaultModuleName); + + ResolvedProductPtr product() const; + static void setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs, + const QString &defaultModuleName); + void setupInputs(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 00000000..f23ffa2d --- /dev/null +++ b/src/lib/corelib/buildgraph/tst_buildgraph.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "tst_buildgraph.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { + +const TopLevelProjectPtr project = TopLevelProject::create(); + +TestBuildGraph::TestBuildGraph(ILogSink *logSink) : m_logSink(logSink) +{ + project->buildData.reset(new ProjectBuildData); +} + +void TestBuildGraph::initTestCase() +{ +} + +void TestBuildGraph::cleanupTestCase() +{ +} + + +bool TestBuildGraph::cycleDetected(const ResolvedProductConstPtr &product) +{ + try { + CycleDetector(Logger(m_logSink)).visitProduct(product); + return false; + } catch (const ErrorInfo &) { + return true; + } +} + +ResolvedProductConstPtr TestBuildGraph::productWithDirectCycle() +{ + const ResolvedProductPtr product = ResolvedProduct::create(); + product->project = project; + product->buildData.reset(new ProductBuildData); + Artifact * const root = new Artifact; + root->product = product; + Artifact * const child = new Artifact; + child->product = product; + product->buildData->roots.insert(root); + product->buildData->nodes << root << child; + qbs::Internal::connect(root, child); + qbs::Internal::connect(child, root); + return product; +} + +ResolvedProductConstPtr TestBuildGraph::productWithLessDirectCycle() +{ + const ResolvedProductPtr product = ResolvedProduct::create(); + product->project = project; + product->buildData.reset(new ProductBuildData); + Artifact * const root = new Artifact; + Artifact * const child = new Artifact; + Artifact * const grandchild = new Artifact; + root->product = product; + child->product = product; + grandchild->product = product; + product->buildData->roots << root; + product->buildData->nodes << root << child << grandchild; + qbs::Internal::connect(root, child); + qbs::Internal::connect(child, grandchild); + qbs::Internal::connect(grandchild, root); + return product; +} + +// root appears as a child, but in a different tree +ResolvedProductConstPtr TestBuildGraph::productWithNoCycle() +{ + const ResolvedProductPtr product = ResolvedProduct::create(); + product->project = project; + product->buildData.reset(new ProductBuildData); + Artifact * const root = new Artifact; + Artifact * const root2 = new Artifact; + root->product = product; + root2->product = product; + product->buildData->roots << root << root2; + product->buildData->nodes << root << root2; + qbs::Internal::connect(root2, root); + 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 00000000..38e11272 --- /dev/null +++ b/src/lib/corelib/buildgraph/tst_buildgraph.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TST_BUILDGRAPH_H +#define TST_BUILDGRAPH_H + +#include +#include +#include +#include + +#include +#include + +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); + + 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 00000000..c2926f3a --- /dev/null +++ b/src/lib/corelib/corelib.pro @@ -0,0 +1,35 @@ +TARGET = qbscore +include(../library.pri) + +QT += core-private network script +qbs_enable_unit_tests:QT += testlib +qbs_enable_project_file_updates: QT += gui + +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(generators/generators.pri) +include(jsextensions/jsextensions.pri) +include(language/language.pri) +include(logging/logging.pri) +include(parser/parser.pri) +include(tools/tools.pri) + +win32:LIBS += -lpsapi -lshell32 + +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 00000000..2aaeefe8 --- /dev/null +++ b/src/lib/corelib/corelib.qbs @@ -0,0 +1,481 @@ +import qbs 1.0 +import QbsFunctions + +QbsLibrary { + Depends { name: "clangcompilationdbgenerator" } + Depends { name: "visualstudiogenerator" } + Depends { name: "cpp" } + Depends { name: "Qt"; submodules: ["core-private", "network", "script", "xml"] } + Depends { condition: qbsbuildconfig.enableProjectFileUpdates; name: "Qt.gui" } + Depends { condition: qbsbuildconfig.enableUnitTests; name: "Qt.testlib" } + name: "qbscore" + cpp.includePaths: base.concat([ + ".", + "../.." // for the plugin headers + ]) + property stringList projectFileUpdateDefines: + qbsbuildconfig.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : [] + cpp.defines: base.concat([ + "QBS_VERSION=\"" + version + "\"", + "QT_CREATOR", "QML_BUILD_STATIC_LIB", // needed for QmlJS + "SRCDIR=\"" + path + "\"" + ]).concat(projectFileUpdateDefines) + + Properties { + condition: qbs.targetOS.contains("windows") + cpp.dynamicLibraries: base.concat(["Psapi", "shell32"]) + } + cpp.dynamicLibraries: base + + Properties { + condition: qbs.targetOS.contains("darwin") + cpp.frameworks: ["Foundation", "Security"] + } + + Group { + name: product.name + files: ["qbs.h"] + qbs.install: qbsbuildconfig.installApiHeaders + qbs.installDir: headerInstallPrefix + } + Group { + name: "project file updating" + condition: qbsbuildconfig.enableProjectFileUpdates + prefix: "api/" + files: [ + "changeset.cpp", + "changeset.h", + "projectfileupdater.cpp", + "projectfileupdater.h", + "qmljsrewriter.cpp", + "qmljsrewriter.h", + ] + } + + Group { + name: "api" + prefix: name + '/' + files: [ + "internaljobs.cpp", + "internaljobs.h", + "jobs.cpp", + "languageinfo.cpp", + "project.cpp", + "project_p.h", + "projectdata.cpp", + "projectdata_p.h", + "propertymap_p.h", + "rulecommand.cpp", + "rulecommand_p.h", + "runenvironment.cpp", + ] + } + Group { + name: "public api headers" + qbs.install: qbsbuildconfig.installApiHeaders + qbs.installDir: headerInstallPrefix + "/api" + prefix: "api/" + files: [ + "jobs.h", + "languageinfo.h", + "project.h", + "projectdata.h", + "rulecommand.h", + "runenvironment.h" + ] + } + Group { + name: "buildgraph" + prefix: name + '/' + files: [ + "abstractcommandexecutor.cpp", + "abstractcommandexecutor.h", + "artifact.cpp", + "artifact.h", + "artifactcleaner.cpp", + "artifactcleaner.h", + "artifactset.cpp", + "artifactset.h", + "artifactvisitor.cpp", + "artifactvisitor.h", + "buildgraph.cpp", + "buildgraph.h", + "buildgraphnode.cpp", + "buildgraphnode.h", + "buildgraphloader.cpp", + "buildgraphloader.h", + "buildgraphvisitor.h", + "command.cpp", + "command.h", + "cycledetector.cpp", + "cycledetector.h", + "depscanner.cpp", + "depscanner.h", + "emptydirectoriesremover.cpp", + "emptydirectoriesremover.h", + "executor.cpp", + "executor.h", + "executorjob.cpp", + "executorjob.h", + "filedependency.cpp", + "filedependency.h", + "inputartifactscanner.cpp", + "inputartifactscanner.h", + "jscommandexecutor.cpp", + "jscommandexecutor.h", + "nodeset.cpp", + "nodeset.h", + "nodetreedumper.cpp", + "nodetreedumper.h", + "processcommandexecutor.cpp", + "processcommandexecutor.h", + "productbuilddata.cpp", + "productbuilddata.h", + "productinstaller.cpp", + "productinstaller.h", + "projectbuilddata.cpp", + "projectbuilddata.h", + "qtmocscanner.cpp", + "qtmocscanner.h", + "rescuableartifactdata.cpp", + "rescuableartifactdata.h", + "rulegraph.cpp", + "rulegraph.h", + "rulenode.cpp", + "rulenode.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: qbsbuildconfig.installApiHeaders + qbs.installDir: headerInstallPrefix + "/buildgraph" + files: "buildgraph/forward_decls.h" + } + Group { + name: "generators" + prefix: "generators/" + files: [ + "generatableprojectiterator.cpp", + "generatableprojectiterator.h", + "generator.cpp", + "generator.h", + "generatordata.cpp", + "generatordata.h", + "igeneratableprojectvisitor.h", + ] + } + Group { + name: "jsextensions" + prefix: name + '/' + files: [ + "environmentextension.cpp", + "environmentextension.h", + "file.cpp", + "file.h", + "fileinfoextension.cpp", + "fileinfoextension.h", + "jsextensions.cpp", + "jsextensions.h", + "moduleproperties.cpp", + "moduleproperties.h", + "process.cpp", + "process.h", + "temporarydir.cpp", + "temporarydir.h", + "textfile.cpp", + "textfile.h", + "utilitiesextension.cpp", + "utilitiesextension.h", + "domxml.cpp", + "domxml.h" + ] + } + Group { + name: "jsextensions (Darwin-specific)" + prefix: "jsextensions/" + condition: qbs.targetOS.contains("darwin") + files: [ + "propertylist.h", + "propertylist.mm", + "propertylistutils.h", + "propertylistutils.mm" + ] + } + Group { + name: "language" + prefix: name + '/' + files: [ + "artifactproperties.cpp", + "artifactproperties.h", + "astimportshandler.cpp", + "astimportshandler.h", + "astpropertiesitemhandler.cpp", + "astpropertiesitemhandler.h", + "asttools.cpp", + "asttools.h", + "builtindeclarations.cpp", + "builtindeclarations.h", + "deprecationinfo.h", + "evaluationdata.h", + "evaluator.cpp", + "evaluator.h", + "evaluatorscriptclass.cpp", + "evaluatorscriptclass.h", + "filecontext.cpp", + "filecontext.h", + "filecontextbase.cpp", + "filecontextbase.h", + "filetags.cpp", + "filetags.h", + "functiondeclaration.h", + "identifiersearch.cpp", + "identifiersearch.h", + "item.cpp", + "item.h", + "itemdeclaration.cpp", + "itemdeclaration.h", + "itemobserver.h", + "itempool.cpp", + "itempool.h", + "itemreader.cpp", + "itemreader.h", + "itemreaderastvisitor.cpp", + "itemreaderastvisitor.h", + "itemreadervisitorstate.cpp", + "itemreadervisitorstate.h", + "itemtype.h", + "jsimports.h", + "language.cpp", + "language.h", + "loader.cpp", + "loader.h", + "moduleloader.cpp", + "moduleloader.h", + "modulemerger.cpp", + "modulemerger.h", + "preparescriptobserver.cpp", + "preparescriptobserver.h", + "projectresolver.cpp", + "projectresolver.h", + "property.cpp", + "property.h", + "propertydeclaration.cpp", + "propertydeclaration.h", + "propertymapinternal.cpp", + "propertymapinternal.h", + "qualifiedid.cpp", + "qualifiedid.h", + "resolvedfilecontext.cpp", + "resolvedfilecontext.h", + "scriptengine.cpp", + "scriptengine.h", + "scriptimporter.cpp", + "scriptimporter.h", + "scriptpropertyobserver.h", + "value.cpp", + "value.h", + ] + } + Group { + name: "public language headers" + qbs.install: qbsbuildconfig.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: qbsbuildconfig.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: [ + "architectures.cpp", + "buildgraphlocker.cpp", + "buildgraphlocker.h", + "buildoptions.cpp", + "cleanoptions.cpp", + "codelocation.cpp", + "commandechomode.cpp", + "error.cpp", + "executablefinder.cpp", + "executablefinder.h", + "fileinfo.cpp", + "fileinfo.h", + "filesaver.cpp", + "filesaver.h", + "filetime.h", + "generateoptions.cpp", + "hostosinfo.h", + "id.cpp", + "id.h", + "jsliterals.cpp", + "jsliterals.h", + "installoptions.cpp", + "msvcinfo.cpp", + "msvcinfo.h", + "pathutils.h", + "persistence.cpp", + "persistence.h", + "persistentobject.h", + "preferences.cpp", + "processresult.cpp", + "processresult_p.h", + "processutils.cpp", + "processutils.h", + "profile.cpp", + "profiling.cpp", + "profiling.h", + "progressobserver.cpp", + "progressobserver.h", + "projectgeneratormanager.cpp", + "propertyfinder.cpp", + "propertyfinder.h", + "qbsassert.cpp", + "qbsassert.h", + "qttools.cpp", + "qttools.h", + "scannerpluginmanager.cpp", + "scannerpluginmanager.h", + "scripttools.cpp", + "scripttools.h", + "settings.cpp", + "settingscreator.cpp", + "settingscreator.h", + "settingsmodel.cpp", + "setupprojectparameters.cpp", + "shellutils.cpp", + "shellutils.h", + "toolchains.cpp", + "version.cpp", + "version.h", + "visualstudioversioninfo.cpp", + "visualstudioversioninfo.h", + "vsenvironmentdetector.cpp", + "vsenvironmentdetector.h", + "weakpointer.h" + ] + } + Group { + name: "public tools headers" + prefix: "tools/" + files: [ + "architectures.h", + "buildoptions.h", + "cleanoptions.h", + "codelocation.h", + "commandechomode.h", + "error.h", + "generateoptions.h", + "installoptions.h", + "preferences.h", + "processresult.h", + "profile.h", + "projectgeneratormanager.h", + "qbs_export.h", + "settings.h", + "settingsmodel.h", + "setupprojectparameters.h", + "toolchains.h", + ] + qbs.install: qbsbuildconfig.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("macos") + name: "tools (macOS)" + prefix: "tools/" + files: [ + "applecodesignutils.cpp", + "applecodesignutils.h" + ] + } + 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: qbsbuildconfig.installApiHeaders + qbs.installDir: headerInstallPrefix + } + Group { + condition: qbsbuildconfig.enableUnitTests + name: "tests" + cpp.defines: outer.filter(function(def) { return def !== "QT_NO_CAST_FROM_ASCII"; }) + 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: "cpp" } + cpp.defines: product.projectFileUpdateDefines + } +} diff --git a/src/lib/corelib/generators/clangcompilationdb/clangcompilationdb.pri b/src/lib/corelib/generators/clangcompilationdb/clangcompilationdb.pri new file mode 100644 index 00000000..61dc8f19 --- /dev/null +++ b/src/lib/corelib/generators/clangcompilationdb/clangcompilationdb.pri @@ -0,0 +1,5 @@ +HEADERS += \ + $$PWD/clangcompilationdbgenerator.h + +SOURCES += \ + $$PWD/clangcompilationdbgenerator.cpp diff --git a/src/lib/corelib/generators/clangcompilationdb/clangcompilationdb.qbs b/src/lib/corelib/generators/clangcompilationdb/clangcompilationdb.qbs new file mode 100644 index 00000000..c8f62619 --- /dev/null +++ b/src/lib/corelib/generators/clangcompilationdb/clangcompilationdb.qbs @@ -0,0 +1,19 @@ +import qbs + +QbsLibrary { + type: ["staticlibrary"] + name: "clangcompilationdbgenerator" + install: false + + cpp.includePaths: base.concat([ + "../..", + ]) + + Depends { name: "cpp" } + Depends { name: "Qt.core" } + + files: [ + "clangcompilationdbgenerator.cpp", + "clangcompilationdbgenerator.h" + ] +} diff --git a/src/lib/corelib/generators/clangcompilationdb/clangcompilationdbgenerator.cpp b/src/lib/corelib/generators/clangcompilationdb/clangcompilationdbgenerator.cpp new file mode 100644 index 00000000..6d8b8a5e --- /dev/null +++ b/src/lib/corelib/generators/clangcompilationdb/clangcompilationdbgenerator.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "clangcompilationdbgenerator.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +using namespace Internal; + +const QString ClangCompilationDatabaseGenerator::DefaultDatabaseFileName = + QStringLiteral("compile_commands.json"); + +ClangCompilationDatabaseGenerator::ClangCompilationDatabaseGenerator() +{ +} + +QString ClangCompilationDatabaseGenerator::generatorName() const +{ + return QStringLiteral("clangdb"); +} + +void ClangCompilationDatabaseGenerator::generate() +{ + for (const Project &theProject : project().projects.values()) { + QJsonArray database; + const ProjectData projectData = theProject.projectData(); + const QString buildDir = projectData.buildDirectory(); + + for (const ProductData &productData : projectData.allProducts()) { + for (const GroupData &groupData : productData.groups()) { + for (const ArtifactData &sourceArtifact : groupData.allSourceArtifacts()) { + if (!hasValidInputFileTag(sourceArtifact.fileTags())) + continue; + + const QString filePath = sourceArtifact.filePath(); + ErrorInfo errorInfo; + const RuleCommandList rules = theProject.ruleCommands(productData, filePath, + QStringLiteral("obj"), + &errorInfo); + + if (errorInfo.hasError()) + throw errorInfo; + + for (const RuleCommand &rule : rules) { + if (rule.type() != RuleCommand::ProcessCommandType) + continue; + database.append(createEntry(filePath, buildDir, rule)); + } + } + } + } + + writeProjectDatabase(QDir(buildDir).filePath(DefaultDatabaseFileName), database); + } +} + +// See http://clang.llvm.org/docs/JSONCompilationDatabase.html +QJsonObject ClangCompilationDatabaseGenerator::createEntry(const QString &filePath, + const QString &buildDir, + const RuleCommand &ruleCommand) +{ + QString workDir = ruleCommand.workingDirectory(); + if (workDir.isEmpty()) + workDir = buildDir; + + const QStringList arguments = QStringList() << ruleCommand.executable() + << ruleCommand.arguments(); + + const QJsonObject object = { + { QStringLiteral("directory"), QJsonValue(workDir) }, + { QStringLiteral("arguments"), QJsonArray::fromStringList(arguments) }, + { QStringLiteral("file"), QJsonValue(filePath) } + }; + return object; +} + +void ClangCompilationDatabaseGenerator::writeProjectDatabase(const QString &filePath, + const QJsonArray &entries) +{ + const QJsonDocument database(entries); + QFile databaseFile(filePath); + + if (!databaseFile.open(QFile::WriteOnly)) + throw ErrorInfo(Tr::tr("Cannot open '%1' for writing: %2") + .arg(filePath) + .arg(databaseFile.errorString())); + + if (databaseFile.write(database.toJson()) == -1) + throw ErrorInfo(Tr::tr("Error while writing '%1': %2") + .arg(filePath) + .arg(databaseFile.errorString())); +} + +bool ClangCompilationDatabaseGenerator::hasValidInputFileTag(const QStringList &fileTags) const +{ + static const QStringList validFileTags = { + QStringLiteral("c"), + QStringLiteral("cpp"), + QStringLiteral("objc"), + QStringLiteral("objcpp") + }; + + for (const QString &tag : fileTags) { + if (validFileTags.contains(tag)) + return true; + } + return false; +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/clangcompilationdb/clangcompilationdbgenerator.h b/src/lib/corelib/generators/clangcompilationdb/clangcompilationdbgenerator.h new file mode 100644 index 00000000..5a850507 --- /dev/null +++ b/src/lib/corelib/generators/clangcompilationdb/clangcompilationdbgenerator.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_CLANGCOMPILATIONDATABASEGENERATOR_H +#define QBS_CLANGCOMPILATIONDATABASEGENERATOR_H + +#include + +namespace qbs { + +class SourceArtifact; +class ProjectData; + +class ClangCompilationDatabaseGenerator : public ProjectGenerator +{ +public: + ClangCompilationDatabaseGenerator(); + +private: + QString generatorName() const override; + void generate() override; + static const QString DefaultDatabaseFileName; + QJsonObject createEntry(const QString &filePath, const QString &buildDir, + const RuleCommand &ruleCommand); + void writeProjectDatabase(const QString &filePath, const QJsonArray &entries); + bool hasValidInputFileTag(const QStringList &fileTags) const; +}; + +} // namespace qbs + +#endif // QBS_VISUALSTUDIOGENERATOR_H diff --git a/src/lib/corelib/generators/generatableprojectiterator.cpp b/src/lib/corelib/generators/generatableprojectiterator.cpp new file mode 100644 index 00000000..c6d990f0 --- /dev/null +++ b/src/lib/corelib/generators/generatableprojectiterator.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "generatableprojectiterator.h" + +namespace qbs { + +GeneratableProjectIterator::GeneratableProjectIterator(const GeneratableProject &project) + : project(project) +{ +} + +void GeneratableProjectIterator::accept(IGeneratableProjectVisitor *visitor) +{ + visitor->visitProject(project); + QMapIterator it(project.projects); + while (it.hasNext()) { + it.next(); + visitor->visitProject(it.value(), it.key()); + } + + accept(project, GeneratableProjectData(), project, visitor); +} + +void GeneratableProjectIterator::accept(const GeneratableProject &project, + const GeneratableProjectData &parentProjectData, + const GeneratableProjectData &projectData, + IGeneratableProjectVisitor *visitor) +{ + visitor->visitProjectData(project, parentProjectData, projectData); + visitor->visitProjectData(project, projectData); + QMapIterator it(projectData.data); + while (it.hasNext()) { + it.next(); + visitor->visitProjectData(parentProjectData.data.value(it.key()), it.value(), it.key()); + visitor->visitProjectData(it.value(), it.key()); + } + + for (const auto &subProject : projectData.subProjects) { + accept(project, projectData, subProject, visitor); + } + + for (const auto &productDataMap : projectData.products) { + visitor->visitProduct(project, projectData, productDataMap); + QMapIterator it(productDataMap.data); + while (it.hasNext()) { + it.next(); + visitor->visitProduct(it.value(), it.key()); + } + } +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/generatableprojectiterator.h b/src/lib/corelib/generators/generatableprojectiterator.h new file mode 100644 index 00000000..295ed6d1 --- /dev/null +++ b/src/lib/corelib/generators/generatableprojectiterator.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GENERATABLEPROJECTITERATOR_H +#define GENERATABLEPROJECTITERATOR_H + +#include "generatordata.h" +#include "igeneratableprojectvisitor.h" + +namespace qbs { + +class GeneratableProjectIterator { + GeneratableProject project; + +public: + GeneratableProjectIterator(const GeneratableProject &project); + void accept(IGeneratableProjectVisitor *visitor); + +private: + void accept(const GeneratableProject &project, const GeneratableProjectData &parentProjectData, + const GeneratableProjectData &projectData, + IGeneratableProjectVisitor *visitor); +}; + +} // namespace qbs + +#endif // GENERATABLEPROJECTITERATOR_H diff --git a/src/lib/corelib/generators/generator.cpp b/src/lib/corelib/generators/generator.cpp new file mode 100644 index 00000000..e167d782 --- /dev/null +++ b/src/lib/corelib/generators/generator.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "generator.h" +#include +#include +#include +#include +#include + +namespace qbs { + +class ProjectGeneratorPrivate { +public: + QList projects; + QList buildConfigurations; + InstallOptions installOptions; +}; + +ProjectGenerator::ProjectGenerator() + : d(new ProjectGeneratorPrivate) +{ +} + +ProjectGenerator::~ProjectGenerator() +{ + delete d; +} + +static QString _configurationName(const Project &project) +{ + return project.projectConfiguration() + .value(QStringLiteral("qbs")).toMap() + .value(QStringLiteral("configurationName")).toString(); +} + +static QString _configurationName(const QVariantMap &buildConfiguration) +{ + return buildConfiguration.value(QStringLiteral("qbs.configurationName")).toString(); +} + +void ProjectGenerator::generate(const QList &projects, + const QList &buildConfigurations, + const InstallOptions &installOptions) +{ + d->projects = projects; + std::sort(d->projects.begin(), d->projects.end(), + [](const Project &a, const Project &b) { + return _configurationName(a) < _configurationName(b); }); + d->buildConfigurations = buildConfigurations; + std::sort(d->buildConfigurations.begin(), d->buildConfigurations.end(), + [](const QVariantMap &a, const QVariantMap &b) { + return _configurationName(a) < _configurationName(b); }); + d->installOptions = installOptions; + generate(); +} + +QList ProjectGenerator::projects() const +{ + return d->projects; +} + +QList ProjectGenerator::buildConfigurations() const +{ + return d->buildConfigurations; +} + +QVariantMap ProjectGenerator::buildConfiguration(const Project &project) const +{ + int idx = d->projects.indexOf(project); + if (idx < 0) + return QVariantMap(); + return d->buildConfigurations.at(idx); +} + +QStringList ProjectGenerator::buildConfigurationCommandLine(const Project &project) const +{ + QVariantMap config = buildConfiguration(project); + + const QString name = config.take(QStringLiteral("qbs.configurationName")).toString(); + if (name.isEmpty()) + throw ErrorInfo(QStringLiteral("Can't find configuration name for project")); + + QStringList commandLineParameters; + commandLineParameters += name; + + QMapIterator it(config); + while (it.hasNext()) { + it.next(); + commandLineParameters += it.key() + QStringLiteral(":") + it.value().toString(); + } + + return commandLineParameters; +} + +// Count the number of products in the project (singular) +// Precondition: each project data (i.e. per-configuration project data) +// has the same number of products. +static int _productCount(const QList &projects) +{ + int count = -1; + for (const auto &project : projects) { + int oldCount = count; + count = project.products().size(); + QBS_CHECK(oldCount == -1 || oldCount == count); + } + return count; +} + +static int _subprojectCount(const QList &projects) +{ + int count = -1; + for (const auto &project : projects) { + int oldCount = count; + count = project.subProjects().size(); + QBS_CHECK(oldCount == -1 || oldCount == count); + } + return count; +} + +static GeneratableProjectData _reduceProjectConfigurations( + const QMap &map) { + GeneratableProjectData gproject; + + // Add the project's project data for each configuration + QMapIterator it(map); + while (it.hasNext()) { + it.next(); + gproject.data.insert(it.key(), it.value()); + } + + // Add the project's products... + for (int i = 0; i < _productCount(map.values()); ++i) { + GeneratableProductData prod; + + // once for each configuration + QMapIterator it(map); + while (it.hasNext()) { + it.next(); + prod.data.insert(it.key(), it.value().products().at(i)); + } + + gproject.products.append(prod); + } + + // Add the project's subprojects... + for (int i = 0; i < _subprojectCount(map.values()); ++i) { + QMap subprojectMap; + + // once for each configuration + QMapIterator it(map); + while (it.hasNext()) { + it.next(); + subprojectMap.insert(it.key(), it.value().subProjects().at(i)); + } + + gproject.subProjects.append(_reduceProjectConfigurations(subprojectMap)); + } + + return gproject; +} + +const GeneratableProject ProjectGenerator::project() const +{ + QMap rootProjects; + GeneratableProject proj; + for (const auto &project : projects()) { + const QString configurationName = _configurationName(project); + rootProjects.insert(configurationName, project.projectData()); + proj.projects.insert(configurationName, project); + proj.buildConfigurations.insert(configurationName, buildConfiguration(project)); + proj.commandLines.insert(configurationName, buildConfigurationCommandLine(project)); + } + auto p = _reduceProjectConfigurations(rootProjects); + proj.data = p.data; + proj.products = p.products; + proj.subProjects = p.subProjects; + proj.installRoot = d->installOptions.installRoot(); + return proj; +} + +QFileInfo ProjectGenerator::qbsExecutableFilePath() const +{ + const QString qbsInstallDir = QString::fromLocal8Bit(qgetenv("QBS_INSTALL_DIR")); + auto file = QFileInfo(Internal::HostOsInfo::appendExecutableSuffix(!qbsInstallDir.isEmpty() + ? qbsInstallDir + QLatin1String("/bin/qbs") + : QCoreApplication::applicationDirPath() + QLatin1String("/qbs"))); + QBS_CHECK(file.isAbsolute() && file.exists()); + return file; +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/generator.h b/src/lib/corelib/generators/generator.h new file mode 100644 index 00000000..36662780 --- /dev/null +++ b/src/lib/corelib/generators/generator.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GENERATORPLUGIN_H +#define GENERATORPLUGIN_H + +#include "generatordata.h" +#include +#include + +namespace qbs { + +class ProjectGeneratorPrivate; + +/*! + * \class ProjectGenerator + * \brief The \c ProjectGenerator class is an abstract base class for generators which generate + * arbitrary output given a resolved Qbs project. + */ +class QBS_EXPORT ProjectGenerator : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(ProjectGenerator) +public: + virtual ~ProjectGenerator(); + + /*! + * Returns the name of the generator used to create the external build system files. + */ + virtual QString generatorName() const = 0; + + virtual void generate() = 0; + + void generate(const QList &projects, + const QList &buildConfigurations, + const InstallOptions &installOptions); + + const GeneratableProject project() const; + QFileInfo qbsExecutableFilePath() const; + +private: + QList projects() const; + QList buildConfigurations() const; + QVariantMap buildConfiguration(const Project &project) const; + + QStringList buildConfigurationCommandLine(const Project &project) const; + +protected: + ProjectGenerator(); + +private: + ProjectGeneratorPrivate *d; +}; + +} // namespace qbs + +#ifdef __cplusplus +extern "C" { +#endif + +typedef qbs::ProjectGenerator **(*getGenerators_f)(); + +#ifdef __cplusplus +} +#endif + +#endif // GENERATORPLUGIN_H diff --git a/src/lib/corelib/generators/generatordata.cpp b/src/lib/corelib/generators/generatordata.cpp new file mode 100644 index 00000000..27341d19 --- /dev/null +++ b/src/lib/corelib/generators/generatordata.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "generatordata.h" +#include + +#include + +namespace qbs { + +QString GeneratableProductData::name() const +{ + QString name; + QMapIterator it(data); + while (it.hasNext()) { + it.next(); + QString oldName = name; + name = it.value().name(); + if (!oldName.isEmpty() && oldName != name) + throw ErrorInfo(QLatin1String("Products with different names per-configuration " + "are not compatible with this generator. Consider " + "using the targetName property instead.")); + } + return name; +} + +QStringList GeneratableProductData::dependencies() const +{ + QStringList list; + QMapIterator it(data); + while (it.hasNext()) { + it.next(); + QStringList oldList = list; + list = it.value().dependencies(); + if (!oldList.isEmpty() && oldList != list) + throw ErrorInfo(QLatin1String("Products with different dependency lists " + "per-configuration are not compatible with this " + "generator.")); + } + return list; +} + +QString GeneratableProjectData::name() const +{ + QString name; + QMapIterator it(data); + while (it.hasNext()) { + it.next(); + QString oldName = name; + name = it.value().name(); + if (!oldName.isEmpty() && oldName != name) + throw ErrorInfo(QLatin1String("Projects with different names per-configuration " + "are not compatible with this generator.")); + } + return name; +} + +QDir GeneratableProject::baseBuildDirectory() const +{ + QSet baseBuildDirectory; + QMapIterator it(data); + while (it.hasNext()) { + it.next(); + QDir dir(it.value().buildDirectory()); + dir.cdUp(); + baseBuildDirectory.insert(dir.absolutePath()); + } + Q_ASSERT(baseBuildDirectory.size() == 1); + return baseBuildDirectory.values().first(); +} + +QFileInfo GeneratableProject::filePath() const +{ + QSet filePath; + QMapIterator it(data); + while (it.hasNext()) { + it.next(); + filePath.insert(it.value().location().filePath()); + } + Q_ASSERT(filePath.size() == 1); + return filePath.values().first(); +} + +bool GeneratableProject::hasMultipleConfigurations() const +{ + return projects.size() > 1; +} + +QStringList GeneratableProject::commandLine() const +{ + QStringList combinedCommandLine; + QMapIterator it(commandLines); + while (it.hasNext()) { + it.next(); + combinedCommandLine << it.value(); + } + return combinedCommandLine; +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/generatordata.h b/src/lib/corelib/generators/generatordata.h new file mode 100644 index 00000000..b60e84b0 --- /dev/null +++ b/src/lib/corelib/generators/generatordata.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GENERATORDATA_H +#define GENERATORDATA_H + +#include +#include +#include +#include + +namespace qbs { + +typedef QMap GeneratableProjectMap; +typedef QMap GeneratableProjectDataMap; +typedef QMap GeneratableProductDataMap; + +struct GeneratableProductData { + GeneratableProductDataMap data; + QString name() const; + QStringList dependencies() const; +}; + +struct GeneratableProjectData { + GeneratableProjectDataMap data; + QList subProjects; + QList products; + QString name() const; +}; + +struct GeneratableProject : public GeneratableProjectData { + GeneratableProjectMap projects; + QMap buildConfigurations; + QMap commandLines; + QString installRoot; + QDir baseBuildDirectory() const; + QFileInfo filePath() const; + bool hasMultipleConfigurations() const; + QStringList commandLine() const; +}; + +} // namespace qbs + +#endif // GENERATORDATA_H diff --git a/src/lib/corelib/generators/generators.pri b/src/lib/corelib/generators/generators.pri new file mode 100644 index 00000000..935419c1 --- /dev/null +++ b/src/lib/corelib/generators/generators.pri @@ -0,0 +1,13 @@ +SOURCES += \ + $$PWD/generatableprojectiterator.cpp \ + $$PWD/generator.cpp \ + $$PWD/generatordata.cpp + +HEADERS += \ + $$PWD/generatableprojectiterator.h \ + $$PWD/generator.h \ + $$PWD/generatordata.h \ + $$PWD/igeneratableprojectvisitor.h + +include(clangcompilationdb/clangcompilationdb.pri) +include(visualstudio/visualstudio.pri) diff --git a/src/lib/corelib/generators/generators.qbs b/src/lib/corelib/generators/generators.qbs new file mode 100644 index 00000000..b6f40ec8 --- /dev/null +++ b/src/lib/corelib/generators/generators.qbs @@ -0,0 +1,8 @@ +import qbs + +Project { + references: [ + "clangcompilationdb/clangcompilationdb.qbs", + "visualstudio/visualstudio.qbs", + ] +} diff --git a/src/lib/corelib/generators/igeneratableprojectvisitor.h b/src/lib/corelib/generators/igeneratableprojectvisitor.h new file mode 100644 index 00000000..c3bd2858 --- /dev/null +++ b/src/lib/corelib/generators/igeneratableprojectvisitor.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef IGENERATABLEPROJECTVISITOR_H +#define IGENERATABLEPROJECTVISITOR_H + +#include "generatordata.h" + +namespace qbs { + +class IGeneratableProjectVisitor { +public: + virtual ~IGeneratableProjectVisitor() { } + + // Collapsed configurations + virtual void visitProject(const GeneratableProject &project) { + Q_UNUSED(project); + } + + virtual void visitProjectData(const GeneratableProject &project, + const GeneratableProjectData &parentProjectData, + const GeneratableProjectData &projectData) { + Q_UNUSED(project); + Q_UNUSED(parentProjectData); + Q_UNUSED(projectData); + } + + virtual void visitProjectData(const GeneratableProject &project, + const GeneratableProjectData &projectData) { + Q_UNUSED(project); + Q_UNUSED(projectData); + } + + virtual void visitProduct(const GeneratableProject &project, + const GeneratableProjectData &projectData, + const GeneratableProductData &productData) { + Q_UNUSED(project); + Q_UNUSED(projectData); + Q_UNUSED(productData); + } + + // Expanded configurations + virtual void visitProject(const Project &project, const QString &configuration) { + Q_UNUSED(project); + Q_UNUSED(configuration); + } + + virtual void visitProjectData(const ProjectData &parentProjectData, + const ProjectData &projectData, + const QString &configuration) { + Q_UNUSED(parentProjectData); + Q_UNUSED(projectData); + Q_UNUSED(configuration); + } + + virtual void visitProjectData(const ProjectData &projectData, + const QString &configuration) { + Q_UNUSED(projectData); + Q_UNUSED(configuration); + } + + virtual void visitProduct(const ProductData &productData, + const QString &configuration) { + Q_UNUSED(productData); + Q_UNUSED(configuration); + } +}; + +} // namespace qbs + +#endif // IGENERATABLEPROJECTVISITOR_H diff --git a/src/lib/corelib/generators/visualstudio/io/msbuildprojectwriter.cpp b/src/lib/corelib/generators/visualstudio/io/msbuildprojectwriter.cpp new file mode 100644 index 00000000..0ca7c16e --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/io/msbuildprojectwriter.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildprojectwriter.h" + +#include "../msbuild/imsbuildnodevisitor.h" +#include "../msbuild/msbuildimport.h" +#include "../msbuild/msbuildimportgroup.h" +#include "../msbuild/msbuilditem.h" +#include "../msbuild/msbuilditemdefinitiongroup.h" +#include "../msbuild/msbuilditemgroup.h" +#include "../msbuild/msbuilditemmetadata.h" +#include "../msbuild/msbuildproject.h" +#include "../msbuild/msbuildproperty.h" +#include "../msbuild/msbuildpropertygroup.h" + +#include + +namespace qbs { + +static const QString kMSBuildSchemaURI = + QStringLiteral("http://schemas.microsoft.com/developer/msbuild/2003"); + +class MSBuildProjectWriterPrivate : public IMSBuildNodeVisitor +{ +public: + QScopedPointer writer; + + void visitStart(const MSBuildImport *import) override; + void visitEnd(const MSBuildImport *import) override; + + void visitStart(const MSBuildImportGroup *importGroup) override; + void visitEnd(const MSBuildImportGroup *importGroup) override; + + void visitStart(const MSBuildItem *item) override; + void visitEnd(const MSBuildItem *item) override; + + void visitStart(const MSBuildItemDefinitionGroup *itemDefinitionGroup) override; + void visitEnd(const MSBuildItemDefinitionGroup *itemDefinitionGroup) override; + + void visitStart(const MSBuildItemGroup *itemGroup) override; + void visitEnd(const MSBuildItemGroup *itemGroup) override; + + void visitStart(const MSBuildItemMetadata *itemMetadata) override; + void visitEnd(const MSBuildItemMetadata *itemMetadata) override; + + void visitStart(const MSBuildProject *project) override; + void visitEnd(const MSBuildProject *project) override; + + void visitStart(const MSBuildProperty *property) override; + void visitEnd(const MSBuildProperty *property) override; + + void visitStart(const MSBuildPropertyGroup *propertyGroup) override; + void visitEnd(const MSBuildPropertyGroup *propertyGroup) override; +}; + +MSBuildProjectWriter::MSBuildProjectWriter(QIODevice *device) + : d(new MSBuildProjectWriterPrivate) +{ + d->writer.reset(new QXmlStreamWriter(device)); + d->writer->setAutoFormatting(true); +} + +MSBuildProjectWriter::~MSBuildProjectWriter() +{ + delete d; +} + +bool MSBuildProjectWriter::write(const MSBuildProject *project) +{ + d->writer->writeStartDocument(); + project->accept(d); + d->writer->writeEndDocument(); + return !d->writer->hasError(); +} + +void MSBuildProjectWriterPrivate::visitStart(const MSBuildImport *import) +{ + writer->writeStartElement(QStringLiteral("Import")); + writer->writeAttribute(QStringLiteral("Project"), import->project()); + if (!import->condition().isEmpty()) + writer->writeAttribute(QStringLiteral("Condition"), import->condition()); +} + +void MSBuildProjectWriterPrivate::visitEnd(const MSBuildImport *) +{ + writer->writeEndElement(); +} + +void MSBuildProjectWriterPrivate::visitStart(const MSBuildImportGroup *importGroup) +{ + writer->writeStartElement(QStringLiteral("ImportGroup")); + if (!importGroup->condition().isEmpty()) + writer->writeAttribute(QStringLiteral("Condition"), importGroup->condition()); + if (!importGroup->label().isEmpty()) + writer->writeAttribute(QStringLiteral("Label"), importGroup->label()); +} + +void MSBuildProjectWriterPrivate::visitEnd(const MSBuildImportGroup *) +{ + writer->writeEndElement(); +} + +void MSBuildProjectWriterPrivate::visitStart(const MSBuildItem *item) +{ + writer->writeStartElement(item->name()); + if (!item->include().isEmpty()) + writer->writeAttribute(QStringLiteral("Include"), item->include()); +} + +void MSBuildProjectWriterPrivate::visitEnd(const MSBuildItem *) +{ + writer->writeEndElement(); +} + +void MSBuildProjectWriterPrivate::visitStart(const MSBuildItemDefinitionGroup *itemDefinitionGroup) +{ + writer->writeStartElement(QStringLiteral("ItemDefinitionGroup")); + if (!itemDefinitionGroup->condition().isEmpty()) + writer->writeAttribute(QStringLiteral("Condition"), itemDefinitionGroup->condition()); +} + +void MSBuildProjectWriterPrivate::visitEnd(const MSBuildItemDefinitionGroup *) +{ + writer->writeEndElement(); +} + +void MSBuildProjectWriterPrivate::visitStart(const MSBuildItemGroup *itemGroup) +{ + writer->writeStartElement(QStringLiteral("ItemGroup")); + if (!itemGroup->condition().isEmpty()) + writer->writeAttribute(QStringLiteral("Condition"), itemGroup->condition()); + if (!itemGroup->label().isEmpty()) + writer->writeAttribute(QStringLiteral("Label"), itemGroup->label()); +} + +void MSBuildProjectWriterPrivate::visitEnd(const MSBuildItemGroup *) +{ + writer->writeEndElement(); +} + +void MSBuildProjectWriterPrivate::visitStart(const MSBuildItemMetadata *itemMetadata) +{ + QString stringValue; + if (itemMetadata->value().type() == QVariant::Bool) { + stringValue = itemMetadata->value().toBool() + ? QStringLiteral("True") + : QStringLiteral("False"); + } else { + stringValue = itemMetadata->value().toString(); + } + writer->writeTextElement(itemMetadata->name(), stringValue); +} + +void MSBuildProjectWriterPrivate::visitEnd(const MSBuildItemMetadata *) +{ +} + +void MSBuildProjectWriterPrivate::visitStart(const MSBuildProject *project) +{ + writer->writeStartElement(QStringLiteral("Project")); + if (!project->defaultTargets().isEmpty()) + writer->writeAttribute(QStringLiteral("DefaultTargets"), project->defaultTargets()); + if (!project->toolsVersion().isEmpty()) + writer->writeAttribute(QStringLiteral("ToolsVersion"), project->toolsVersion()); + writer->writeAttribute(QStringLiteral("xmlns"), kMSBuildSchemaURI); +} + +void MSBuildProjectWriterPrivate::visitEnd(const MSBuildProject *) +{ + writer->writeEndElement(); +} + +void MSBuildProjectWriterPrivate::visitStart(const MSBuildProperty *property) +{ + QString stringValue; + if (property->value().type() == QVariant::Bool) + stringValue = property->value().toBool() ? QStringLiteral("True") : QStringLiteral("False"); + else + stringValue = property->value().toString(); + writer->writeTextElement(property->name(), stringValue); +} + +void MSBuildProjectWriterPrivate::visitEnd(const MSBuildProperty *) +{ +} + +void MSBuildProjectWriterPrivate::visitStart(const MSBuildPropertyGroup *propertyGroup) +{ + writer->writeStartElement(QStringLiteral("PropertyGroup")); + if (!propertyGroup->condition().isEmpty()) + writer->writeAttribute(QStringLiteral("Condition"), propertyGroup->condition()); + if (!propertyGroup->label().isEmpty()) + writer->writeAttribute(QStringLiteral("Label"), propertyGroup->label()); +} + +void MSBuildProjectWriterPrivate::visitEnd(const MSBuildPropertyGroup *) +{ + writer->writeEndElement(); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/io/msbuildprojectwriter.h b/src/lib/corelib/generators/visualstudio/io/msbuildprojectwriter.h new file mode 100644 index 00000000..f2a9a991 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/io/msbuildprojectwriter.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDPROJECTWRITER_H +#define MSBUILDPROJECTWRITER_H + +#include + +namespace qbs { + +class MSBuildProject; +class MSBuildProjectWriterPrivate; + +class MSBuildProjectWriter +{ + Q_DISABLE_COPY(MSBuildProjectWriter) +public: + explicit MSBuildProjectWriter(QIODevice *device); + ~MSBuildProjectWriter(); + + bool write(const MSBuildProject *project); + +private: + MSBuildProjectWriterPrivate *d; +}; + +} // namespace qbs + +#endif // MSBUILDPROJECTWRITER_H diff --git a/src/lib/corelib/generators/visualstudio/io/visualstudiosolutionwriter.cpp b/src/lib/corelib/generators/visualstudio/io/visualstudiosolutionwriter.cpp new file mode 100644 index 00000000..4e786aa5 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/io/visualstudiosolutionwriter.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "visualstudiosolutionwriter.h" + +#include "../solution/visualstudiosolutionfileproject.h" +#include "../solution/visualstudiosolutionfolderproject.h" +#include "../solution/visualstudiosolutionglobalsection.h" +#include "../solution/visualstudiosolution.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace qbs { + +class VisualStudioSolutionWriterPrivate +{ +public: + QIODevice *device; + QString baseDir; +}; + +VisualStudioSolutionWriter::VisualStudioSolutionWriter(QIODevice *device) + : d(new VisualStudioSolutionWriterPrivate) +{ + d->device = device; +} + +VisualStudioSolutionWriter::~VisualStudioSolutionWriter() +{ +} + +QString VisualStudioSolutionWriter::projectBaseDirectory() const +{ + return d->baseDir; +} + +void VisualStudioSolutionWriter::setProjectBaseDirectory(const QString &dir) +{ + d->baseDir = dir; +} + +bool VisualStudioSolutionWriter::write(const VisualStudioSolution *solution) +{ + QTextStream out(d->device); + out << QString(QLatin1String("Microsoft Visual Studio Solution File, " + "Format Version %1\n" + "# Visual Studio %2\n")) + .arg(solution->versionInfo().solutionVersion()) + .arg(solution->versionInfo().version().majorVersion()); + + for (const auto &project : solution->fileProjects()) { + auto projectFilePath = project->filePath(); + + // Try to make the project file path relative to the + // solution file path if we're writing to a file device + if (!d->baseDir.isEmpty()) { + const QDir solutionDir(d->baseDir); + projectFilePath = Internal::PathUtils::toNativeSeparators( + solutionDir.relativeFilePath(projectFilePath), + Internal::HostOsInfo::HostOsWindows); + } + + out << QStringLiteral("Project(\"%1\") = \"%2\", \"%3\", \"%4\"\n") + .arg(project->projectTypeGuid().toString()) + .arg(QFileInfo(projectFilePath).baseName()) + .arg(projectFilePath) + .arg(project->guid().toString()); + + const auto dependencies = solution->dependencies(project); + if (!dependencies.isEmpty()) { + out << "\tProjectSection(ProjectDependencies) = postProject\n"; + + for (const auto &dependency : dependencies) + out << QStringLiteral("\t\t%1 = %1\n").arg(dependency->guid().toString()); + + out << "\tEndProjectSection\n"; + } + + out << "EndProject\n"; + } + + for (const auto &project : solution->folderProjects()) { + out << QStringLiteral("Project(\"%1\") = \"%2\", \"%3\", \"%4\"\n") + .arg(project->projectTypeGuid().toString()) + .arg(project->name()) + .arg(project->name()) + .arg(project->guid().toString()); + out << QStringLiteral("EndProject\n"); + } + + out << "Global\n"; + + for (const auto &globalSection : solution->globalSections()) { + out << QStringLiteral("\tGlobalSection(%1) = %2\n") + .arg(globalSection->name()) + .arg(globalSection->isPost() + ? QStringLiteral("postSolution") + : QStringLiteral("preSolution")); + for (const auto &property : globalSection->properties()) + out << QStringLiteral("\t\t%1 = %2\n").arg(property.first).arg(property.second); + out << "\tEndGlobalSection\n"; + } + + out << "EndGlobal\n"; + + return out.status() == QTextStream::Ok; +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/io/visualstudiosolutionwriter.h b/src/lib/corelib/generators/visualstudio/io/visualstudiosolutionwriter.h new file mode 100644 index 00000000..2f3c0208 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/io/visualstudiosolutionwriter.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef VISUALSTUDIOSOLUTIONWRITER_H +#define VISUALSTUDIOSOLUTIONWRITER_H + +#include +#include + +namespace qbs { + +namespace Internal { class VisualStudioVersionInfo; } + +class VisualStudioSolution; +class VisualStudioSolutionWriterPrivate; + +class VisualStudioSolutionWriter +{ + Q_DISABLE_COPY(VisualStudioSolutionWriter) +public: + explicit VisualStudioSolutionWriter(QIODevice *device); + ~VisualStudioSolutionWriter(); + + QString projectBaseDirectory() const; + void setProjectBaseDirectory(const QString &dir); + + bool write(const VisualStudioSolution *solution); + +private: + void addDefaultGlobalSections(); + + QScopedPointer d; +}; + +} // namespace qbs + +#endif // VISUALSTUDIOSOLUTIONWRITER_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/imsbuildgroup.cpp b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildgroup.cpp new file mode 100644 index 00000000..c51f0e51 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildgroup.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "imsbuildgroup.h" +#include "msbuildproject.h" + +namespace qbs { + +class IMSBuildGroupPrivate +{ +public: + QString condition; +}; + +IMSBuildGroup::IMSBuildGroup(MSBuildProject *parent) + : QObject(parent) + , d(new IMSBuildGroupPrivate) +{ +} + +IMSBuildGroup::~IMSBuildGroup() +{ +} + +QString IMSBuildGroup::condition() const +{ + return d->condition; +} + +void IMSBuildGroup::setCondition(const QString &condition) +{ + d->condition = condition; +} + +IMSBuildItemGroup::IMSBuildItemGroup(MSBuildProject *parent) + : IMSBuildGroup(parent) +{ +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/imsbuildgroup.h b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildgroup.h new file mode 100644 index 00000000..561cd940 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildgroup.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef IMSBUILDGROUP_H +#define IMSBUILDGROUP_H + +#include +#include + +namespace qbs { + +class MSBuildProject; +class IMSBuildGroupPrivate; + +class IMSBuildGroup : public QObject +{ + Q_OBJECT +public: + explicit IMSBuildGroup(MSBuildProject *parent = 0); + virtual ~IMSBuildGroup(); + + QString condition() const; + void setCondition(const QString &condition); + +private: + QScopedPointer d; +}; + +class IMSBuildItemGroup : public IMSBuildGroup +{ + Q_OBJECT +public: + explicit IMSBuildItemGroup(MSBuildProject *parent = 0); +}; + +} // namespace qbs + +#endif // IMSBUILDGROUP_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/imsbuildnode.cpp b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildnode.cpp new file mode 100644 index 00000000..4c70405c --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildnode.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "imsbuildnode.h" + +namespace qbs { + +IMSBuildNode::~IMSBuildNode() +{ +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/imsbuildnode.h b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildnode.h new file mode 100644 index 00000000..67fb1008 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildnode.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef IMSBUILDNODE_H +#define IMSBUILDNODE_H + +namespace qbs { + +class IMSBuildNodeVisitor; + +class IMSBuildNode +{ +public: + virtual ~IMSBuildNode(); + virtual void accept(IMSBuildNodeVisitor *visitor) const = 0; +}; + +} // namespace qbs + +#endif // IMSBUILDNODE_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/imsbuildnodevisitor.h b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildnodevisitor.h new file mode 100644 index 00000000..fe75c23f --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildnodevisitor.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef IMSBUILDNODEVISITOR_H +#define IMSBUILDNODEVISITOR_H + +namespace qbs { + +class MSBuildImport; +class MSBuildImportGroup; +class MSBuildItem; +class MSBuildItemDefinitionGroup; +class MSBuildItemGroup; +class MSBuildItemMetadata; +class MSBuildProject; +class MSBuildProperty; +class MSBuildPropertyGroup; + +class IMSBuildNodeVisitor +{ +public: + virtual ~IMSBuildNodeVisitor() {} + + virtual void visitStart(const MSBuildImport *import) = 0; + virtual void visitEnd(const MSBuildImport *import) = 0; + + virtual void visitStart(const MSBuildImportGroup *importGroup) = 0; + virtual void visitEnd(const MSBuildImportGroup *importGroup) = 0; + + virtual void visitStart(const MSBuildItem *item) = 0; + virtual void visitEnd(const MSBuildItem *item) = 0; + + virtual void visitStart(const MSBuildItemDefinitionGroup *itemDefinitionGroup) = 0; + virtual void visitEnd(const MSBuildItemDefinitionGroup *itemDefinitionGroup) = 0; + + virtual void visitStart(const MSBuildItemGroup *itemGroup) = 0; + virtual void visitEnd(const MSBuildItemGroup *itemGroup) = 0; + + virtual void visitStart(const MSBuildItemMetadata *itemMetadata) = 0; + virtual void visitEnd(const MSBuildItemMetadata *itemMetadata) = 0; + + virtual void visitStart(const MSBuildProject *project) = 0; + virtual void visitEnd(const MSBuildProject *project) = 0; + + virtual void visitStart(const MSBuildProperty *property) = 0; + virtual void visitEnd(const MSBuildProperty *property) = 0; + + virtual void visitStart(const MSBuildPropertyGroup *propertyGroup) = 0; + virtual void visitEnd(const MSBuildPropertyGroup *propertyGroup) = 0; +}; + +} // namespace qbs + +#endif // IMSBUILDNODEVISITOR_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/imsbuildproperty.cpp b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildproperty.cpp new file mode 100644 index 00000000..6e54f1e4 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildproperty.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "imsbuildproperty.h" + +namespace qbs { + +class IMSBuildPropertyPrivate +{ +public: + QString condition; + QString name; + QVariant value; +}; + +IMSBuildProperty::IMSBuildProperty(QObject *parent) + : QObject(parent) + , d(new IMSBuildPropertyPrivate) +{ +} + +IMSBuildProperty::~IMSBuildProperty() +{ +} + +QString IMSBuildProperty::condition() const +{ + return d->condition; +} + +void IMSBuildProperty::setCondition(const QString &condition) +{ + d->condition = condition; +} + +QString IMSBuildProperty::name() const +{ + return d->name; +} + +void IMSBuildProperty::setName(const QString &name) +{ + d->name = name; +} + +QVariant IMSBuildProperty::value() const +{ + return d->value; +} + +void IMSBuildProperty::setValue(const QVariant &value) +{ + d->value = value; +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/imsbuildproperty.h b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildproperty.h new file mode 100644 index 00000000..512bedf3 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/imsbuildproperty.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef IMSBUILDPROPERTY_H +#define IMSBUILDPROPERTY_H + +#include +#include +#include + +namespace qbs { + +class IMSBuildPropertyPrivate; + +class IMSBuildProperty : public QObject +{ + Q_OBJECT +protected: + explicit IMSBuildProperty(QObject *parent = 0); + +public: + virtual ~IMSBuildProperty(); + + QString condition() const; + void setCondition(const QString &condition); + + QString name() const; + void setName(const QString &name); + + QVariant value() const; + void setValue(const QVariant &value); + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // IMSBUILDPROPERTY_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclcompile.cpp b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclcompile.cpp new file mode 100644 index 00000000..d8a58745 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclcompile.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildclcompile.h" + +namespace qbs { + +static const QString MSBuildClCompileItemName = QStringLiteral("ClCompile"); + +MSBuildClCompile::MSBuildClCompile(IMSBuildItemGroup *parent) + : MSBuildFileItem(MSBuildClCompileItemName, parent) +{ +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclcompile.h b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclcompile.h new file mode 100644 index 00000000..745c3ed7 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclcompile.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDCLCOMPILE_H +#define MSBUILDCLCOMPILE_H + +#include "msbuildfileitem.h" + +namespace qbs { + +class MSBuildClCompile : public MSBuildFileItem +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildClCompile) +public: + explicit MSBuildClCompile(IMSBuildItemGroup *parent = 0); +}; + +} // namespace qbs + +#endif // MSBUILDCLCOMPILE_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclinclude.cpp b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclinclude.cpp new file mode 100644 index 00000000..d9c61bec --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclinclude.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildclinclude.h" + +namespace qbs { + +static const QString MSBuildClIncludeItemName = QStringLiteral("ClInclude"); + +MSBuildClInclude::MSBuildClInclude(IMSBuildItemGroup *parent) + : MSBuildFileItem(MSBuildClIncludeItemName, parent) +{ +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclinclude.h b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclinclude.h new file mode 100644 index 00000000..cfe31022 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildclinclude.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDCLINCLUDE_H +#define MSBUILDCLINCLUDE_H + +#include "msbuildfileitem.h" + +namespace qbs { + +class MSBuildClInclude : public MSBuildFileItem +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildClInclude) +public: + explicit MSBuildClInclude(IMSBuildItemGroup *parent = 0); +}; + +} // namespace qbs + +#endif // MSBUILDCLINCLUDE_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfileitem.cpp b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfileitem.cpp new file mode 100644 index 00000000..bce7488b --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfileitem.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildfileitem.h" +#include "../msbuilditemmetadata.h" + +namespace qbs { + +class MSBuildFileItemPrivate +{ +public: + QScopedPointer filter; +}; + +MSBuildFileItem::MSBuildFileItem(const QString &name, IMSBuildItemGroup *parent) + : MSBuildItem(name, parent) + , d(new MSBuildFileItemPrivate) +{ + d->filter.reset(new MSBuildItemMetadata(QStringLiteral("Filter"), QVariant())); +} + +MSBuildFileItem::~MSBuildFileItem() +{ +} + +QString MSBuildFileItem::filePath() const +{ + return include(); +} + +void MSBuildFileItem::setFilePath(const QString &filePath) +{ + setInclude(filePath); +} + +QString MSBuildFileItem::filterName() const +{ + return d->filter->value().toString(); +} + +void MSBuildFileItem::setFilterName(const QString &filterName) +{ + d->filter->setValue(filterName); + d->filter->setParent(!filterName.isEmpty() ? this : nullptr); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfileitem.h b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfileitem.h new file mode 100644 index 00000000..d6e4d485 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfileitem.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDFILEITEM_H +#define MSBUILDFILEITEM_H + +#include "../msbuilditem.h" + +namespace qbs { + +class MSBuildFileItemPrivate; + +class MSBuildFileItem : public MSBuildItem +{ +public: + virtual ~MSBuildFileItem(); + + QString filePath() const; + void setFilePath(const QString &filePath); + + QString filterName() const; + void setFilterName(const QString &filterName); + +protected: + explicit MSBuildFileItem(const QString &name, IMSBuildItemGroup *parent = 0); + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // MSBUILDFILEITEM_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfilter.cpp b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfilter.cpp new file mode 100644 index 00000000..0885f710 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfilter.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildfilter.h" +#include "../msbuilditemmetadata.h" +#include +#include + +namespace qbs { + +static const QString MSBuildFilterItemName = QStringLiteral("Filter"); + +class MSBuildFilterPrivate +{ +public: + QUuid identifier; + QList extensions; + bool parseFiles = true; + bool sourceControlFiles = true; + MSBuildItemMetadata *identifierMetadata; + MSBuildItemMetadata *extensionsMetadata; +}; + +MSBuildFilter::MSBuildFilter(IMSBuildItemGroup *parent) + : MSBuildItem(MSBuildFilterItemName, parent) + , d(new MSBuildFilterPrivate) +{ + d->identifierMetadata = new MSBuildItemMetadata(QStringLiteral("UniqueIdentifier"), + QVariant(), this); + d->extensionsMetadata = new MSBuildItemMetadata(QStringLiteral("Extensions"), + QVariant(), this); + setIdentifier(QUuid::createUuid()); +} + +MSBuildFilter::MSBuildFilter(const QString &name, + const QList &extensions, + IMSBuildItemGroup *parent) + : MSBuildFilter(parent) +{ + setInclude(name); + setExtensions(extensions); +} + +MSBuildFilter::~MSBuildFilter() +{ +} + +QUuid MSBuildFilter::identifier() const +{ + return d->identifier; +} + +void MSBuildFilter::setIdentifier(const QUuid &identifier) +{ + d->identifier = identifier; + d->identifierMetadata->setValue(identifier.toString()); +} + +QList MSBuildFilter::extensions() const +{ + return d->extensions; +} + +void MSBuildFilter::setExtensions(const QList &extensions) +{ + d->extensions = extensions; + d->extensionsMetadata->setValue(QStringList(extensions).join( + Internal::HostOsInfo::pathListSeparator( + Internal::HostOsInfo::HostOsWindows))); +} + +bool MSBuildFilter::parseFiles() const +{ + return d->parseFiles; +} + +void MSBuildFilter::setParseFiles(bool parseFiles) +{ + d->parseFiles = parseFiles; +} + +bool MSBuildFilter::sourceControlFiles() const +{ + return d->sourceControlFiles; +} + +void MSBuildFilter::setSourceControlFiles(bool sourceControlFiles) +{ + d->sourceControlFiles = sourceControlFiles; +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfilter.h b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfilter.h new file mode 100644 index 00000000..344783a1 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildfilter.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDFILTER_H +#define MSBUILDFILTER_H + +#include "../msbuilditem.h" + +namespace qbs { + +class IMSBuildItemGroup; +class MSBuildFilterPrivate; + +class MSBuildFilter : public MSBuildItem +{ + Q_OBJECT +public: + explicit MSBuildFilter(IMSBuildItemGroup *parent = 0); + MSBuildFilter(const QString &name, const QList &extensions, + IMSBuildItemGroup *parent = 0); + ~MSBuildFilter(); + + QUuid identifier() const; + void setIdentifier(const QUuid &identifier); + + QList extensions() const; + void setExtensions(const QList &extensions); + + bool parseFiles() const; + void setParseFiles(bool parseFiles); + + bool sourceControlFiles() const; + void setSourceControlFiles(bool sourceControlFiles); + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // MSBUILDFILTER_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildlink.cpp b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildlink.cpp new file mode 100644 index 00000000..cae1a63a --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildlink.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildlink.h" + +#include "../imsbuildgroup.h" + +namespace qbs { + +static const QString MSBuildLinkItemName = QStringLiteral("Link"); + +MSBuildLink::MSBuildLink(IMSBuildItemGroup *parent) + : MSBuildItem(MSBuildLinkItemName, parent) +{ +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildlink.h b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildlink.h new file mode 100644 index 00000000..caf12544 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildlink.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDLINK_H +#define MSBUILDLINK_H + +#include "../msbuilditem.h" + +namespace qbs { + +class IMSBuildItemGroup; + +class MSBuildLink : public MSBuildItem +{ + Q_OBJECT +public: + explicit MSBuildLink(IMSBuildItemGroup *parent = 0); +}; + +} // namespace qbs + +#endif // MSBUILDLINK_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildnone.cpp b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildnone.cpp new file mode 100644 index 00000000..a590c6e9 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildnone.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildnone.h" + +namespace qbs { + +MSBuildNone::MSBuildNone(IMSBuildItemGroup *parent) + : MSBuildFileItem(QStringLiteral("None"), parent) +{ +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildnone.h b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildnone.h new file mode 100644 index 00000000..98dac7a7 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/items/msbuildnone.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDNONE_H +#define MSBUILDNONE_H + +#include "msbuildfileitem.h" + +namespace qbs { + +class MSBuildNone : public MSBuildFileItem +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildNone) +public: + explicit MSBuildNone(IMSBuildItemGroup *parent = 0); +}; + +} // namespace qbs + +#endif // MSBUILDNONE_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildimport.cpp b/src/lib/corelib/generators/visualstudio/msbuild/msbuildimport.cpp new file mode 100644 index 00000000..a0c693af --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildimport.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildimport.h" + +#include "imsbuildnodevisitor.h" +#include "msbuildimportgroup.h" +#include "msbuildproject.h" + +namespace qbs { + +class MSBuildImportPrivate +{ +public: + QString project; + QString condition; +}; + +MSBuildImport::MSBuildImport(MSBuildProject *parent) + : QObject(parent) + , d(new MSBuildImportPrivate) +{ +} + +MSBuildImport::MSBuildImport(MSBuildImportGroup *parent) + : QObject(parent) + , d(new MSBuildImportPrivate) +{ +} + +MSBuildImport::~MSBuildImport() +{ +} + +QString MSBuildImport::project() const +{ + return d->project; +} + +void MSBuildImport::setProject(const QString &project) +{ + d->project = project; +} + +QString MSBuildImport::condition() const +{ + return d->condition; +} + +void MSBuildImport::setCondition(const QString &condition) +{ + d->condition = condition; +} + +void MSBuildImport::accept(IMSBuildNodeVisitor *visitor) const +{ + visitor->visitStart(this); + visitor->visitEnd(this); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildimport.h b/src/lib/corelib/generators/visualstudio/msbuild/msbuildimport.h new file mode 100644 index 00000000..ce4c5c51 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildimport.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ +#ifndef MSBUILDIMPORT_H +#define MSBUILDIMPORT_H + +#include +#include "imsbuildnode.h" + +namespace qbs { + +class MSBuildImportGroup; +class MSBuildImportPrivate; +class MSBuildProject; + +/*! + * \brief The MSBuildImport class represents an MSBuild Import element. + * + * https://msdn.microsoft.com/en-us/library/92x05xfs.aspx + */ +class MSBuildImport : public QObject, public IMSBuildNode +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildImport) +public: + explicit MSBuildImport(MSBuildProject *parent = 0); + explicit MSBuildImport(MSBuildImportGroup *parent = 0); + virtual ~MSBuildImport(); + + QString project() const; + void setProject(const QString &project); + + QString condition() const; + void setCondition(const QString &condition); + + void accept(IMSBuildNodeVisitor *visitor) const; + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // MSBUILDIMPORT_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildimportgroup.cpp b/src/lib/corelib/generators/visualstudio/msbuild/msbuildimportgroup.cpp new file mode 100644 index 00000000..56c48049 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildimportgroup.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildimportgroup.h" + +#include "imsbuildnodevisitor.h" +#include "msbuildimport.h" + +namespace qbs { + +class MSBuildImportGroupPrivate +{ +public: + QString label; +}; + +MSBuildImportGroup::MSBuildImportGroup(MSBuildProject *parent) + : IMSBuildGroup(parent) + , d(new MSBuildImportGroupPrivate) +{ +} + +MSBuildImportGroup::~MSBuildImportGroup() +{ +} + +QString MSBuildImportGroup::label() const +{ + return d->label; +} + +void MSBuildImportGroup::setLabel(const QString &label) +{ + d->label = label; +} + +void MSBuildImportGroup::accept(IMSBuildNodeVisitor *visitor) const +{ + visitor->visitStart(this); + + for (const auto &child : children()) { + if (const MSBuildImport *import = qobject_cast(child)) + import->accept(visitor); + } + + visitor->visitEnd(this); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildimportgroup.h b/src/lib/corelib/generators/visualstudio/msbuild/msbuildimportgroup.h new file mode 100644 index 00000000..c2f0b8fc --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildimportgroup.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDIMPORTGROUP_H +#define MSBUILDIMPORTGROUP_H + +#include "imsbuildgroup.h" +#include "imsbuildnode.h" + +namespace qbs { + +class MSBuildProject; +class MSBuildImportGroupPrivate; + +/*! + * \brief The MSBuildImportGroup class represents an MSBuild ImportGroup element. + * + * https://msdn.microsoft.com/en-us/library/ff606262.aspx + */ +class MSBuildImportGroup : public IMSBuildGroup, public IMSBuildNode +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildImportGroup) +public: + explicit MSBuildImportGroup(MSBuildProject *parent = 0); + virtual ~MSBuildImportGroup(); + + QString label() const; + void setLabel(const QString &label); + + void accept(IMSBuildNodeVisitor *visitor) const; + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // MSBUILDIMPORTGROUP_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuilditem.cpp b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditem.cpp new file mode 100644 index 00000000..9a2ffb73 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditem.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuilditem.h" + +#include "imsbuildnodevisitor.h" +#include "msbuilditemdefinitiongroup.h" +#include "msbuilditemgroup.h" +#include "msbuilditemmetadata.h" + +namespace qbs { + +class MSBuildItemPrivate +{ +public: + QString name = QStringLiteral("Item"); + QString include; +}; + +MSBuildItem::MSBuildItem(const QString &name, IMSBuildItemGroup *parent) + : QObject(parent) + , d(new MSBuildItemPrivate) +{ + setName(name); +} + +MSBuildItem::~MSBuildItem() +{ +} + +QString MSBuildItem::name() const +{ + return d->name; +} + +void MSBuildItem::setName(const QString &name) +{ + d->name = name; +} + +QString MSBuildItem::include() const +{ + return d->include; +} + +void MSBuildItem::setInclude(const QString &include) +{ + d->include = include; +} + +void MSBuildItem::appendProperty(const QString &name, const QVariant &value) +{ + new MSBuildItemMetadata(name, value, this); +} + +void MSBuildItem::accept(IMSBuildNodeVisitor *visitor) const +{ + visitor->visitStart(this); + + for (const auto &child : children()) { + if (MSBuildItemMetadata *itemMetadata = qobject_cast(child)) + itemMetadata->accept(visitor); + } + + visitor->visitEnd(this); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuilditem.h b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditem.h new file mode 100644 index 00000000..1314c15a --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditem.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDITEM_H +#define MSBUILDITEM_H + +#include +#include "imsbuildnode.h" + +namespace qbs { + +class IMSBuildItemGroup; +class MSBuildItemDefinitionGroup; +class MSBuildItemGroup; +class MSBuildItemPrivate; + +/*! + * \brief The MSBuildItem class represents an MSBuild Item element. + * + * https://msdn.microsoft.com/en-us/library/ms164283.aspx + */ +class MSBuildItem : public QObject, public IMSBuildNode +{ + Q_OBJECT +public: + explicit MSBuildItem(const QString &name, IMSBuildItemGroup *parent = 0); + virtual ~MSBuildItem(); + + QString name() const; + void setName(const QString &name); + + QString include() const; + void setInclude(const QString &include); + + void appendProperty(const QString &name, const QVariant &value); + + void accept(IMSBuildNodeVisitor *visitor) const; + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // MSBUILDITEM_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemdefinitiongroup.cpp b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemdefinitiongroup.cpp new file mode 100644 index 00000000..5228e850 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemdefinitiongroup.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuilditemdefinitiongroup.h" + +#include "imsbuildnodevisitor.h" +#include "msbuilditem.h" + +namespace qbs { + +MSBuildItemDefinitionGroup::MSBuildItemDefinitionGroup(MSBuildProject *parent) + : IMSBuildItemGroup(parent) +{ +} + +MSBuildItemDefinitionGroup::~MSBuildItemDefinitionGroup() +{ +} + +void MSBuildItemDefinitionGroup::accept(IMSBuildNodeVisitor *visitor) const +{ + visitor->visitStart(this); + + for (const auto &child : children()) { + if (const auto item = qobject_cast(child)) + item->accept(visitor); + } + + visitor->visitEnd(this); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemdefinitiongroup.h b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemdefinitiongroup.h new file mode 100644 index 00000000..a96f0fff --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemdefinitiongroup.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDITEMDEFINITIONGROUP_H +#define MSBUILDITEMDEFINITIONGROUP_H + +#include "imsbuildgroup.h" +#include "imsbuildnode.h" + +namespace qbs { + +class MSBuildProject; +class MSBuildItemDefinitionGroupPrivate; + +/*! + * \brief The MSBuildItemDefinitionGroup class represents an MSBuild ItemDefinitionGroup element. + * + * https://msdn.microsoft.com/en-us/library/bb629392.aspx + */ +class MSBuildItemDefinitionGroup : public IMSBuildItemGroup, public IMSBuildNode +{ + Q_OBJECT +public: + explicit MSBuildItemDefinitionGroup(MSBuildProject *parent = 0); + ~MSBuildItemDefinitionGroup(); + + void accept(IMSBuildNodeVisitor *visitor) const; +}; + +} // namespace qbs + +#endif // MSBUILDITEMDEFINITIONGROUP_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemgroup.cpp b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemgroup.cpp new file mode 100644 index 00000000..7a9bd122 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemgroup.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuilditemgroup.h" + +#include "imsbuildnodevisitor.h" +#include "msbuilditem.h" + +namespace qbs { + +class MSBuildItemGroupPrivate +{ +public: + QString label; +}; + +MSBuildItemGroup::MSBuildItemGroup(MSBuildProject *parent) + : IMSBuildItemGroup(parent) + , d(new MSBuildItemGroupPrivate) +{ +} + +MSBuildItemGroup::~MSBuildItemGroup() +{ +} + +QString MSBuildItemGroup::label() const +{ + return d->label; +} + +void MSBuildItemGroup::setLabel(const QString &label) +{ + d->label = label; +} + +void MSBuildItemGroup::accept(IMSBuildNodeVisitor *visitor) const +{ + visitor->visitStart(this); + + for (const auto &child : children()) { + if (const MSBuildItem *item = qobject_cast(child)) + item->accept(visitor); + } + + visitor->visitEnd(this); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemgroup.h b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemgroup.h new file mode 100644 index 00000000..02ac6b08 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemgroup.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDITEMGROUP_H +#define MSBUILDITEMGROUP_H + +#include "imsbuildgroup.h" +#include "imsbuildnode.h" + +namespace qbs { + +class MSBuildProject; +class MSBuildItemGroupPrivate; + +/*! + * \brief The MSBuildItemGroup class represents an MSBuild ItemGroup element. + * + * https://msdn.microsoft.com/en-us/library/646dk05y.aspx + */ +class MSBuildItemGroup : public IMSBuildItemGroup, public IMSBuildNode +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildItemGroup) +public: + explicit MSBuildItemGroup(MSBuildProject *parent = 0); + ~MSBuildItemGroup(); + + QString label() const; + void setLabel(const QString &label); + + void accept(IMSBuildNodeVisitor *visitor) const; + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // MSBUILDITEMGROUP_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemmetadata.cpp b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemmetadata.cpp new file mode 100644 index 00000000..daaa4c6b --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemmetadata.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuilditemmetadata.h" + +#include "imsbuildnodevisitor.h" +#include "msbuilditem.h" + +namespace qbs { + +MSBuildItemMetadata::MSBuildItemMetadata(MSBuildItem *parent) + : IMSBuildProperty(parent) +{ +} + +MSBuildItemMetadata::MSBuildItemMetadata(const QString &name, const QVariant &value, + MSBuildItem *parent) + : MSBuildItemMetadata(parent) +{ + setName(name); + setValue(value); +} + +void MSBuildItemMetadata::accept(IMSBuildNodeVisitor *visitor) const +{ + visitor->visitStart(this); + visitor->visitEnd(this); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemmetadata.h b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemmetadata.h new file mode 100644 index 00000000..7a42f173 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuilditemmetadata.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDITEMMETADATA_H +#define MSBUILDITEMMETADATA_H + +#include "imsbuildproperty.h" +#include "imsbuildnode.h" + +namespace qbs { + +class MSBuildItem; + +/*! + * \brief The MSBuildItemMetadata class represents an MSBuild ItemMetadata element. + * + * https://msdn.microsoft.com/en-us/library/ms164284.aspx + */ +class MSBuildItemMetadata : public IMSBuildProperty, public IMSBuildNode +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildItemMetadata) +public: + explicit MSBuildItemMetadata(MSBuildItem *parent = 0); + MSBuildItemMetadata(const QString &name, const QVariant &value = QVariant(), + MSBuildItem *parent = 0); + + void accept(IMSBuildNodeVisitor *visitor) const; +}; + +} // namespace qbs + +#endif // MSBUILDITEMMETADATA_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildproject.cpp b/src/lib/corelib/generators/visualstudio/msbuild/msbuildproject.cpp new file mode 100644 index 00000000..c872622b --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildproject.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildproject.h" + +#include "imsbuildnodevisitor.h" +#include "msbuildimport.h" +#include "msbuildimportgroup.h" +#include "msbuilditemdefinitiongroup.h" +#include "msbuilditemgroup.h" +#include "msbuildpropertygroup.h" + +namespace qbs { + +class MSBuildProjectPrivate +{ +public: + QString defaultTargets; + QString toolsVersion; +}; + +MSBuildProject::MSBuildProject(QObject *parent) + : QObject(parent) + , d(new MSBuildProjectPrivate) +{ +} + +MSBuildProject::~MSBuildProject() +{ +} + +QString MSBuildProject::defaultTargets() const +{ + return d->defaultTargets; +} + +void MSBuildProject::setDefaultTargets(const QString &defaultTargets) +{ + d->defaultTargets = defaultTargets; +} + +QString MSBuildProject::toolsVersion() const +{ + return d->toolsVersion; +} + +void MSBuildProject::setToolsVersion(const QString &toolsVersion) +{ + d->toolsVersion = toolsVersion; +} + +void MSBuildProject::accept(IMSBuildNodeVisitor *visitor) const +{ + visitor->visitStart(this); + + for (const auto &child : children()) { + if (const auto node = qobject_cast(child)) + node->accept(visitor); + else if (const auto node = qobject_cast(child)) + node->accept(visitor); + else if (const auto node = qobject_cast(child)) + node->accept(visitor); + else if (const auto node = qobject_cast(child)) + node->accept(visitor); + else if (const auto node = qobject_cast(child)) + node->accept(visitor); + } + + visitor->visitEnd(this); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildproject.h b/src/lib/corelib/generators/visualstudio/msbuild/msbuildproject.h new file mode 100644 index 00000000..23b5a682 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildproject.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDPROJECT_H +#define MSBUILDPROJECT_H + +#include +#include "imsbuildnode.h" + +namespace qbs { + +class MSBuildProjectPrivate; + +/*! + * \brief The MSBuildProject class represents an MSBuild Project element. + * + * https://msdn.microsoft.com/en-us/library/bcxfsh87.aspx + */ +class MSBuildProject : public QObject, public IMSBuildNode +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildProject) +public: + explicit MSBuildProject(QObject *parent = 0); + ~MSBuildProject(); + + QString defaultTargets() const; + void setDefaultTargets(const QString &defaultTargets); + + QString toolsVersion() const; + void setToolsVersion(const QString &toolsVersion); + + void accept(IMSBuildNodeVisitor *visitor) const; + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // MSBUILDPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildproperty.cpp b/src/lib/corelib/generators/visualstudio/msbuild/msbuildproperty.cpp new file mode 100644 index 00000000..410e2a69 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildproperty.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildproperty.h" + +#include "imsbuildnodevisitor.h" +#include "msbuildpropertygroup.h" + +namespace qbs { + +MSBuildProperty::MSBuildProperty(MSBuildPropertyGroup *parent) + : IMSBuildProperty(parent) +{ +} + +MSBuildProperty::MSBuildProperty(const QString &name, const QVariant &value, + MSBuildPropertyGroup *parent) + : MSBuildProperty(parent) +{ + setName(name); + setValue(value); +} + +void MSBuildProperty::accept(IMSBuildNodeVisitor *visitor) const +{ + visitor->visitStart(this); + visitor->visitEnd(this); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildproperty.h b/src/lib/corelib/generators/visualstudio/msbuild/msbuildproperty.h new file mode 100644 index 00000000..310fcc04 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildproperty.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDPROPERTY_H +#define MSBUILDPROPERTY_H + +#include "imsbuildproperty.h" +#include "imsbuildnode.h" + +namespace qbs { + +class MSBuildPropertyGroup; + +/*! + * \brief The MSBuildProperty class represents an MSBuild Property element. + * + * https://msdn.microsoft.com/en-us/library/ms164288.aspx + */ +class MSBuildProperty : public IMSBuildProperty, public IMSBuildNode +{ + Q_OBJECT +public: + explicit MSBuildProperty(MSBuildPropertyGroup *parent = 0); + MSBuildProperty(const QString &name, const QVariant &value = QVariant(), + MSBuildPropertyGroup *parent = 0); + + void accept(IMSBuildNodeVisitor *visitor) const; +}; + +} // namespace qbs + +#endif // MSBUILDPROPERTY_H diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildpropertygroup.cpp b/src/lib/corelib/generators/visualstudio/msbuild/msbuildpropertygroup.cpp new file mode 100644 index 00000000..4f9c7293 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildpropertygroup.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildpropertygroup.h" + +#include "imsbuildnodevisitor.h" +#include "msbuildproperty.h" + +namespace qbs { + +class MSBuildPropertyGroupPrivate +{ +public: + QString condition; + QString label; +}; + +MSBuildPropertyGroup::MSBuildPropertyGroup(MSBuildProject *parent) + : IMSBuildGroup(parent) + , d(new MSBuildPropertyGroupPrivate) +{ +} + +MSBuildPropertyGroup::~MSBuildPropertyGroup() +{ +} + +QString MSBuildPropertyGroup::label() const +{ + return d->label; +} + +void MSBuildPropertyGroup::setLabel(const QString &label) +{ + d->label = label; +} + +void MSBuildPropertyGroup::appendProperty(const QString &name, const QVariant &value) +{ + new MSBuildProperty(name, value, this); +} + +void MSBuildPropertyGroup::accept(IMSBuildNodeVisitor *visitor) const +{ + visitor->visitStart(this); + + for (const auto &child : children()) { + if (const MSBuildProperty *property = qobject_cast(child)) + property->accept(visitor); + } + + visitor->visitEnd(this); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuild/msbuildpropertygroup.h b/src/lib/corelib/generators/visualstudio/msbuild/msbuildpropertygroup.h new file mode 100644 index 00000000..33fa07b3 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuild/msbuildpropertygroup.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDPROPERTYGROUP_H +#define MSBUILDPROPERTYGROUP_H + +#include "imsbuildgroup.h" +#include "imsbuildnode.h" + +namespace qbs { + +class MSBuildProject; +class MSBuildPropertyGroupPrivate; + +/*! + * \brief The MSBuildPropertyGroup class represents an MSBuild PropertyGroup element. + * + * https://msdn.microsoft.com/en-us/library/t4w159bs.aspx + */ +class MSBuildPropertyGroup : public IMSBuildGroup, public IMSBuildNode +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildPropertyGroup) +public: + explicit MSBuildPropertyGroup(MSBuildProject *parent = 0); + ~MSBuildPropertyGroup(); + + QString label() const; + void setLabel(const QString &label); + + void appendProperty(const QString &name, const QVariant &value); + + void accept(IMSBuildNodeVisitor *visitor) const; + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // MSBUILDPROPERTYGROUP_H diff --git a/src/lib/corelib/generators/visualstudio/msbuildfiltersproject.cpp b/src/lib/corelib/generators/visualstudio/msbuildfiltersproject.cpp new file mode 100644 index 00000000..1eceb004 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildfiltersproject.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildfiltersproject.h" + +#include "msbuild/msbuilditemgroup.h" + +#include "msbuild/items/msbuildclcompile.h" +#include "msbuild/items/msbuildclinclude.h" +#include "msbuild/items/msbuildfilter.h" +#include "msbuild/items/msbuildnone.h" + +#include +#include + +namespace qbs { + +namespace { + +static QStringList sourceFileExtensions() +{ + return QStringList() + << QStringLiteral("c") + << QStringLiteral("C") + << QStringLiteral("cpp") + << QStringLiteral("cxx") + << QStringLiteral("c++") + << QStringLiteral("cc") + << QStringLiteral("cs") + << QStringLiteral("def") + << QStringLiteral("java") + << QStringLiteral("m") + << QStringLiteral("mm"); +} + +static QStringList headerFileExtensions() +{ + return QStringList() + << QStringLiteral("h") + << QStringLiteral("H") + << QStringLiteral("hpp") + << QStringLiteral("hxx") + << QStringLiteral("h++"); +} + +static QVector defaultItemGroupFilters(IMSBuildItemGroup *parent = 0) +{ + const auto filters = QVector { + new MSBuildFilter(QStringLiteral("Source Files"), sourceFileExtensions(), parent), + new MSBuildFilter(QStringLiteral("Header Files"), headerFileExtensions(), parent) + }; + + const auto formFilter = new MSBuildFilter(QStringLiteral("Form Files"), + QStringList() << QStringLiteral("ui"), parent); + const auto resourceFilter = new MSBuildFilter(QStringLiteral("Resource Files"), + QStringList() + << QStringLiteral("qrc") + << QStringLiteral("rc") + << QStringLiteral("*"), parent); + resourceFilter->setParseFiles(false); + const auto generatedFilter = new MSBuildFilter(QStringLiteral("Generated Files"), + QStringList() << QStringLiteral("moc"), parent); + generatedFilter->setSourceControlFiles(false); + const auto translationFilter = new MSBuildFilter(QStringLiteral("Translation Files"), + QStringList() << QStringLiteral("ts"), parent); + translationFilter->setParseFiles(false); + + return QVector() + << filters + << formFilter + << resourceFilter + << generatedFilter + << translationFilter; +} + +static bool matchesFilter(const MSBuildFilter *filter, const QString &filePath) +{ + return filter->extensions().contains(QFileInfo(filePath).completeSuffix()); +} + +} + +MSBuildFiltersProject::MSBuildFiltersProject(const GeneratableProductData &product, + QObject *parent) + : MSBuildProject(parent) +{ + // Normally this would be versionInfo.toolsVersion() but for some reason it seems + // filters projects are always v4.0 + setToolsVersion(QStringLiteral("4.0")); + + auto itemGroup = new MSBuildItemGroup(this); + const auto filterOptions = defaultItemGroupFilters(); + for (const auto options : filterOptions) { + auto filter = new MSBuildFilter(options->include(), options->extensions(), itemGroup); + filter->appendProperty(QStringLiteral("ParseFiles"), options->parseFiles()); + filter->appendProperty(QStringLiteral("SourceControlFiles"), options->sourceControlFiles()); + } + + QSet allFiles; + for (const auto &productData : product.data.values()) { + for (const auto &groupData : productData.groups()) + if (groupData.isEnabled()) + allFiles.unite(groupData.allFilePaths().toSet()); + } + + auto allFilesSorted = allFiles.toList(); + std::sort(allFilesSorted.begin(), allFilesSorted.end()); + + MSBuildItemGroup *headerFilesGroup = nullptr; + MSBuildItemGroup *sourceFilesGroup = nullptr; + MSBuildItemGroup *filesGroup = nullptr; + + for (const auto &filePath : allFilesSorted) { + MSBuildFileItem *fileItem = nullptr; + + for (const MSBuildFilter *options : filterOptions) { + if (matchesFilter(options, filePath)) { + if (options->include() == QStringLiteral("Header Files")) { + if (!headerFilesGroup) + headerFilesGroup = new MSBuildItemGroup(this); + fileItem = new MSBuildClInclude(headerFilesGroup); + } else if (options->include() == QStringLiteral("Source Files")) { + if (!sourceFilesGroup) + sourceFilesGroup = new MSBuildItemGroup(this); + fileItem = new MSBuildClCompile(sourceFilesGroup); + } + + if (fileItem) { + fileItem->setFilterName(options->include()); + break; + } + } + } + + if (!fileItem) { + if (!filesGroup) + filesGroup = new MSBuildItemGroup(this); + fileItem = new MSBuildNone(filesGroup); + } + fileItem->setFilePath(filePath); + } + + qDeleteAll(filterOptions); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuildfiltersproject.h b/src/lib/corelib/generators/visualstudio/msbuildfiltersproject.h new file mode 100644 index 00000000..521a6e61 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildfiltersproject.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDFILTERSPROJECT_H +#define MSBUILDFILTERSPROJECT_H + +#include "msbuild/msbuildproject.h" + +#include +#include + +namespace qbs { + +class MSBuildFilter; + +class MSBuildFiltersProject : public MSBuildProject +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildFiltersProject) +public: + explicit MSBuildFiltersProject(const GeneratableProductData &product, + QObject *parent = 0); +}; + +} // namespace qbs + +#endif // MSBUILDFILTERSPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/msbuildqbsgenerateproject.cpp b/src/lib/corelib/generators/visualstudio/msbuildqbsgenerateproject.cpp new file mode 100644 index 00000000..bdbb0fe7 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildqbsgenerateproject.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildqbsgenerateproject.h" + +#include "msbuild/msbuildimport.h" +#include "msbuild/msbuildproperty.h" +#include "msbuild/msbuildpropertygroup.h" + +#include +#include +#include + +namespace qbs { + +MSBuildQbsGenerateProject::MSBuildQbsGenerateProject( + const GeneratableProject &project, + const Internal::VisualStudioVersionInfo &versionInfo, + VisualStudioGenerator *parent) + : MSBuildTargetProject(project, versionInfo, parent) +{ + auto cppDefaultProps = new MSBuildImport(this); + cppDefaultProps->setProject(QStringLiteral("$(VCTargetsPath)\\Microsoft.Cpp.Default.props")); + + auto group = new MSBuildPropertyGroup(this); + group->setLabel(QStringLiteral("Configuration")); + group->appendProperty(QStringLiteral("PlatformToolset"), + versionInfo.platformToolsetVersion()); + group->appendProperty(QStringLiteral("ConfigurationType"), + QStringLiteral("Makefile")); + const auto params = Internal::shellQuote(project.commandLine(), + Internal::HostOsInfo::HostOsWindows); + group->appendProperty(QStringLiteral("NMakeBuildCommandLine"), + QStringLiteral("$(QbsGenerateCommandLine) ") + params); + + auto cppProps = new MSBuildImport(this); + cppProps->setProject(QStringLiteral("$(VCTargetsPath)\\Microsoft.Cpp.props")); + + auto import = new MSBuildImport(this); + import->setProject(QStringLiteral("$(VCTargetsPath)\\Microsoft.Cpp.targets")); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuildqbsgenerateproject.h b/src/lib/corelib/generators/visualstudio/msbuildqbsgenerateproject.h new file mode 100644 index 00000000..433bd31a --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildqbsgenerateproject.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDQBSGENERATEPROJECT_H +#define MSBUILDQBSGENERATEPROJECT_H + +#include "msbuildtargetproject.h" + +#include +#include + +namespace qbs { + +class MSBuildQbsGenerateProject : public MSBuildTargetProject +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildQbsGenerateProject) +public: + MSBuildQbsGenerateProject(const GeneratableProject &project, + const Internal::VisualStudioVersionInfo &versionInfo, + VisualStudioGenerator *parent = 0); +}; + +} // namespace qbs + +#endif // MSBUILDQBSGENERATEPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/msbuildqbsproductproject.cpp b/src/lib/corelib/generators/visualstudio/msbuildqbsproductproject.cpp new file mode 100644 index 00000000..81d0be8b --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildqbsproductproject.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildqbsproductproject.h" + +#include "msbuild/msbuildimport.h" +#include "msbuild/msbuildimportgroup.h" +#include "msbuild/msbuilditemdefinitiongroup.h" +#include "msbuild/msbuilditemgroup.h" +#include "msbuild/msbuilditemmetadata.h" +#include "msbuild/msbuildproperty.h" +#include "msbuild/msbuildpropertygroup.h" + +#include "msbuild/items/msbuildclcompile.h" +#include "msbuild/items/msbuildclinclude.h" +#include "msbuild/items/msbuildlink.h" +#include "msbuild/items/msbuildnone.h" + +#include "msbuildutils.h" +#include "visualstudiogenerator.h" + +#include +#include +#include + +#include +#include + +namespace qbs { + +MSBuildQbsProductProject::MSBuildQbsProductProject( + const GeneratableProject &project, + const GeneratableProductData &product, + const Internal::VisualStudioVersionInfo &versionInfo, + VisualStudioGenerator *parent) + : MSBuildTargetProject(project, versionInfo, parent) +{ + Q_ASSERT(project.projects.size() == project.commandLines.size()); + Q_ASSERT(project.projects.size() == product.data.size()); + + const int count = std::max(project.projects.size(), product.data.size()); + + globalsPropertyGroup()->appendProperty(QStringLiteral("QbsProductName"), product.name()); + + MSBuildImport *cppDefaultProps = new MSBuildImport(this); + cppDefaultProps->setProject(QStringLiteral("$(VCTargetsPath)\\Microsoft.Cpp.Default.props")); + + for (int i = 0; i < count; ++i) { + addConfiguration( + project, + project.projects.values().at(i), + product.data.values().at(i), + project.commandLines.values().at(i)); + } + + MSBuildImport *cppProps = new MSBuildImport(this); + cppProps->setProject(QStringLiteral("$(VCTargetsPath)\\Microsoft.Cpp.props")); + + for (int i = 0; i < count; ++i) + addItemDefGroup(project.projects.values().at(i), product.data.values().at(i)); + + addFiles(project, product); +} + +static QString productTargetPath(const qbs::ProductData &productData) +{ + const QString fullPath = productData.targetExecutable(); + if (!fullPath.isEmpty()) + return QFileInfo(fullPath).absolutePath(); + return productData.properties().value(QStringLiteral("buildDirectory")).toString(); +} + +void MSBuildQbsProductProject::addConfiguration(const GeneratableProject &project, + const Project &buildTask, + const ProductData &productData, + const QStringList &buildConfigurationCommandLine) +{ + const auto targetDir = Internal::PathUtils::toNativeSeparators( + productTargetPath(productData), Internal::HostOsInfo::HostOsWindows); + + auto configurationDir = Internal::PathUtils::toNativeSeparators( + project.baseBuildDirectory().absolutePath() + + QLatin1Char('\\') + + MSBuildUtils::configurationName(buildTask), + Internal::HostOsInfo::HostOsWindows); + auto relativeTargetDir = targetDir; + if (targetDir.startsWith(configurationDir)) + relativeTargetDir = QStringLiteral("$(SolutionDir)$(Configuration)") + + relativeTargetDir.mid(configurationDir.size()); + + const auto properties = productData.moduleProperties(); + + const bool debugBuild = properties.getModuleProperty(QStringLiteral("qbs"), + QStringLiteral("debugInformation")) + .toBool(); + + const auto includePaths = QStringList() + << properties.getModulePropertiesAsStringList(QStringLiteral("cpp"), + QStringLiteral("includePaths")) + << properties.getModulePropertiesAsStringList(QStringLiteral("cpp"), + QStringLiteral("systemIncludePaths")); + const auto cppDefines = properties + .getModulePropertiesAsStringList(QStringLiteral("cpp"), QStringLiteral("defines")); + + const auto sep = Internal::HostOsInfo::pathListSeparator(Internal::HostOsInfo::HostOsWindows); + + auto propertyGroup1 = new MSBuildPropertyGroup(this); + propertyGroup1->setCondition(MSBuildUtils::buildTaskCondition(buildTask)); + propertyGroup1->setLabel(QStringLiteral("Configuration")); + propertyGroup1->appendProperty(QStringLiteral("UseDebugLibraries"), + debugBuild ? QStringLiteral("true") : QStringLiteral("false")); + + // General - General + propertyGroup1->appendProperty(QStringLiteral("OutDir"), relativeTargetDir); + propertyGroup1->appendProperty(QStringLiteral("TargetName"), productData.targetName()); + propertyGroup1->appendProperty(QStringLiteral("PlatformToolset"), + versionInfo().platformToolsetVersion()); + propertyGroup1->appendProperty(QStringLiteral("ConfigurationType"), QStringLiteral("Makefile")); + + // VS possible values: Unicode|MultiByte|NotSet + propertyGroup1->appendProperty(QStringLiteral("CharacterSet"), + properties.getModuleProperty(QStringLiteral("cpp"), + QStringLiteral("windowsApiCharacterSet")) == QStringLiteral("unicode") + ? QStringLiteral("MultiByte") : QStringLiteral("NotSet")); + + // Debugging + propertyGroup1->appendProperty(QStringLiteral("DebuggerFlavor"), + QStringLiteral("WindowsLocalDebugger")); + propertyGroup1->appendProperty(QStringLiteral("LocalDebuggerCommand"), + QStringLiteral("$(OutDir)$(TargetName)$(TargetExt)")); + propertyGroup1->appendProperty(QStringLiteral("LocalDebuggerWorkingDirectory"), + QStringLiteral("$(OutDir)")); + + // NMake - General + // Skip configuration name, that's handled in qbs-shared.props + const auto params = Internal::shellQuote(buildConfigurationCommandLine.mid(1), + Internal::HostOsInfo::HostOsWindows); + propertyGroup1->appendProperty(QStringLiteral("NMakeBuildCommandLine"), + QStringLiteral("$(QbsBuildCommandLine) ") + params); + propertyGroup1->appendProperty(QStringLiteral("NMakeReBuildCommandLine"), + QStringLiteral("$(QbsReBuildCommandLine) ") + params); + propertyGroup1->appendProperty(QStringLiteral("NMakeCleanCommandLine"), + QStringLiteral("$(QbsCleanCommandLine) ") + params); + propertyGroup1->appendProperty(QStringLiteral("NMakeOutput"), + QStringLiteral("$(OutDir)$(TargetName)$(TargetExt)")); + + // NMake - IntelliSense + propertyGroup1->appendProperty(QStringLiteral("NMakePreprocessorDefinitions"), + cppDefines.join(sep)); + propertyGroup1->appendProperty(QStringLiteral("NMakeIncludeSearchPath"), + includePaths.join(sep)); +} + +static QString subsystemVersion(const QString &version) +{ + const auto v = Internal::Version::fromString(version); + return QStringLiteral("%1.%2").arg( + QString::number(v.majorVersion()), + QString::number(v.minorVersion()).rightJustified(2, QLatin1Char('0'))); +} + +void MSBuildQbsProductProject::addItemDefGroup(const Project &project, + const ProductData &productData) +{ + const auto properties = productData.moduleProperties(); + + const bool consoleApp = productData.properties().value(QStringLiteral("consoleApplication")) + .toBool(); + const bool debugBuild = properties.getModuleProperty(QStringLiteral("qbs"), + QStringLiteral("debugInformation")) + .toBool(); + const auto optimizationLevel = properties.getModuleProperty(QStringLiteral("qbs"), + QStringLiteral("optimization")) + .toString(); + const auto warningLevel = properties.getModuleProperty(QStringLiteral("qbs"), + QStringLiteral("warningLevel")) + .toString(); + + const auto includePaths = QStringList() + << properties.getModulePropertiesAsStringList(QStringLiteral("cpp"), + QStringLiteral("includePaths")) + << properties.getModulePropertiesAsStringList(QStringLiteral("cpp"), + QStringLiteral("systemIncludePaths")); + const auto cppDefines = properties.getModulePropertiesAsStringList( + QStringLiteral("cpp"), QStringLiteral("defines")); + + const auto sep = Internal::HostOsInfo::pathListSeparator(Internal::HostOsInfo::HostOsWindows); + + auto itemDefGroup = new MSBuildItemDefinitionGroup(this); + itemDefGroup->setCondition(MSBuildUtils::buildTaskCondition(project)); + + auto compile = new MSBuildClCompile(itemDefGroup); + + // C++ - General + compile->appendProperty(QStringLiteral("AdditionalIncludeDirectories"), + includePaths.join(sep) + + sep + + QStringLiteral("%(AdditionalIncludeDirectories)")); + if (warningLevel == QStringLiteral("none")) + compile->appendProperty(QStringLiteral("WarningLevel"), + QStringLiteral("TurnOffAllWarnings")); + else if (warningLevel == QStringLiteral("all")) + compile->appendProperty(QStringLiteral("WarningLevel"), + QStringLiteral("EnableAllWarnings")); + else + compile->appendProperty(QStringLiteral("WarningLevel"), + QStringLiteral("Level3")); // this is VS default. + + // C++ - Optimization + compile->appendProperty(QStringLiteral("Optimization"), + optimizationLevel == QStringLiteral("none") + ? QStringLiteral("Disabled") + : QStringLiteral("MaxSpeed")); + + // C++ - Preprocessor + compile->appendProperty(QStringLiteral("PreprocessorDefinitions"), + cppDefines.join(sep) + + sep + + QStringLiteral("%(PreprocessorDefinitions)")); + + // C++ - Code Generation + compile->appendProperty(QStringLiteral("RuntimeLibrary"), debugBuild + ? QStringLiteral("MultiThreadedDebugDLL") + : QStringLiteral("MultiThreadedDLL")); + + auto link = new MSBuildLink(itemDefGroup); + + // Linker - General + link->appendProperty(QStringLiteral("AdditionalLibraryDirectories"), + properties.getModulePropertiesAsStringList(QStringLiteral("cpp"), + QStringLiteral("libraryPaths")).join(sep)); + + // Linker - Input + link->appendProperty(QStringLiteral("AdditionalDependencies"), + properties.getModulePropertiesAsStringList(QStringLiteral("cpp"), + QStringLiteral("staticLibraries")).join(sep) + + sep + QStringLiteral("%(AdditionalDependencies)")); + + // Linker - Debugging + link->appendProperty(QStringLiteral("GenerateDebugInformation"), + debugBuild ? QStringLiteral("true") : QStringLiteral("false")); + + // Linker - System + link->appendProperty(QStringLiteral("SubSystem"), + consoleApp ? QStringLiteral("Console") : QStringLiteral("Windows")); + const auto subsysVersion = properties.getModuleProperty( + QStringLiteral("cpp"), QStringLiteral("minimumWindowsVersion")).toString(); + if (!subsysVersion.isEmpty()) + link->appendProperty(QStringLiteral("MinimumRequiredVersion"), + subsystemVersion(subsysVersion)); + + // Linker - Optimization + link->appendProperty(QStringLiteral("OptimizeReferences"), + debugBuild ? QStringLiteral("false") : QStringLiteral("true")); +} + +// No QSet::intersects until Qt 5.6 +template bool setIntersects(const QSet &this_, const QSet &other) +{ + QSet s = this_; + return !s.intersect(other).isEmpty(); +} + +static MSBuildFileItem *fileItemForFileTags(const QList &fileTags, + IMSBuildItemGroup *parent = 0) +{ + const auto fileTagsSet = fileTags.toSet(); + if (setIntersects(fileTagsSet, QSet() << QStringLiteral("hpp"))) + return new MSBuildClInclude(parent); + if (setIntersects(fileTagsSet, QSet() << QStringLiteral("c") << QStringLiteral("cpp"))) + return new MSBuildClCompile(parent); + return new MSBuildNone(parent); +} + +void MSBuildQbsProductProject::addFiles(const GeneratableProject &project, + const GeneratableProductData &product) +{ + auto itemGroup = new MSBuildItemGroup(this); + + std::map sourceFileNodes; + std::map sourceFileEnabledConfigurations; + + // Create a ClCompile item for each source file, keeping track of which configurations that + // file's containing group is enabled in + QMapIterator productDataIt(product.data); + while (productDataIt.hasNext()) { + productDataIt.next(); + for (const auto &group : productDataIt.value().groups()) { + for (const auto &sourceArtifact : group.allSourceArtifacts()) { + const auto filePath = sourceArtifact.filePath(); + if (sourceFileNodes.find(filePath) == sourceFileNodes.end()) { + sourceFileNodes.insert({ + filePath, + fileItemForFileTags(sourceArtifact.fileTags(), itemGroup) + }); + } + auto fileItem = sourceFileNodes[filePath]; + fileItem->setFilePath(QStringLiteral("$(ProjectDir)") + + project.baseBuildDirectory().relativeFilePath(filePath)); + if (group.isEnabled()) + sourceFileEnabledConfigurations[filePath] << productDataIt.key(); + } + } + } + + // Add ExcludedFromBuild item metadata to each file for each configuration + // where that file's containing group is disabled + for (const auto &sourceFileNode : sourceFileNodes) { + QMapIterator projIt(project.projects); + while (projIt.hasNext()) { + projIt.next(); + if (!sourceFileEnabledConfigurations[sourceFileNode.first].contains(projIt.key())) { + auto metadata = new MSBuildItemMetadata( + QStringLiteral("ExcludedFromBuild"), + QStringLiteral("true"), + sourceFileNode.second); + metadata->setCondition(QStringLiteral("'$(Configuration)|$(Platform)'=='") + + MSBuildUtils::fullName(projIt.value()) + + QStringLiteral("'")); + } + } + } + + auto import = new MSBuildImport(this); + import->setProject(QStringLiteral("$(VCTargetsPath)\\Microsoft.Cpp.targets")); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuildqbsproductproject.h b/src/lib/corelib/generators/visualstudio/msbuildqbsproductproject.h new file mode 100644 index 00000000..e93507ae --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildqbsproductproject.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDQBSPRODUCTPROJECT_H +#define MSBUILDQBSPRODUCTPROJECT_H + +#include "msbuildtargetproject.h" + +#include +#include + +namespace qbs { + +class MSBuildImportGroup; +class MSBuildProperty; + +class VisualStudioGenerator; + +class MSBuildQbsProductProject : public MSBuildTargetProject +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildQbsProductProject) +public: + MSBuildQbsProductProject(const GeneratableProject &project, + const GeneratableProductData &product, + const Internal::VisualStudioVersionInfo &versionInfo, + VisualStudioGenerator *parent = 0); + +private: + typedef QHash> ProjectConfigurations; + + void addConfiguration(const GeneratableProject &project, const Project &buildTask, + const ProductData &productData, + const QStringList &buildConfigurationCommandLine); + void addItemDefGroup(const Project &project, + const ProductData &productData); + void addFiles(const GeneratableProject &project, const GeneratableProductData &product); +}; + +} // namespace qbs + +#endif // MSBUILDQBSPRODUCTPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/msbuildsharedsolutionpropertiesproject.cpp b/src/lib/corelib/generators/visualstudio/msbuildsharedsolutionpropertiesproject.cpp new file mode 100644 index 00000000..cbf0d430 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildsharedsolutionpropertiesproject.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildsharedsolutionpropertiesproject.h" + +#include "msbuild/msbuildpropertygroup.h" + +#include +#include + +#include + +namespace qbs { + +QString qbsCommandLine(const GeneratableProject &project, + const QString &subCommand, + const Internal::VisualStudioVersionInfo &versionInfo) +{ + auto addEnvironmentVariableArgument = [&](Internal::CommandLine &cl, const QString &var) { + cl.appendRawArgument(QStringLiteral("\"$(%1)\"").arg(var)); + }; + + auto realSubCommand = subCommand; + if (subCommand == QStringLiteral("rebuild")) + realSubCommand = QStringLiteral("build"); + + // "path/to/qbs.exe" {build|clean} -f "path/to/project.qbs" -d "/build/directory/" + // -p product_name [[configuration key:value]...] + Internal::CommandLine commandLine; + commandLine.setProgram(QStringLiteral("\"$(QbsExecutablePath)\""), true); + commandLine.appendArgument(realSubCommand); + commandLine.appendArgument(QStringLiteral("-f")); + addEnvironmentVariableArgument(commandLine, QStringLiteral("QbsProjectFile")); + commandLine.appendArgument(QStringLiteral("-d")); + addEnvironmentVariableArgument(commandLine, QStringLiteral("QbsBuildDir")); + + if (subCommand == QStringLiteral("generate")) { + commandLine.appendArgument(QStringLiteral("-g")); + commandLine.appendArgument( + QString(QStringLiteral("visualstudio%1")).arg(versionInfo.marketingVersion())); + } else { + commandLine.appendArgument(QStringLiteral("-p")); + addEnvironmentVariableArgument(commandLine, QStringLiteral("QbsProductName")); + + commandLine.appendArgument(QStringLiteral("--wait-lock")); + } + + if (realSubCommand == QStringLiteral("build") && !project.installRoot.isEmpty()) { + commandLine.appendArgument(QStringLiteral("--install-root")); + addEnvironmentVariableArgument(commandLine, QStringLiteral("QbsInstallRoot")); + } + + if (realSubCommand == QStringLiteral("build") && subCommand == QStringLiteral("rebuild")) { + commandLine.appendArgument(QStringLiteral("--check-timestamps")); + commandLine.appendArgument(QStringLiteral("--force-probe-execution")); + } + + addEnvironmentVariableArgument(commandLine, QStringLiteral("Configuration")); + + return commandLine.toCommandLine(Internal::HostOsInfo::HostOsWindows); +} + +MSBuildSharedSolutionPropertiesProject::MSBuildSharedSolutionPropertiesProject( + const Internal::VisualStudioVersionInfo &versionInfo, + const GeneratableProject &project, + const QFileInfo &qbsExecutable) +{ + setDefaultTargets(QStringLiteral("Build")); + setToolsVersion(versionInfo.toolsVersion()); + + MSBuildPropertyGroup *group = new MSBuildPropertyGroup(this); + group->setLabel(QStringLiteral("UserMacros")); + + // Order's important here... a variable must be listed before one that uses it + group->appendProperty(QStringLiteral("QbsExecutablePath"), + QStringLiteral("$(QbsExecutableDir)") + qbsExecutable.fileName()); + if (!project.installRoot.isEmpty()) { + group->appendProperty(QStringLiteral("QbsInstallRoot"), + Internal::PathUtils::toNativeSeparators( + project.installRoot, + Internal::HostOsInfo::HostOsWindows)); + } + + group->appendProperty(QStringLiteral("QbsProjectFile"), + QStringLiteral("$(QbsProjectDir)") + + project.filePath().fileName()); + + // Trailing '.' is not a typo. It prevents the trailing slash from combining with the closing + // quote to form an escape sequence. Unfortunately, Visual Studio expands variables *before* + // passing them to the underlying command shell, so there's not much we can do with regard to + // doing it "properly". Setting environment variables through MSBuild and using them in place + // of actual arguments does not work either, as Visual Studio apparently expands the environment + // variables as well, before passing them to the underlying shell. + group->appendProperty(QStringLiteral("QbsBuildDir"), + QStringLiteral("$(SolutionDir).")); + + group->appendProperty(QStringLiteral("QbsBuildCommandLine"), + qbsCommandLine(project, QStringLiteral("build"), versionInfo)); + group->appendProperty(QStringLiteral("QbsReBuildCommandLine"), + qbsCommandLine(project, QStringLiteral("rebuild"), versionInfo)); + group->appendProperty(QStringLiteral("QbsCleanCommandLine"), + qbsCommandLine(project, QStringLiteral("clean"), versionInfo)); + group->appendProperty(QStringLiteral("QbsGenerateCommandLine"), + qbsCommandLine(project, QStringLiteral("generate"), versionInfo)); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuildsharedsolutionpropertiesproject.h b/src/lib/corelib/generators/visualstudio/msbuildsharedsolutionpropertiesproject.h new file mode 100644 index 00000000..5b8b6275 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildsharedsolutionpropertiesproject.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDSHAREDSOLUTIONPROPERTIESPROJECT_H +#define MSBUILDSHAREDSOLUTIONPROPERTIESPROJECT_H + +#include "msbuild/msbuildproject.h" + +#include +#include + +namespace qbs { + +class MSBuildSharedSolutionPropertiesProject : public MSBuildProject +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildSharedSolutionPropertiesProject) +public: + MSBuildSharedSolutionPropertiesProject(const Internal::VisualStudioVersionInfo &versionInfo, + const GeneratableProject &project, + const QFileInfo &qbsExecutable); +}; + +} // namespace qbs + +#endif // MSBUILDSHAREDSOLUTIONPROPERTIESPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/msbuildsolutionpropertiesproject.cpp b/src/lib/corelib/generators/visualstudio/msbuildsolutionpropertiesproject.cpp new file mode 100644 index 00000000..5cc1eb63 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildsolutionpropertiesproject.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildsolutionpropertiesproject.h" + +#include "msbuild/msbuildpropertygroup.h" + +#include + +#include + +namespace qbs { + +MSBuildSolutionPropertiesProject::MSBuildSolutionPropertiesProject( + const Internal::VisualStudioVersionInfo &versionInfo, + const GeneratableProject &project, + const QFileInfo &qbsExecutable) +{ + setDefaultTargets(QStringLiteral("Build")); + setToolsVersion(versionInfo.toolsVersion()); + + auto group = new MSBuildPropertyGroup(this); + group->setLabel(QStringLiteral("UserMacros")); + + static const auto win = Internal::HostOsInfo::HostOsWindows; + + group->appendProperty(QStringLiteral("QbsExecutableDir"), + Internal::PathUtils::toNativeSeparators(qbsExecutable.path(), win) + + Internal::HostOsInfo::pathSeparator(win)); + group->appendProperty(QStringLiteral("QbsProjectDir"), + Internal::PathUtils::toNativeSeparators(project.filePath().path(), win) + + Internal::HostOsInfo::pathSeparator(win)); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuildsolutionpropertiesproject.h b/src/lib/corelib/generators/visualstudio/msbuildsolutionpropertiesproject.h new file mode 100644 index 00000000..6e70ed46 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildsolutionpropertiesproject.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDSOLUTIONPROPERTIESPROJECT_H +#define MSBUILDSOLUTIONPROPERTIESPROJECT_H + +#include "msbuild/msbuildproject.h" + +#include +#include + +#include + +namespace qbs { + +class MSBuildSolutionPropertiesProject : public MSBuildProject +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildSolutionPropertiesProject) +public: + MSBuildSolutionPropertiesProject(const Internal::VisualStudioVersionInfo &versionInfo, + const GeneratableProject &project, + const QFileInfo &qbsExecutable); +}; + +} // namespace qbs + +#endif // MSBUILDSOLUTIONPROPERTIESPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/msbuildtargetproject.cpp b/src/lib/corelib/generators/visualstudio/msbuildtargetproject.cpp new file mode 100644 index 00000000..9a1d0047 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildtargetproject.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildtargetproject.h" +#include "msbuildutils.h" +#include "visualstudiogenerator.h" + +#include "msbuild/msbuildimport.h" +#include "msbuild/msbuildimportgroup.h" +#include "msbuild/msbuilditem.h" +#include "msbuild/msbuilditemgroup.h" +#include "msbuild/msbuildproperty.h" +#include "msbuild/msbuildpropertygroup.h" + +namespace qbs { + +class MSBuildTargetProjectPrivate +{ +public: + MSBuildTargetProjectPrivate(const Internal::VisualStudioVersionInfo &versionInfo) + : versionInfo(versionInfo) {} + MSBuildPropertyGroup *globalsPropertyGroup; + MSBuildProperty *projectGuidProperty; + const Internal::VisualStudioVersionInfo &versionInfo; +}; + +MSBuildTargetProject::MSBuildTargetProject(const GeneratableProject &project, + const Internal::VisualStudioVersionInfo &versionInfo, + VisualStudioGenerator *parent) + : MSBuildProject(parent) + , d(new MSBuildTargetProjectPrivate(versionInfo)) +{ + setDefaultTargets(QStringLiteral("Build")); + setToolsVersion(versionInfo.toolsVersion()); + + auto projectConfigurationsGroup = new MSBuildItemGroup(this); + projectConfigurationsGroup->setLabel(QStringLiteral("ProjectConfigurations")); + + QMapIterator it(project.projects); + while (it.hasNext()) { + it.next(); + auto item = new MSBuildItem(QStringLiteral("ProjectConfiguration"), + projectConfigurationsGroup); + item->setInclude(MSBuildUtils::fullName(it.value())); + item->appendProperty(QStringLiteral("Configuration"), it.key()); + item->appendProperty(QStringLiteral("Platform"), MSBuildUtils::platform(it.value())); + } + + d->globalsPropertyGroup = new MSBuildPropertyGroup(this); + d->globalsPropertyGroup->setLabel(QStringLiteral("Globals")); + d->projectGuidProperty = new MSBuildProperty(QStringLiteral("ProjectGuid"), + QUuid::createUuid().toString(), + d->globalsPropertyGroup); + + // Trigger creation of the property sheets ImportGroup + propertySheetsImportGroup(); +} + +MSBuildTargetProject::~MSBuildTargetProject() +{ +} + +const Internal::VisualStudioVersionInfo &MSBuildTargetProject::versionInfo() const +{ + return d->versionInfo; +} + +QUuid MSBuildTargetProject::guid() const +{ + return QUuid(d->projectGuidProperty->value().toString()); +} + +void MSBuildTargetProject::setGuid(const QUuid &guid) +{ + d->projectGuidProperty->setValue(guid.toString()); +} + +MSBuildPropertyGroup *MSBuildTargetProject::globalsPropertyGroup() +{ + return d->globalsPropertyGroup; +} + +MSBuildImportGroup *MSBuildTargetProject::propertySheetsImportGroup() +{ + MSBuildImportGroup *importGroup = nullptr; + for (const auto &child : children()) { + if (auto group = qobject_cast(child)) { + if (group->label() == QStringLiteral("PropertySheets")) { + importGroup = group; + break; + } + } + } + + if (!importGroup) { + importGroup = new MSBuildImportGroup(this); + importGroup->setLabel(QStringLiteral("PropertySheets")); + } + + return importGroup; +} + +void MSBuildTargetProject::appendPropertySheet(const QString &path, bool optional) +{ + auto import = new MSBuildImport(propertySheetsImportGroup()); + import->setProject(path); + if (optional) + import->setCondition(QStringLiteral("Exists('%1')").arg(path)); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/msbuildtargetproject.h b/src/lib/corelib/generators/visualstudio/msbuildtargetproject.h new file mode 100644 index 00000000..13d890d7 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildtargetproject.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDTARGETPROJECT_H +#define MSBUILDTARGETPROJECT_H + +#include "msbuild/msbuildproject.h" + +#include +#include + +namespace qbs { + +class MSBuildImportGroup; +class MSBuildPropertyGroup; +class MSBuildTargetProjectPrivate; +class VisualStudioGenerator; + +class MSBuildTargetProject : public MSBuildProject +{ + Q_OBJECT + Q_DISABLE_COPY(MSBuildTargetProject) +protected: + MSBuildTargetProject(const GeneratableProject &project, + const Internal::VisualStudioVersionInfo &versionInfo, + VisualStudioGenerator *parent = 0); + +public: + ~MSBuildTargetProject(); + + const Internal::VisualStudioVersionInfo &versionInfo() const; + + QUuid guid() const; + void setGuid(const QUuid &guid); + + MSBuildPropertyGroup *globalsPropertyGroup(); + MSBuildImportGroup *propertySheetsImportGroup(); + void appendPropertySheet(const QString &path, bool optional = false); + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // MSBUILDTARGETPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/msbuildutils.h b/src/lib/corelib/generators/visualstudio/msbuildutils.h new file mode 100644 index 00000000..3eceeba2 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/msbuildutils.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef MSBUILDUTILS_H +#define MSBUILDUTILS_H + +#include + +namespace qbs { + +class MSBuildUtils +{ +public: + static QString _qbsArchitecture(const qbs::Project &project) + { + return project.projectConfiguration() + .value(QStringLiteral("qbs")).toMap() + .value(QStringLiteral("architecture")).toString(); + } + + static const QString visualStudioArchitectureName(const QString &qbsArch, bool useDisplayName) + { + if (qbsArch == QStringLiteral("x86") && useDisplayName) + return qbsArch; + + // map of qbs architecture names to MSBuild architecture names + static const QMap map { + {QStringLiteral("x86"), QStringLiteral("Win32")}, + {QStringLiteral("x86_64"), QStringLiteral("x64")}, + {QStringLiteral("ia64"), QStringLiteral("Itanium")}, + {QStringLiteral("arm"), QStringLiteral("ARM")}, + {QStringLiteral("arm64"), QStringLiteral("ARM64")} + }; + return map[qbsArch]; + } + + static QString configurationName(const qbs::Project &project) + { + return project.projectConfiguration() + .value(QStringLiteral("qbs")).toMap() + .value(QStringLiteral("configurationName")).toString(); + } + + static QString displayPlatform(const qbs::Project &project) + { + const auto architecture = _qbsArchitecture(project); + auto displayPlatform = visualStudioArchitectureName(architecture, true); + if (displayPlatform.isEmpty()) + displayPlatform = architecture; + return displayPlatform; + } + + static QString platform(const qbs::Project &project) + { + const auto architecture = _qbsArchitecture(project); + auto platform = visualStudioArchitectureName(architecture, false); + if (platform.isEmpty()) { + qWarning() << "WARNING: Unsupported architecture \"" + << architecture << "\"; using \"Win32\" platform."; + platform = QStringLiteral("Win32"); + } + + return platform; + } + + static QString fullDisplayName(const qbs::Project &project) + { + return QStringLiteral("%1|%2") + .arg(configurationName(project)) + .arg(displayPlatform(project)); + } + + static QString fullName(const qbs::Project &project) + { + return QStringLiteral("%1|%2").arg(configurationName(project)).arg(platform(project)); + } + + static QString buildTaskCondition(const Project &buildTask) + { + return QStringLiteral("'$(Configuration)|$(Platform)'=='") + + MSBuildUtils::fullName(buildTask) + + QStringLiteral("'"); + } +}; + +} // namespace qbs + +#endif // MSBUILDUTILS_H diff --git a/src/lib/corelib/generators/visualstudio/solution/ivisualstudiosolutionproject.cpp b/src/lib/corelib/generators/visualstudio/solution/ivisualstudiosolutionproject.cpp new file mode 100644 index 00000000..f3a6a0cb --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/ivisualstudiosolutionproject.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "ivisualstudiosolutionproject.h" + +namespace qbs { + +class IVisualStudioSolutionProjectPrivate +{ +public: + QUuid guid = QUuid::createUuid(); + QString name; + QString filePath; +}; + +IVisualStudioSolutionProject::IVisualStudioSolutionProject(QObject *parent) + : QObject(parent) + , d(new IVisualStudioSolutionProjectPrivate) +{ +} + +IVisualStudioSolutionProject::~IVisualStudioSolutionProject() +{ +} + +QUuid IVisualStudioSolutionProject::guid() const +{ + return d->guid; +} + +void IVisualStudioSolutionProject::setGuid(const QUuid &guid) +{ + d->guid = guid; +} + +QString IVisualStudioSolutionProject::name() const +{ + return d->name; +} + +void IVisualStudioSolutionProject::setName(const QString &name) +{ + d->name = name; +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/solution/ivisualstudiosolutionproject.h b/src/lib/corelib/generators/visualstudio/solution/ivisualstudiosolutionproject.h new file mode 100644 index 00000000..c7089e95 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/ivisualstudiosolutionproject.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef IVISUALSTUDIOSOLUTIONPROJECT_H +#define IVISUALSTUDIOSOLUTIONPROJECT_H + +#include +#include +#include +#include + +namespace qbs { + +class IVisualStudioSolutionProjectPrivate; + +class IVisualStudioSolutionProject : public QObject +{ + Q_OBJECT +protected: + explicit IVisualStudioSolutionProject(QObject *parent = 0); + +public: + virtual ~IVisualStudioSolutionProject(); + + virtual QUuid projectTypeGuid() const = 0; + + QUuid guid() const; + void setGuid(const QUuid &guid); + + virtual QString name() const; + void setName(const QString &name); + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // IVISUALSTUDIOSOLUTIONPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/solution/visualstudiosolution.cpp b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolution.cpp new file mode 100644 index 00000000..db2946ed --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolution.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "visualstudiosolution.h" + +#include "visualstudiosolutionfileproject.h" +#include "visualstudiosolutionfolderproject.h" + +#include + +#include + +namespace qbs { + +class VisualStudioSolutionPrivate +{ +public: + VisualStudioSolutionPrivate(const Internal::VisualStudioVersionInfo &versionInfo) + : versionInfo(versionInfo) { } + const Internal::VisualStudioVersionInfo versionInfo; + QList projects; + QMap> dependencies; + QList globalSections; +}; + +VisualStudioSolution::VisualStudioSolution(const Internal::VisualStudioVersionInfo &versionInfo, + QObject *parent) + : QObject(parent) + , d(new VisualStudioSolutionPrivate(versionInfo)) +{ +} + +VisualStudioSolution::~VisualStudioSolution() +{ +} + +Internal::VisualStudioVersionInfo VisualStudioSolution::versionInfo() const +{ + return d->versionInfo; +} + +QList VisualStudioSolution::projects() const +{ + return d->projects; +} + +QList VisualStudioSolution::fileProjects() const +{ + QList list; + for (const auto &project : d->projects) + if (auto fileProject = qobject_cast(project)) + list.append(fileProject); + return list; +} + +QList VisualStudioSolution::folderProjects() const +{ + QList list; + for (const auto &project : d->projects) + if (auto folderProject = qobject_cast(project)) + list.append(folderProject); + return list; +} + +void VisualStudioSolution::appendProject(IVisualStudioSolutionProject *project) +{ + d->projects.append(project); +} + +QList VisualStudioSolution::dependencies( + VisualStudioSolutionFileProject *project) const +{ + return d->dependencies.value(project); +} + +void VisualStudioSolution::addDependency(VisualStudioSolutionFileProject *project, + VisualStudioSolutionFileProject *dependency) +{ + d->dependencies[project].append(dependency); +} + +QList VisualStudioSolution::globalSections() const +{ + return d->globalSections; +} + +void VisualStudioSolution::appendGlobalSection(VisualStudioSolutionGlobalSection *globalSection) +{ + d->globalSections.append(globalSection); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/solution/visualstudiosolution.h b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolution.h new file mode 100644 index 00000000..7769a9d1 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolution.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef VISUALSTUDIOSOLUTION_H +#define VISUALSTUDIOSOLUTION_H + +#include +#include + +namespace qbs { + +namespace Internal { class VisualStudioVersionInfo; } + +class MSBuildProject; + +class IVisualStudioSolutionProject; +class VisualStudioSolutionFileProject; +class VisualStudioSolutionFolderProject; +class VisualStudioSolutionGlobalSection; + +class VisualStudioSolutionPrivate; + +class VisualStudioSolution : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(VisualStudioSolution) +public: + explicit VisualStudioSolution(const Internal::VisualStudioVersionInfo &versionInfo, + QObject *parent = 0); + ~VisualStudioSolution(); + + Internal::VisualStudioVersionInfo versionInfo() const; + + QList projects() const; + QList fileProjects() const; + QList folderProjects() const; + void appendProject(IVisualStudioSolutionProject *project); + void removeProject(const IVisualStudioSolutionProject *project); + void clearProjects(); + + QList dependencies( + VisualStudioSolutionFileProject *project) const; + void addDependency(VisualStudioSolutionFileProject *project, + VisualStudioSolutionFileProject *dependency); + + QList globalSections() const; + void appendGlobalSection(VisualStudioSolutionGlobalSection *globalSection); + +private: + void addDefaultGlobalSections(); + + QScopedPointer d; +}; + +} // namespace qbs + +#endif // VISUALSTUDIOSOLUTION_H diff --git a/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfileproject.cpp b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfileproject.cpp new file mode 100644 index 00000000..3be75954 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfileproject.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "visualstudiosolutionfileproject.h" + +#include + +namespace qbs { + +class VisualStudioSolutionFileProjectPrivate +{ +public: + QString filePath; +}; + +VisualStudioSolutionFileProject::VisualStudioSolutionFileProject(const QString &filePath, + QObject *parent) + : IVisualStudioSolutionProject(parent) + , d(new VisualStudioSolutionFileProjectPrivate) +{ + setFilePath(filePath); +} + +VisualStudioSolutionFileProject::~VisualStudioSolutionFileProject() +{ +} + +QString VisualStudioSolutionFileProject::name() const +{ + const auto projectName = IVisualStudioSolutionProject::name(); + if (projectName.isEmpty()) + return QFileInfo(filePath()).baseName(); + return projectName; +} + +QUuid VisualStudioSolutionFileProject::projectTypeGuid() const +{ + return QStringLiteral("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"); // C++ +} + +QString VisualStudioSolutionFileProject::filePath() const +{ + return d->filePath; +} + +void VisualStudioSolutionFileProject::setFilePath(const QString &filePath) +{ + d->filePath = filePath; +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfileproject.h b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfileproject.h new file mode 100644 index 00000000..b0dc63ee --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfileproject.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef VISUALSTUDIOSOLUTIONFILEPROJECT_H +#define VISUALSTUDIOSOLUTIONFILEPROJECT_H + +#include +#include "ivisualstudiosolutionproject.h" + +namespace qbs { + +class VisualStudioSolutionFileProjectPrivate; + +class VisualStudioSolutionFileProject : public IVisualStudioSolutionProject +{ + Q_OBJECT +public: + explicit VisualStudioSolutionFileProject(const QString &filePath, QObject *parent = 0); + ~VisualStudioSolutionFileProject(); + + QString name() const override; + + QString filePath() const; + void setFilePath(const QString &filePath); + + QUuid projectTypeGuid() const override; + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // VISUALSTUDIOSOLUTIONFILEPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfolderproject.cpp b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfolderproject.cpp new file mode 100644 index 00000000..1ad2dd9b --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfolderproject.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "visualstudiosolutionfolderproject.h" + +#include + +namespace qbs { + +VisualStudioSolutionFolderProject::VisualStudioSolutionFolderProject(QObject *parent) + : IVisualStudioSolutionProject(parent) +{ +} + +QUuid VisualStudioSolutionFolderProject::projectTypeGuid() const +{ + return QStringLiteral("{2150E333-8FDC-42A3-9474-1A3956D46DE8}"); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfolderproject.h b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfolderproject.h new file mode 100644 index 00000000..967e5f4d --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionfolderproject.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef VISUALSTUDIOSOLUTIONFOLDERPROJECT_H +#define VISUALSTUDIOSOLUTIONFOLDERPROJECT_H + +#include +#include "ivisualstudiosolutionproject.h" + +namespace qbs { + +class VisualStudioSolutionFolderProject : public IVisualStudioSolutionProject +{ + Q_OBJECT +public: + explicit VisualStudioSolutionFolderProject(QObject *parent); + + QUuid projectTypeGuid() const override; +}; + +} // namespace qbs + +#endif // VISUALSTUDIOSOLUTIONFOLDERPROJECT_H diff --git a/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionglobalsection.cpp b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionglobalsection.cpp new file mode 100644 index 00000000..4fc4d07f --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionglobalsection.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "visualstudiosolutionglobalsection.h" +#include +#include + +namespace qbs { + +class VisualStudioSolutionGlobalSectionPrivate +{ +public: + QString name; + QVector> properties; + bool post = false; +}; + +VisualStudioSolutionGlobalSection::VisualStudioSolutionGlobalSection(const QString &name, + QObject *parent) + : QObject(parent) + , d(new VisualStudioSolutionGlobalSectionPrivate) +{ + setName(name); +} + +VisualStudioSolutionGlobalSection::~VisualStudioSolutionGlobalSection() +{ +} + +QString VisualStudioSolutionGlobalSection::name() const +{ + return d->name; +} + +void VisualStudioSolutionGlobalSection::setName(const QString &name) +{ + d->name = name; +} + +bool VisualStudioSolutionGlobalSection::isPost() const +{ + return d->post; +} + +void VisualStudioSolutionGlobalSection::setPost(bool post) +{ + d->post = post; +} + +QVector > VisualStudioSolutionGlobalSection::properties() const +{ + return d->properties; +} + +void VisualStudioSolutionGlobalSection::appendProperty(const QString &key, const QString &value) +{ + d->properties.append({ key, value }); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionglobalsection.h b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionglobalsection.h new file mode 100644 index 00000000..8795253d --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/solution/visualstudiosolutionglobalsection.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef VISUALSTUDIOSOLUTIONGLOBALSECTION_H +#define VISUALSTUDIOSOLUTIONGLOBALSECTION_H + +#include +#include + +namespace qbs { + +class VisualStudioSolutionGlobalSectionPrivate; + +class VisualStudioSolutionGlobalSection : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(VisualStudioSolutionGlobalSection) +public: + explicit VisualStudioSolutionGlobalSection(const QString &name, QObject *parent = 0); + ~VisualStudioSolutionGlobalSection(); + + QString name() const; + void setName(const QString &name); + + bool isPost() const; + void setPost(bool post); + + QVector > properties() const; + void appendProperty(const QString &key, const QString &value); + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // VISUALSTUDIOSOLUTIONGLOBALSECTION_H diff --git a/src/lib/corelib/generators/visualstudio/visualstudio.pri b/src/lib/corelib/generators/visualstudio/visualstudio.pri new file mode 100644 index 00000000..e7615d8c --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/visualstudio.pri @@ -0,0 +1,87 @@ +HEADERS += \ + $$PWD/msbuildfiltersproject.h \ + $$PWD/msbuildqbsgenerateproject.h \ + $$PWD/msbuildqbsproductproject.h \ + $$PWD/msbuildsharedsolutionpropertiesproject.h \ + $$PWD/msbuildsolutionpropertiesproject.h \ + $$PWD/msbuildtargetproject.h \ + $$PWD/msbuildutils.h \ + $$PWD/visualstudiogenerator.h \ + $$PWD/visualstudioguidpool.h + +SOURCES += \ + $$PWD/msbuildfiltersproject.cpp \ + $$PWD/msbuildqbsgenerateproject.cpp \ + $$PWD/msbuildqbsproductproject.cpp \ + $$PWD/msbuildsharedsolutionpropertiesproject.cpp \ + $$PWD/msbuildsolutionpropertiesproject.cpp \ + $$PWD/msbuildtargetproject.cpp \ + $$PWD/visualstudiogenerator.cpp \ + $$PWD/visualstudioguidpool.cpp + +HEADERS += \ + $$PWD/solution/ivisualstudiosolutionproject.h \ + $$PWD/solution/visualstudiosolutionfileproject.h \ + $$PWD/solution/visualstudiosolutionfolderproject.h \ + $$PWD/solution/visualstudiosolution.h \ + $$PWD/solution/visualstudiosolutionglobalsection.h \ + +SOURCES += \ + $$PWD/solution/ivisualstudiosolutionproject.cpp \ + $$PWD/solution/visualstudiosolutionfileproject.cpp \ + $$PWD/solution/visualstudiosolutionfolderproject.cpp \ + $$PWD/solution/visualstudiosolution.cpp \ + $$PWD/solution/visualstudiosolutionglobalsection.cpp + +HEADERS += \ + $$PWD/msbuild/imsbuildgroup.h \ + $$PWD/msbuild/imsbuildnode.h \ + $$PWD/msbuild/imsbuildnodevisitor.h \ + $$PWD/msbuild/imsbuildproperty.h \ + $$PWD/msbuild/msbuildimport.h \ + $$PWD/msbuild/msbuildimportgroup.h \ + $$PWD/msbuild/msbuilditem.h \ + $$PWD/msbuild/msbuilditemdefinitiongroup.h \ + $$PWD/msbuild/msbuilditemgroup.h \ + $$PWD/msbuild/msbuilditemmetadata.h \ + $$PWD/msbuild/msbuildproject.h \ + $$PWD/msbuild/msbuildproperty.h \ + $$PWD/msbuild/msbuildpropertygroup.h + +SOURCES += \ + $$PWD/msbuild/imsbuildgroup.cpp \ + $$PWD/msbuild/imsbuildnode.cpp \ + $$PWD/msbuild/imsbuildproperty.cpp \ + $$PWD/msbuild/msbuildimport.cpp \ + $$PWD/msbuild/msbuildimportgroup.cpp \ + $$PWD/msbuild/msbuilditem.cpp \ + $$PWD/msbuild/msbuilditemdefinitiongroup.cpp \ + $$PWD/msbuild/msbuilditemgroup.cpp \ + $$PWD/msbuild/msbuilditemmetadata.cpp \ + $$PWD/msbuild/msbuildproject.cpp \ + $$PWD/msbuild/msbuildproperty.cpp \ + $$PWD/msbuild/msbuildpropertygroup.cpp + +HEADERS += \ + $$PWD/msbuild/items/msbuildclcompile.h \ + $$PWD/msbuild/items/msbuildclinclude.h \ + $$PWD/msbuild/items/msbuildfileitem.h \ + $$PWD/msbuild/items/msbuildfilter.h \ + $$PWD/msbuild/items/msbuildlink.h \ + $$PWD/msbuild/items/msbuildnone.h + +SOURCES += \ + $$PWD/msbuild/items/msbuildclcompile.cpp \ + $$PWD/msbuild/items/msbuildclinclude.cpp \ + $$PWD/msbuild/items/msbuildfileitem.cpp \ + $$PWD/msbuild/items/msbuildfilter.cpp \ + $$PWD/msbuild/items/msbuildlink.cpp \ + $$PWD/msbuild/items/msbuildnone.cpp + +HEADERS += \ + $$PWD/io/msbuildprojectwriter.h \ + $$PWD/io/visualstudiosolutionwriter.h + +SOURCES += \ + $$PWD/io/msbuildprojectwriter.cpp \ + $$PWD/io/visualstudiosolutionwriter.cpp diff --git a/src/lib/corelib/generators/visualstudio/visualstudio.qbs b/src/lib/corelib/generators/visualstudio/visualstudio.qbs new file mode 100644 index 00000000..44694d8d --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/visualstudio.qbs @@ -0,0 +1,112 @@ +import qbs + +QbsLibrary { + type: ["staticlibrary"] + name: "visualstudiogenerator" + install: false + + cpp.includePaths: base.concat([ + "../..", + ]) + + Depends { name: "cpp" } + Depends { name: "Qt.core" } + + Group { + name: "Visual Studio generator" + files: [ + "msbuildfiltersproject.cpp", + "msbuildfiltersproject.h", + "msbuildqbsgenerateproject.cpp", + "msbuildqbsgenerateproject.h", + "msbuildqbsproductproject.cpp", + "msbuildqbsproductproject.h", + "msbuildsharedsolutionpropertiesproject.cpp", + "msbuildsharedsolutionpropertiesproject.h", + "msbuildsolutionpropertiesproject.cpp", + "msbuildsolutionpropertiesproject.h", + "msbuildtargetproject.cpp", + "msbuildtargetproject.h", + "msbuildutils.h", + "visualstudiogenerator.cpp", + "visualstudiogenerator.h", + "visualstudioguidpool.cpp", + "visualstudioguidpool.h", + ] + } + Group { + name: "Solution Object Model" + prefix: "solution/" + files: [ + "ivisualstudiosolutionproject.cpp", + "ivisualstudiosolutionproject.h", + "visualstudiosolutionfileproject.cpp", + "visualstudiosolutionfileproject.h", + "visualstudiosolutionfolderproject.cpp", + "visualstudiosolutionfolderproject.h", + "visualstudiosolution.cpp", + "visualstudiosolution.h", + "visualstudiosolutionglobalsection.cpp", + "visualstudiosolutionglobalsection.h", + ] + } + Group { + name: "MSBuild Object Model" + prefix: "msbuild/" + files: [ + "imsbuildgroup.cpp", + "imsbuildgroup.h", + "imsbuildnode.cpp", + "imsbuildnode.h", + "imsbuildnodevisitor.h", + "imsbuildproperty.cpp", + "imsbuildproperty.h", + "msbuildimport.cpp", + "msbuildimport.h", + "msbuildimportgroup.cpp", + "msbuildimportgroup.h", + "msbuilditem.cpp", + "msbuilditem.h", + "msbuilditemdefinitiongroup.cpp", + "msbuilditemdefinitiongroup.h", + "msbuilditemgroup.cpp", + "msbuilditemgroup.h", + "msbuilditemmetadata.cpp", + "msbuilditemmetadata.h", + "msbuildproject.cpp", + "msbuildproject.h", + "msbuildproperty.cpp", + "msbuildproperty.h", + "msbuildpropertygroup.cpp", + "msbuildpropertygroup.h", + ] + } + Group { + name: "MSBuild Object Model Items" + prefix: "msbuild/items/" + files: [ + "msbuildclcompile.cpp", + "msbuildclcompile.h", + "msbuildclinclude.cpp", + "msbuildclinclude.h", + "msbuildfileitem.cpp", + "msbuildfileitem.h", + "msbuildfilter.cpp", + "msbuildfilter.h", + "msbuildlink.cpp", + "msbuildlink.h", + "msbuildnone.cpp", + "msbuildnone.h", + ] + } + Group { + name: "Visual Studio Object Model I/O" + prefix: "io/" + files: [ + "msbuildprojectwriter.cpp", + "msbuildprojectwriter.h", + "visualstudiosolutionwriter.cpp", + "visualstudiosolutionwriter.h", + ] + } +} diff --git a/src/lib/corelib/generators/visualstudio/visualstudiogenerator.cpp b/src/lib/corelib/generators/visualstudio/visualstudiogenerator.cpp new file mode 100644 index 00000000..dd2f66e0 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/visualstudiogenerator.cpp @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "msbuildfiltersproject.h" +#include "msbuildqbsgenerateproject.h" +#include "msbuildsharedsolutionpropertiesproject.h" +#include "msbuildsolutionpropertiesproject.h" +#include "msbuildqbsproductproject.h" +#include "msbuildutils.h" +#include "visualstudiogenerator.h" +#include "visualstudioguidpool.h" + +#include "msbuild/msbuildpropertygroup.h" +#include "msbuild/msbuildproject.h" + +#include "solution/visualstudiosolution.h" +#include "solution/visualstudiosolutionfileproject.h" +#include "solution/visualstudiosolutionglobalsection.h" +#include "solution/visualstudiosolutionfolderproject.h" + +#include "io/msbuildprojectwriter.h" +#include "io/visualstudiosolutionwriter.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { + +using namespace Internal; + +class VisualStudioGeneratorPrivate +{ + friend class SolutionDependenciesVisitor; +public: + VisualStudioGeneratorPrivate(const Internal::VisualStudioVersionInfo &versionInfo) + : versionInfo(versionInfo) {} + + Internal::VisualStudioVersionInfo versionInfo; + + QSharedPointer guidPool; + QSharedPointer solution; + QString solutionFilePath; + QMap> msbuildProjects; + QMap solutionProjects; + QMap solutionFolders; + QList> propertySheetNames; + + void reset(); +}; + +void VisualStudioGeneratorPrivate::reset() +{ + guidPool.reset(); + solution.reset(); + solutionFilePath.clear(); + msbuildProjects.clear(); + solutionProjects.clear(); + solutionFolders.clear(); + propertySheetNames.clear(); +} + +class SolutionDependenciesVisitor : public IGeneratableProjectVisitor +{ +public: + SolutionDependenciesVisitor(VisualStudioGenerator *generator) + : generator(generator) { + } + + void visitProject(const GeneratableProject &project) override { + Q_UNUSED(project); + nestedProjects = new VisualStudioSolutionGlobalSection( + QStringLiteral("NestedProjects"), generator->d->solution.data()); + generator->d->solution->appendGlobalSection(nestedProjects); + } + + void visitProjectData(const GeneratableProject &project, + const GeneratableProjectData &parentProjectData, + const GeneratableProjectData &projectData) override { + Q_UNUSED(project); + // The root project will have a null GeneratableProjectData + // as its parent object (so skip giving it a parent folder) + if (!parentProjectData.name().isEmpty()) { + nestedProjects->appendProperty( + generator->d->solutionFolders.value(projectData.name())->guid() + .toString(), + generator->d->solutionFolders.value(parentProjectData.name())->guid() + .toString()); + } + } + + void visitProduct(const GeneratableProject &project, + const GeneratableProjectData &projectData, + const GeneratableProductData &productData) override { + Q_UNUSED(project); + Q_UNUSED(projectData); + for (const auto &dep : productData.dependencies()) { + generator->d->solution->addDependency( + generator->d->solutionProjects.value(productData.name()), + generator->d->solutionProjects.value(dep)); + } + + nestedProjects->appendProperty( + generator->d->solutionProjects.value(productData.name())->guid().toString(), + generator->d->solutionFolders.value(projectData.name())->guid().toString()); + } + +private: + VisualStudioGenerator *generator; + VisualStudioSolutionGlobalSection *nestedProjects; +}; + +VisualStudioGenerator::VisualStudioGenerator(const VisualStudioVersionInfo &versionInfo) + : d(new VisualStudioGeneratorPrivate(versionInfo)) +{ + if (d->versionInfo.usesVcBuild()) + throw ErrorInfo(Tr::tr("VCBuild (Visual Studio 2008 and below) is not supported")); + else if (!d->versionInfo.usesMsBuild()) + throw ErrorInfo(Tr::tr("Unknown/unsupported build engine")); + Q_ASSERT(d->versionInfo.usesSolutions()); +} + +VisualStudioGenerator::~VisualStudioGenerator() +{ +} + +QString VisualStudioGenerator::generatorName() const +{ + return QStringLiteral("visualstudio%1").arg(d->versionInfo.marketingVersion()); +} + +void VisualStudioGenerator::addPropertySheets(const GeneratableProject &project) +{ + { + const auto fileName = QStringLiteral("qbs.props"); + d->propertySheetNames.append({ fileName, true }); + d->msbuildProjects.insert(project.baseBuildDirectory().absoluteFilePath(fileName), + QSharedPointer::create( + d->versionInfo, project, qbsExecutableFilePath())); + } + + { + const auto fileName = QStringLiteral("qbs-shared.props"); + d->propertySheetNames.append({ fileName, false }); + d->msbuildProjects.insert(project.baseBuildDirectory().absoluteFilePath(fileName), + QSharedPointer::create( + d->versionInfo, project, qbsExecutableFilePath())); + } +} + +void VisualStudioGenerator::addPropertySheets( + const QSharedPointer &targetProject) +{ + for (const auto &pair : d->propertySheetNames) { + targetProject->appendPropertySheet( + QStringLiteral("$(SolutionDir)\\") + pair.first, pair.second); + } +} + +static QString targetFilePath(const QString &baseName, const QString &baseBuildDirectory) +{ + return QDir(baseBuildDirectory).absoluteFilePath(baseName + QStringLiteral(".vcxproj")); +} + +static QString targetFilePath(const GeneratableProductData &product, + const QString &baseBuildDirectory) +{ + return targetFilePath(product.name(), baseBuildDirectory); +} + +static void addDefaultGlobalSections(const GeneratableProject &topLevelProject, + VisualStudioSolution *solution) +{ + auto configurationPlatformsSection = new VisualStudioSolutionGlobalSection( + QStringLiteral("SolutionConfigurationPlatforms"), solution); + solution->appendGlobalSection(configurationPlatformsSection); + for (const auto &qbsProject : topLevelProject.projects) + configurationPlatformsSection->appendProperty(MSBuildUtils::fullName(qbsProject), + MSBuildUtils::fullName(qbsProject)); + + auto projectConfigurationPlatformsSection = new VisualStudioSolutionGlobalSection( + QStringLiteral("ProjectConfigurationPlatforms"), solution); + solution->appendGlobalSection(projectConfigurationPlatformsSection); + projectConfigurationPlatformsSection->setPost(true); + for (const auto project : solution->projects()) { + for (const auto &qbsProject : topLevelProject.projects) { + projectConfigurationPlatformsSection->appendProperty( + QStringLiteral("%1.%2.ActiveCfg") + .arg(project->guid().toString()) + .arg(MSBuildUtils::fullDisplayName(qbsProject)), + MSBuildUtils::fullName(qbsProject)); + projectConfigurationPlatformsSection->appendProperty( + QStringLiteral("%1.%2.Build.0") + .arg(project->guid().toString()) + .arg(MSBuildUtils::fullDisplayName(qbsProject)), + MSBuildUtils::fullName(qbsProject)); + } + } + + auto solutionPropsSection = new VisualStudioSolutionGlobalSection( + QStringLiteral("SolutionProperties"), solution); + solution->appendGlobalSection(solutionPropsSection); + solutionPropsSection->appendProperty(QStringLiteral("HideSolutionNode"), + QStringLiteral("FALSE")); +} + +static void writeProjectFiles(const QMap> &projects) +{ + // Write out all the MSBuild project files to disk + QMapIterator> it(projects); + while (it.hasNext()) { + it.next(); + const auto projectFilePath = it.key(); + Internal::FileSaver file(projectFilePath); + if (!file.open()) + throw ErrorInfo(Tr::tr("Cannot open %s for writing").arg(projectFilePath)); + + QSharedPointer project = it.value(); + MSBuildProjectWriter writer(file.device()); + if (!(writer.write(project.data()) && file.commit())) + throw ErrorInfo(Tr::tr("Failed to generate %1").arg(projectFilePath)); + } +} + +static void writeSolution(const QSharedPointer &solution, + const QString &solutionFilePath) +{ + Internal::FileSaver file(solutionFilePath); + if (!file.open()) + throw ErrorInfo(Tr::tr("Cannot open %s for writing").arg(solutionFilePath)); + + VisualStudioSolutionWriter writer(file.device()); + writer.setProjectBaseDirectory(QFileInfo(solutionFilePath).path()); + if (!(writer.write(solution.data()) && file.commit())) + throw ErrorInfo(Tr::tr("Failed to generate %1").arg(solutionFilePath)); + + qDebug() << "Generated" << qPrintable(QFileInfo(solutionFilePath).fileName()); +} + +void VisualStudioGenerator::generate() +{ + GeneratableProjectIterator it(project()); + it.accept(this); + + addDefaultGlobalSections(project(), d->solution.data()); + + // Second pass: connection solution project interdependencies and project nesting hierarchy + SolutionDependenciesVisitor solutionDependenciesVisitor(this); + it.accept(&solutionDependenciesVisitor); + + writeProjectFiles(d->msbuildProjects); + writeSolution(d->solution, d->solutionFilePath); + + d->reset(); +} + +QVector > VisualStudioGenerator::createGeneratorList() +{ + QVector > result; + for (const auto &info : VisualStudioVersionInfo::knownVersions()) { + if (info.usesMsBuild()) + result << QSharedPointer(new VisualStudioGenerator(info)); + } + return result; +} + +void VisualStudioGenerator::visitProject(const GeneratableProject &project) +{ + addPropertySheets(project); + + const auto buildDir = project.baseBuildDirectory(); + + d->guidPool = QSharedPointer::create( + buildDir.absoluteFilePath(project.name() + QStringLiteral(".guid.txt"))); + + d->solutionFilePath = buildDir.absoluteFilePath(project.name() + QStringLiteral(".sln")); + d->solution = QSharedPointer::create(d->versionInfo); + + // Create a helper project to re-run qbs generate + const auto qbsGenerate = QStringLiteral("qbs-generate"); + const auto projectFilePath = targetFilePath(qbsGenerate, buildDir.absolutePath()); + const auto relativeProjectFilePath = QFileInfo(d->solutionFilePath).dir() + .relativeFilePath(projectFilePath); + auto targetProject = QSharedPointer::create(project, d->versionInfo); + targetProject->setGuid(d->guidPool->drawProductGuid(relativeProjectFilePath)); + d->msbuildProjects.insert(projectFilePath, targetProject); + + addPropertySheets(targetProject); + + auto solutionProject = new VisualStudioSolutionFileProject( + targetFilePath(qbsGenerate, project.baseBuildDirectory().absolutePath()), + d->solution.data()); + solutionProject->setGuid(targetProject->guid()); + d->solution->appendProject(solutionProject); + d->solutionProjects.insert(qbsGenerate, solutionProject); +} + +void VisualStudioGenerator::visitProjectData(const GeneratableProject &project, + const GeneratableProjectData &projectData) +{ + Q_UNUSED(project); + auto solutionFolder = new VisualStudioSolutionFolderProject(d->solution.data()); + solutionFolder->setName(projectData.name()); + d->solution->appendProject(solutionFolder); + d->solutionFolders.insert(projectData.name(), solutionFolder); +} + +void VisualStudioGenerator::visitProduct(const GeneratableProject &project, + const GeneratableProjectData &projectData, + const GeneratableProductData &productData) +{ + Q_UNUSED(projectData); + const auto projectFilePath = targetFilePath(productData, + project.baseBuildDirectory().absolutePath()); + const auto relativeProjectFilePath = QFileInfo(d->solutionFilePath) + .dir().relativeFilePath(projectFilePath); + auto targetProject = QSharedPointer::create(project, productData, + d->versionInfo); + targetProject->setGuid(d->guidPool->drawProductGuid(relativeProjectFilePath)); + + addPropertySheets(targetProject); + + d->msbuildProjects.insert(projectFilePath, targetProject); + d->msbuildProjects.insert(projectFilePath + QStringLiteral(".filters"), + QSharedPointer::create(productData)); + + auto solutionProject = new VisualStudioSolutionFileProject( + targetFilePath(productData, project.baseBuildDirectory().absolutePath()), + d->solution.data()); + solutionProject->setGuid(targetProject->guid()); + d->solution->appendProject(solutionProject); + d->solutionProjects.insert(productData.name(), solutionProject); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/visualstudiogenerator.h b/src/lib/corelib/generators/visualstudio/visualstudiogenerator.h new file mode 100644 index 00000000..1dc13db0 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/visualstudiogenerator.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef QBS_VISUALSTUDIOGENERATOR_H +#define QBS_VISUALSTUDIOGENERATOR_H + +#include +#include +#include +#include "visualstudioguidpool.h" + +#include +#include +#include + +namespace qbs { + +namespace Internal { class VisualStudioVersionInfo; } + +class MSBuildProject; +class MSBuildTargetProject; + +class VisualStudioGeneratorPrivate; +class VisualStudioSolution; +class VisualStudioSolutionFileProject; +class VisualStudioSolutionFolderProject; + +class VisualStudioGenerator : public ProjectGenerator, private IGeneratableProjectVisitor +{ + friend class SolutionDependenciesVisitor; +public: + explicit VisualStudioGenerator(const Internal::VisualStudioVersionInfo &versionInfo); + ~VisualStudioGenerator(); + QString generatorName() const override; + void generate() override; + + static QVector > createGeneratorList(); + +private: + virtual void visitProject(const GeneratableProject &project) override; + virtual void visitProjectData(const GeneratableProject &project, + const GeneratableProjectData &projectData) override; + virtual void visitProduct(const GeneratableProject &project, + const GeneratableProjectData &projectData, + const GeneratableProductData &productData) override; + + void addPropertySheets(const GeneratableProject &project); + void addPropertySheets(const QSharedPointer &targetProject); + + QScopedPointer d; +}; + +} // namespace qbs + +#endif // QBS_VISUALSTUDIOGENERATOR_H diff --git a/src/lib/corelib/generators/visualstudio/visualstudioguidpool.cpp b/src/lib/corelib/generators/visualstudio/visualstudioguidpool.cpp new file mode 100644 index 00000000..40e61010 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/visualstudioguidpool.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "visualstudioguidpool.h" +#include +#include +#include +#include +#include + +namespace qbs { + +class VisualStudioGuidPoolPrivate +{ +public: + QString storeFilePath; + QMap productGuids; +}; + +VisualStudioGuidPool::VisualStudioGuidPool(const QString &storeFilePath) + : d(new VisualStudioGuidPoolPrivate) +{ + // Read any existing GUIDs from the on-disk store + QFile file(d->storeFilePath = storeFilePath); + if (file.exists() && file.open(QIODevice::ReadOnly)) { + const auto data = QJsonDocument::fromJson(file.readAll()).toVariant().toMap(); + + QMapIterator it = data; + while (it.hasNext()) { + it.next(); + d->productGuids.insert(it.key(), QUuid(it.value().toString())); + } + } +} + +VisualStudioGuidPool::~VisualStudioGuidPool() +{ + Internal::FileSaver file(d->storeFilePath); + if (file.open()) { + QVariantMap productData; + QMapIterator it(d->productGuids); + while (it.hasNext()) { + it.next(); + productData.insert(it.key(), it.value().toString()); + } + + file.write(QJsonDocument::fromVariant(productData).toJson()); + file.commit(); + } +} + +QUuid VisualStudioGuidPool::drawProductGuid(const QString &productName) +{ + if (!d->productGuids.contains(productName)) + d->productGuids.insert(productName, QUuid::createUuid()); + return d->productGuids.value(productName); +} + +} // namespace qbs diff --git a/src/lib/corelib/generators/visualstudio/visualstudioguidpool.h b/src/lib/corelib/generators/visualstudio/visualstudioguidpool.h new file mode 100644 index 00000000..d5e46790 --- /dev/null +++ b/src/lib/corelib/generators/visualstudio/visualstudioguidpool.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef VISUALSTUDIOGUIDPOOL_H +#define VISUALSTUDIOGUIDPOOL_H + +#include +#include +#include + +namespace qbs { + +class VisualStudioGuidPoolPrivate; + +/*! + * Provides persistent storage for GUIDs related to Visual Studio project file nodes. + * These are stored on disk separately from project files and so allow projects to be + * regenerated while retaining the same GUIDs. This helps avoid unnecessary project + * reloads in Visual Studio, and helps ease source control usage. + */ +class VisualStudioGuidPool +{ +public: + explicit VisualStudioGuidPool(const QString &storeFilePath); + ~VisualStudioGuidPool(); + + QUuid drawProductGuid(const QString &productName); + +private: + QScopedPointer d; +}; + +} // namespace qbs + +#endif // VISUALSTUDIOGUIDPOOL_H diff --git a/src/lib/corelib/jsextensions/domxml.cpp b/src/lib/corelib/jsextensions/domxml.cpp new file mode 100644 index 00000000..e3f178fd --- /dev/null +++ b/src/lib/corelib/jsextensions/domxml.cpp @@ -0,0 +1,380 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "domxml.h" + +#include +#include + + +namespace qbs { +namespace Internal { + +void initializeJsExtensionXml(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue obj = engine->newQMetaObject(&XmlDomDocument::staticMetaObject, engine->newFunction(&XmlDomDocument::ctor)); + extensionObject.setProperty(QLatin1String("XmlDomDocument"), obj); + obj = engine->newQMetaObject(&XmlDomNode::staticMetaObject, engine->newFunction(&XmlDomNode::ctor)); + extensionObject.setProperty(QLatin1String("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(QLatin1String("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(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(newChild.toQObject()); + if (!newNode) { + context()->throwError(QString::fromLatin1("First argument is not a XmlDomNode object")); + return QScriptValue(); + } + + XmlDomNode *refNode = qobject_cast(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(newChild.toQObject()); + if (!newNode) { + context()->throwError(QString::fromLatin1("First argument is not a XmlDomNode object")); + return QScriptValue(); + } + + XmlDomNode *refNode = qobject_cast(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(newChild.toQObject()); + if (!newNode) { + context()->throwError(QString::fromLatin1("First argument is not a XmlDomNode object")); + return QScriptValue(); + } + + XmlDomNode *oldNode = qobject_cast(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(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 00000000..e9f61a4f --- /dev/null +++ b/src/lib/corelib/jsextensions/domxml.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 BogDan Vatra +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DOMXML_H +#define DOMXML_H + +#include +#include +#include +#include +#include +#include + +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/environmentextension.cpp b/src/lib/corelib/jsextensions/environmentextension.cpp new file mode 100644 index 00000000..91f98fe2 --- /dev/null +++ b/src/lib/corelib/jsextensions/environmentextension.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "environmentextension.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class EnvironmentExtension : public QObject, QScriptable +{ + Q_OBJECT +public: + void initializeJsExtensionEnvironment(QScriptValue extensionObject); + static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_getEnv(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_putEnv(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_unsetEnv(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_currentEnv(QScriptContext *context, QScriptEngine *engine); +}; + +void initializeJsExtensionEnvironment(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue environmentObj = engine->newQMetaObject(&EnvironmentExtension::staticMetaObject, + engine->newFunction(&EnvironmentExtension::js_ctor)); + environmentObj.setProperty(QStringLiteral("getEnv"), + engine->newFunction(EnvironmentExtension::js_getEnv, 1)); + environmentObj.setProperty(QStringLiteral("putEnv"), + engine->newFunction(EnvironmentExtension::js_putEnv, 2)); + environmentObj.setProperty(QStringLiteral("unsetEnv"), + engine->newFunction(EnvironmentExtension::js_unsetEnv, 1)); + environmentObj.setProperty(QStringLiteral("currentEnv"), + engine->newFunction(EnvironmentExtension::js_currentEnv, 0)); + extensionObject.setProperty(QStringLiteral("Environment"), environmentObj); +} + +QScriptValue EnvironmentExtension::js_ctor(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + return context->throwError(Tr::tr("'Environment' cannot be instantiated.")); +} + +static QProcessEnvironment *getProcessEnvironment(QScriptContext *context, QScriptEngine *engine, + const QString &func, bool doThrow = true) +{ + QVariant v = engine->property("_qbs_procenv"); + QProcessEnvironment *procenv = reinterpret_cast(v.value()); + if (!procenv && doThrow) + throw context->throwError(QScriptContext::UnknownError, + QStringLiteral("%1 can only be called from ").arg(func) + + QStringLiteral("Module.setupBuildEnvironment and ") + + QStringLiteral("Module.setupRunEnvironment")); + return procenv; +} + +QScriptValue EnvironmentExtension::js_getEnv(QScriptContext *context, QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("getEnv expects 1 argument")); + const QProcessEnvironment env = static_cast(engine)->environment(); + const QProcessEnvironment *procenv = getProcessEnvironment(context, engine, + QStringLiteral("getEnv"), false); + if (!procenv) + procenv = &env; + + const QString name = context->argument(0).toString(); + const QString value = procenv->value(name); + + // Don't track environment variable access inside environment setup scripts + // since change tracking is irrelevant for them + if (procenv == &env) + static_cast(engine)->addEnvironmentVariable(name, value); + + return value.isNull() ? engine->undefinedValue() : value; +} + +QScriptValue EnvironmentExtension::js_putEnv(QScriptContext *context, QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() != 2)) + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("putEnv expects 2 arguments")); + getProcessEnvironment(context, engine, QStringLiteral("putEnv"))->insert( + context->argument(0).toString(), + context->argument(1).toString()); + return engine->undefinedValue(); +} + +QScriptValue EnvironmentExtension::js_unsetEnv(QScriptContext *context, QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("unsetEnv expects 1 argument")); + getProcessEnvironment(context, engine, QStringLiteral("unsetEnv"))->remove( + context->argument(0).toString()); + return engine->undefinedValue(); +} + +QScriptValue EnvironmentExtension::js_currentEnv(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(context); + const QProcessEnvironment env = static_cast(engine)->environment(); + const QProcessEnvironment *procenv = getProcessEnvironment(context, engine, + QStringLiteral("currentEnv"), false); + if (!procenv) + procenv = &env; + QScriptValue envObject = engine->newObject(); + foreach (const QString &key, procenv->keys()) + envObject.setProperty(key, QScriptValue(procenv->value(key))); + return envObject; +} + +} // namespace Internal +} // namespace qbs + +Q_DECLARE_METATYPE(qbs::Internal::EnvironmentExtension *) + +#include "environmentextension.moc" diff --git a/src/lib/corelib/jsextensions/environmentextension.h b/src/lib/corelib/jsextensions/environmentextension.h new file mode 100644 index 00000000..a9faf1a5 --- /dev/null +++ b/src/lib/corelib/jsextensions/environmentextension.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ENVIRONMENTEXTENSION_H +#define QBS_ENVIRONMENTEXTENSION_H + +#include + +QT_BEGIN_NAMESPACE +class QScriptContext; +class QScriptEngine; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { + +void initializeJsExtensionEnvironment(QScriptValue extensionObject); + +} // namespace Internal +} // namespace qbs + +#endif // QBS_ENVIRONMENTEXTENSION_H diff --git a/src/lib/corelib/jsextensions/file.cpp b/src/lib/corelib/jsextensions/file.cpp new file mode 100644 index 00000000..90dae1d4 --- /dev/null +++ b/src/lib/corelib/jsextensions/file.cpp @@ -0,0 +1,298 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "file.h" + +#include +#include +#include + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class File : public QObject, QScriptable +{ + Q_OBJECT +public: + enum Filter { + Dirs = 0x001, + Files = 0x002, + Drives = 0x004, + NoSymLinks = 0x008, + AllEntries = Dirs | Files | Drives, + TypeMask = 0x00f, + + Readable = 0x010, + Writable = 0x020, + Executable = 0x040, + PermissionMask = 0x070, + + Modified = 0x080, + Hidden = 0x100, + System = 0x200, + + AccessMask = 0x3F0, + + AllDirs = 0x400, + CaseSensitive = 0x800, + NoDot = 0x2000, + NoDotDot = 0x4000, + NoDotAndDotDot = NoDot | NoDotDot, + + NoFilter = -1 + }; + Q_DECLARE_FLAGS(Filters, Filter) + Q_ENUMS(Filter) + + 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_directoryEntries(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_lastModified(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_makePath(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_move(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_remove(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_canonicalFilePath(QScriptContext *context, QScriptEngine *engine); +}; + +void initializeJsExtensionFile(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue fileObj = engine->newQMetaObject(&File::staticMetaObject, + engine->newFunction(&File::js_ctor)); + fileObj.setProperty(QLatin1String("copy"), engine->newFunction(File::js_copy)); + fileObj.setProperty(QLatin1String("exists"), engine->newFunction(File::js_exists)); + fileObj.setProperty(QLatin1String("directoryEntries"), + engine->newFunction(File::js_directoryEntries)); + fileObj.setProperty(QLatin1String("lastModified"), engine->newFunction(File::js_lastModified)); + fileObj.setProperty(QLatin1String("makePath"), engine->newFunction(File::js_makePath)); + fileObj.setProperty(QLatin1String("move"), engine->newFunction(File::js_move)); + fileObj.setProperty(QLatin1String("remove"), engine->newFunction(File::js_remove)); + fileObj.setProperty(QLatin1String("canonicalFilePath"), + engine->newFunction(File::js_canonicalFilePath)); + extensionObject.setProperty(QLatin1String("File"), fileObj); +} + +QScriptValue File::js_ctor(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + return context->throwError(Tr::tr("'File' cannot be instantiated.")); +} + +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")); + } + + ScriptEngine * const se = static_cast(engine); + const DubiousContextList dubiousContexts({ + DubiousContext(EvalContext::PropertyEvaluation), + DubiousContext(EvalContext::RuleExecution, DubiousContext::SuggestMoving) + }); + se->checkContext(QLatin1String("File.copy()"), dubiousContexts); + + const QString sourceFile = context->argument(0).toString(); + const QString targetFile = context->argument(1).toString(); + QString errorMessage; + if (Q_UNLIKELY(!copyFileRecursion(sourceFile, targetFile, true, true, &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(engine); + se->addFileExistsResult(filePath, exists); + return exists; +} + +QScriptValue File::js_directoryEntries(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 2)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("directoryEntries expects 2 arguments")); + } + + ScriptEngine * const se = static_cast(engine); + const DubiousContextList dubiousContexts({ + DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving) + }); + se->checkContext(QLatin1String("File.directoryEntries()"), dubiousContexts); + + const QString path = context->argument(0).toString(); + const QDir::Filters filters = static_cast(context->argument(1).toUInt32()); + QDir dir(path); + const QStringList entries = dir.entryList(filters, QDir::Name); + se->addDirectoryEntriesResult(path, filters, entries); + return qScriptValueFromSequence(engine, entries); +} + +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")); + } + + ScriptEngine * const se = static_cast(engine); + const DubiousContextList dubiousContexts({ DubiousContext(EvalContext::PropertyEvaluation) }); + se->checkContext(QLatin1String("File.remove()"), dubiousContexts); + + 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(engine); + se->addFileLastModifiedResult(filePath, timestamp); + return static_cast(timestamp.m_fileTime); +} + +QScriptValue File::js_makePath(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("makePath expects 1 argument")); + } + + ScriptEngine * const se = static_cast(engine); + const DubiousContextList dubiousContexts({ DubiousContext(EvalContext::PropertyEvaluation) }); + se->checkContext(QLatin1String("File.makePath()"), dubiousContexts); + + return QDir::root().mkpath(context->argument(0).toString()); +} + +QScriptValue File::js_move(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 2)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("move expects 2 arguments")); + } + + ScriptEngine * const se = static_cast(engine); + const DubiousContextList dubiousContexts({ DubiousContext(EvalContext::PropertyEvaluation) }); + se->checkContext(QLatin1String("File.move()"), dubiousContexts); + + const QString sourceFile = context->argument(0).toString(); + const QString targetFile = context->argument(1).toString(); + const bool overwrite = context->argumentCount() > 2 ? context->argument(2).toBool() : true; + + if (Q_UNLIKELY(QFileInfo(sourceFile).isDir())) + return context->throwError(QString(QLatin1String("Could not move '%1' to '%2': " + "Source file path is a directory.")) + .arg(sourceFile, targetFile)); + + if (Q_UNLIKELY(QFileInfo(targetFile).isDir())) { + return context->throwError(QString(QLatin1String("Could not move '%1' to '%2': " + "Destination file path is a directory.")) + .arg(sourceFile, targetFile)); + } + + if (!QFile(sourceFile).isReadable()) + return context->throwError(QString(QLatin1String("Could not move '%1' to '%2': " + "Source file is not accessible.")) + .arg(sourceFile, targetFile)); + + QFile f(targetFile); + if (overwrite && !f.remove()) + return context->throwError(QString(QLatin1String("Could not move '%1' to '%2': %3")) + .arg(sourceFile, targetFile, f.errorString())); + + if (QFile::exists(targetFile)) + return context->throwError(QString(QLatin1String("Could not move '%1' to '%2': " + "Destination file exists.")) + .arg(sourceFile, targetFile)); + + QFile f2(sourceFile); + if (Q_UNLIKELY(!f2.rename(targetFile))) + return context->throwError(QString(QLatin1String("Could not move '%1' to '%2': %3")) + .arg(sourceFile, targetFile, f2.errorString())); + return true; +} + +QScriptValue File::js_canonicalFilePath(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("canonicalFilePath expects 1 argument")); + } + return QFileInfo(context->argument(0).toString()).canonicalFilePath(); +} + +} // namespace Internal +} // namespace qbs + +Q_DECLARE_METATYPE(qbs::Internal::File *) + +#include "file.moc" diff --git a/src/lib/corelib/jsextensions/file.h b/src/lib/corelib/jsextensions/file.h new file mode 100644 index 00000000..f81eb82b --- /dev/null +++ b/src/lib/corelib/jsextensions/file.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_FILE_H +#define QBS_FILE_H + +#include +#include + +namespace qbs { +namespace Internal { + +void initializeJsExtensionFile(QScriptValue extensionObject); + +} // namespace Internal +} // namespace qbs + +#endif // QBS_FILE_H diff --git a/src/lib/corelib/jsextensions/fileinfoextension.cpp b/src/lib/corelib/jsextensions/fileinfoextension.cpp new file mode 100644 index 00000000..1c8a12cc --- /dev/null +++ b/src/lib/corelib/jsextensions/fileinfoextension.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fileinfoextension.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class FileInfoExtension : public QObject, QScriptable +{ + Q_OBJECT +public: + friend void initializeJsExtensionFileInfo(QScriptValue extensionObject); + +private: + static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_path(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_fileName(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_baseName(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_completeBaseName(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_relativePath(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_resolvePath(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_isAbsolutePath(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_toWindowsSeparators(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_fromWindowsSeparators(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_toNativeSeparators(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_fromNativeSeparators(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_joinPaths(QScriptContext *context, QScriptEngine *engine); +}; + +void initializeJsExtensionFileInfo(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue fileInfoObj = engine->newQMetaObject(&FileInfoExtension::staticMetaObject, + engine->newFunction(&FileInfoExtension::js_ctor)); + fileInfoObj.setProperty(QLatin1String("path"), + engine->newFunction(FileInfoExtension::js_path)); + fileInfoObj.setProperty(QLatin1String("fileName"), + engine->newFunction(FileInfoExtension::js_fileName)); + fileInfoObj.setProperty(QLatin1String("baseName"), + engine->newFunction(FileInfoExtension::js_baseName)); + fileInfoObj.setProperty(QLatin1String("completeBaseName"), + engine->newFunction(FileInfoExtension::js_completeBaseName)); + fileInfoObj.setProperty(QLatin1String("relativePath"), + engine->newFunction(FileInfoExtension::js_relativePath)); + fileInfoObj.setProperty(QLatin1String("resolvePath"), + engine->newFunction(FileInfoExtension::js_resolvePath)); + fileInfoObj.setProperty(QLatin1String("isAbsolutePath"), + engine->newFunction(FileInfoExtension::js_isAbsolutePath)); + fileInfoObj.setProperty(QLatin1String("toWindowsSeparators"), + engine->newFunction(FileInfoExtension::js_toWindowsSeparators)); + fileInfoObj.setProperty(QLatin1String("fromWindowsSeparators"), + engine->newFunction(FileInfoExtension::js_fromWindowsSeparators)); + fileInfoObj.setProperty(QLatin1String("toNativeSeparators"), + engine->newFunction(FileInfoExtension::js_toWindowsSeparators)); + fileInfoObj.setProperty(QLatin1String("fromNativeSeparators"), + engine->newFunction(FileInfoExtension::js_fromWindowsSeparators)); + fileInfoObj.setProperty(QLatin1String("joinPaths"), + engine->newFunction(FileInfoExtension::js_joinPaths)); + extensionObject.setProperty(QLatin1String("FileInfo"), fileInfoObj); +} + +QScriptValue FileInfoExtension::js_ctor(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + return context->throwError(Tr::tr("'FileInfo' cannot be instantiated.")); +} + +QScriptValue FileInfoExtension::js_path(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("path expects 1 argument")); + } + HostOsInfo::HostOs hostOs = HostOsInfo::hostOs(); + if (context->argumentCount() > 1) { + hostOs = context->argument(1).toVariant().toStringList().contains(QLatin1String("windows")) + ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix; + } + return FileInfo::path(context->argument(0).toString(), hostOs); +} + +QScriptValue FileInfoExtension::js_fileName(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("fileName expects 1 argument")); + } + return FileInfo::fileName(context->argument(0).toString()); +} + +QScriptValue FileInfoExtension::js_baseName(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("baseName expects 1 argument")); + } + return FileInfo::baseName(context->argument(0).toString()); +} + +QScriptValue FileInfoExtension::js_completeBaseName(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("completeBaseName expects 1 argument")); + } + return FileInfo::completeBaseName(context->argument(0).toString()); +} + +QScriptValue FileInfoExtension::js_relativePath(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("relativePath expects 2 arguments")); + } + const QString base = context->argument(0).toString(); + const QString rel = context->argument(1).toString(); + return QDir(base).relativeFilePath(rel); +} + +QScriptValue FileInfoExtension::js_resolvePath(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("resolvePath expects 2 arguments")); + } + const QString base = context->argument(0).toString(); + const QString rel = context->argument(1).toString(); + return FileInfo::resolvePath(base, rel); +} + +QScriptValue FileInfoExtension::js_isAbsolutePath(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("isAbsolutePath expects 1 argument")); + } + HostOsInfo::HostOs hostOs = HostOsInfo::hostOs(); + if (context->argumentCount() > 1) { + hostOs = context->argument(1).toVariant().toStringList().contains(QLatin1String("windows")) + ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix; + } + return FileInfo::isAbsolute(context->argument(0).toString(), hostOs); +} + +QScriptValue FileInfoExtension::js_toWindowsSeparators(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("toWindowsSeparators expects 1 argument")); + } + return context->argument(0).toString().replace(QLatin1Char('/'), QLatin1Char('\\')); +} + +QScriptValue FileInfoExtension::js_fromWindowsSeparators(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("fromWindowsSeparators expects 1 argument")); + } + return context->argument(0).toString().replace(QLatin1Char('\\'), QLatin1Char('/')); +} + +QScriptValue FileInfoExtension::js_toNativeSeparators(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("toNativeSeparators expects 1 argument")); + } + return QDir::toNativeSeparators(context->argument(0).toString()); +} + +QScriptValue FileInfoExtension::js_fromNativeSeparators(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + Tr::tr("fromNativeSeparators expects 1 argument")); + } + return QDir::fromNativeSeparators(context->argument(0).toString()); +} + +QScriptValue FileInfoExtension::js_joinPaths(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + QStringList paths; + for (int i = 0; i < context->argumentCount(); ++i) { + const QScriptValue value = context->argument(i); + if (!value.isUndefined() && !value.isNull()) { + const QString arg = value.toString(); + if (!arg.isEmpty()) + paths.append(arg); + } + } + return paths.join(QLatin1Char('/')).replace(QRegularExpression(QLatin1String("/{2,}")), + QLatin1String("/")); +} + +} // namespace Internal +} // namespace qbs + +Q_DECLARE_METATYPE(qbs::Internal::FileInfoExtension *) + +#include "fileinfoextension.moc" diff --git a/src/lib/corelib/jsextensions/fileinfoextension.h b/src/lib/corelib/jsextensions/fileinfoextension.h new file mode 100644 index 00000000..9a1baa5b --- /dev/null +++ b/src/lib/corelib/jsextensions/fileinfoextension.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_FILEINFOEXTENSION_H +#define QBS_FILEINFOEXTENSION_H + +#include + +namespace qbs { +namespace Internal { + +void initializeJsExtensionFileInfo(QScriptValue extensionObject); + +} // namespace Internal +} // namespace qbs + +#endif // QBS_FILEINFOEXTENSION_H diff --git a/src/lib/corelib/jsextensions/jsextensions.cpp b/src/lib/corelib/jsextensions/jsextensions.cpp new file mode 100644 index 00000000..886998a2 --- /dev/null +++ b/src/lib/corelib/jsextensions/jsextensions.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "jsextensions.h" + +#include "domxml.h" +#include "environmentextension.h" +#include "file.h" +#include "fileinfoextension.h" +#include "process.h" +#include "propertylist.h" +#include "temporarydir.h" +#include "textfile.h" +#include "utilitiesextension.h" + +#include + +#include + +namespace qbs { +namespace Internal { + +typedef QHash InitializerMap; +static InitializerMap initializers() +{ + static const InitializerMap theMap = { + std::make_pair(QLatin1String("Environment"), &initializeJsExtensionEnvironment), + std::make_pair(QLatin1String("File"), &initializeJsExtensionFile), + std::make_pair(QLatin1String("FileInfo"), &initializeJsExtensionFileInfo), + std::make_pair(QLatin1String("Process"), &initializeJsExtensionProcess), + std::make_pair(QLatin1String("Xml"), &initializeJsExtensionXml), + std::make_pair(QLatin1String("TemporaryDir"), &initializeJsExtensionTemporaryDir), + std::make_pair(QLatin1String("TextFile"), &initializeJsExtensionTextFile), + std::make_pair(QLatin1String("PropertyList"), &initializeJsExtensionPropertyList), + std::make_pair(QLatin1String("Utilities"), &initializeJsExtensionUtilities) + }; + return theMap; +} + +void JsExtensions::setupExtensions(const QStringList &names, QScriptValue scope) +{ + foreach (const QString &name, names) + initializers().value(name)(scope); +} + +QScriptValue JsExtensions::loadExtension(QScriptEngine *engine, const QString &name) +{ + if (!hasExtension(name)) + return QScriptValue(); + + QScriptValue extensionObj = engine->newObject(); + initializers().value(name)(extensionObj); + return extensionObj.property(name); +} + +bool JsExtensions::hasExtension(const QString &name) +{ + return initializers().contains(name); +} + +} // 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 00000000..52a1dbb0 --- /dev/null +++ b/src/lib/corelib/jsextensions/jsextensions.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_JSEXTENSIONS_H +#define QBS_JSEXTENSIONS_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QScriptEngine; +class QScriptValue; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { + +class JsExtensions +{ +public: + static void setupExtensions(const QStringList &names, QScriptValue scope); + static QScriptValue loadExtension(QScriptEngine *engine, const QString &name); + static bool hasExtension(const QString &name); +}; + +} // 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 00000000..0258d42e --- /dev/null +++ b/src/lib/corelib/jsextensions/jsextensions.pri @@ -0,0 +1,31 @@ +QT += xml + +HEADERS += \ + $$PWD/environmentextension.h \ + $$PWD/file.h \ + $$PWD/fileinfoextension.h \ + $$PWD/temporarydir.h \ + $$PWD/textfile.h \ + $$PWD/process.h \ + $$PWD/moduleproperties.h \ + $$PWD/domxml.h \ + $$PWD/jsextensions.h \ + $$PWD/utilitiesextension.h + +SOURCES += \ + $$PWD/environmentextension.cpp \ + $$PWD/file.cpp \ + $$PWD/fileinfoextension.cpp \ + $$PWD/temporarydir.cpp \ + $$PWD/textfile.cpp \ + $$PWD/process.cpp \ + $$PWD/moduleproperties.cpp \ + $$PWD/domxml.cpp \ + $$PWD/jsextensions.cpp \ + $$PWD/utilitiesextension.cpp + +mac { + HEADERS += $$PWD/propertylist.h $$PWD/propertylistutils.h + OBJECTIVE_SOURCES += $$PWD/propertylist.mm $$PWD/propertylistutils.mm + LIBS += -framework Foundation +} diff --git a/src/lib/corelib/jsextensions/moduleproperties.cpp b/src/lib/corelib/jsextensions/moduleproperties.cpp new file mode 100644 index 00000000..0425df25 --- /dev/null +++ b/src/lib/corelib/jsextensions/moduleproperties.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "moduleproperties.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +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(QLatin1String("moduleProperties"), + engine->newFunction(ModuleProperties::js_moduleProperties, 2)); + objectWithProperties.setProperty(QLatin1String("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) +{ + ErrorInfo deprWarning(Tr::tr("The moduleProperties() function is deprecated and will be " + "removed in a future version of Qbs. Use moduleProperty() " + "instead."), context->backtrace()); + static_cast(engine)->logger().printWarning(deprWarning); + try { + return moduleProperties(context, engine); + } catch (const ErrorInfo &e) { + return context->throwError(e.toString()); + } +} + +QScriptValue ModuleProperties::js_moduleProperty(QScriptContext *context, QScriptEngine *engine) +{ + try { + return moduleProperties(context, engine); + } catch (const ErrorInfo &e) { + return context->throwError(e.toString()); + } +} + +QScriptValue ModuleProperties::moduleProperties(QScriptContext *context, QScriptEngine *engine) +{ + 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(qscriptvalue_cast(ptrScriptValue)); + PropertyMapConstPtr properties; + const Artifact *artifact = 0; + if (typeScriptValue.toString() == productType()) { + properties = static_cast(ptr)->moduleProperties; + } else if (typeScriptValue.toString() == artifactType()) { + artifact = static_cast(ptr); + properties = artifact->properties; + } else { + return context->throwError(QScriptContext::TypeError, + QLatin1String("Internal error: invalid type")); + } + + ScriptEngine * const qbsEngine = static_cast(engine); + const QString moduleName = context->argument(0).toString(); + const QString propertyName = context->argument(1).toString(); + + QVariant value; + if (qbsEngine->isPropertyCacheEnabled()) + value = qbsEngine->retrieveFromPropertyCache(moduleName, propertyName, properties); + if (!value.isValid()) { + value = PropertyFinder().propertyValue(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. + if (qbsEngine->isPropertyCacheEnabled()) + qbsEngine->addToPropertyCache(moduleName, propertyName, properties, value); + } + return engine->toScriptValue(value); +} + +} // 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 00000000..7c1fbe21 --- /dev/null +++ b/src/lib/corelib/jsextensions/moduleproperties.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_MODULEPROPERTIES_H +#define QBS_MODULEPROPERTIES_H + +#include +#include + +#include +#include + +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); +}; + +} // 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 00000000..ab4df57b --- /dev/null +++ b/src/lib/corelib/jsextensions/process.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "process.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +void initializeJsExtensionProcess(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue obj = engine->newQMetaObject(&Process::staticMetaObject, engine->newFunction(&Process::ctor)); + extensionObject.setProperty(QLatin1String("Process"), obj); + obj.setProperty(QStringLiteral("shellQuote"), engine->newFunction(Process::js_shellQuote, 3)); +} + +QScriptValue Process::ctor(QScriptContext *context, QScriptEngine *engine) +{ + Process *t; + switch (context->argumentCount()) { + case 0: + t = new Process(context); + break; + default: + return context->throwError(QLatin1String("Process()")); + } + + ScriptEngine * const se = static_cast(engine); + const DubiousContextList dubiousContexts ({ + DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving) + }); + se->checkContext(QLatin1String("qbs.Process"), dubiousContexts); + + QScriptValue obj = engine->newQObject(t, QScriptEngine::ScriptOwnership); + + // Get environment + QVariant v = engine->property("_qbs_procenv"); + if (v.isNull()) { + // The build environment is not initialized yet. + // This can happen if one uses Process on the RHS of a binding like Group.name. + t->m_environment = static_cast(engine)->environment(); + } else { + t->m_environment + = QProcessEnvironment(*reinterpret_cast(v.value())); + } + + 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(findExecutable(program), arguments); + return m_qProcess->waitForStarted(); +} + +int Process::exec(const QString &program, const QStringList &arguments, bool throwOnError) +{ + Q_ASSERT(thisObject().engine() == engine()); + + if (!start(findExecutable(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_textStream; + m_textStream = 0; + delete m_qProcess; + m_qProcess = 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()); +} + +void Process::closeWriteChannel() +{ + m_textStream->flush(); + m_qProcess->closeWriteChannel(); +} + +int Process::exitCode() const +{ + return m_qProcess->exitCode(); +} + +Logger Process::logger() const +{ + ScriptEngine *scriptEngine = static_cast(engine()); + return scriptEngine->logger(); +} + +QString Process::findExecutable(const QString &filePath) const +{ + ExecutableFinder exeFinder(ResolvedProductPtr(), m_environment, logger()); + return exeFinder.findExecutable(filePath, m_workingDirectory); +} + +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'; +} + +QScriptValue Process::js_shellQuote(QScriptContext *context, QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() < 2)) { + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("shellQuote expects at least 2 arguments")); + } + const QString program = context->argument(0).toString(); + const QStringList args = context->argument(1).toVariant().toStringList(); + HostOsInfo::HostOs hostOs = HostOsInfo::hostOs(); + if (context->argumentCount() > 2) { + hostOs = context->argument(2).toVariant().toStringList().contains(QLatin1String("windows")) + ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix; + } + return engine->toScriptValue(shellQuote(program, args, hostOs)); +} + +} // 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 00000000..389fbf56 --- /dev/null +++ b/src/lib/corelib/jsextensions/process.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PROCESS_H +#define QBS_PROCESS_H + +#include + +#include +#include +#include +#include + +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 closeWriteChannel(); + + Q_INVOKABLE void write(const QString &str); + Q_INVOKABLE void writeLine(const QString &str); + + Q_INVOKABLE int exitCode() const; + + static QScriptValue js_shellQuote(QScriptContext *context, QScriptEngine *engine); + +private: + Logger logger() const; + QString findExecutable(const QString &filePath) const; + + 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/propertylist.h b/src/lib/corelib/jsextensions/propertylist.h new file mode 100644 index 00000000..31442207 --- /dev/null +++ b/src/lib/corelib/jsextensions/propertylist.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PROPERTYLIST_H +#define QBS_PROPERTYLIST_H + +#include + +// ### remove when qbs requires qbs 1.6 to build itself +#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) && defined(__APPLE__) && !defined(Q_OS_MAC) +#define Q_OS_MAC +#endif + +#ifndef Q_OS_MAC + +#include + +namespace qbs { +namespace Internal { + +// provide a fake initializer for other platforms +void initializeJsExtensionPropertyList(QScriptValue extensionObject) +{ + // provide a fake object + QScriptEngine *engine = extensionObject.engine(); + extensionObject.setProperty(QLatin1String("PropertyList"), engine->newObject()); +} + +} // namespace Internal +} // namespace qbs + +#else // Q_OS_MAC + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +void initializeJsExtensionPropertyList(QScriptValue extensionObject); + +class PropertyListPrivate; + +class PropertyList : public QObject, public QScriptable +{ + Q_OBJECT +public: + static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine); + PropertyList(QScriptContext *context); + ~PropertyList(); + Q_INVOKABLE bool isEmpty() const; + Q_INVOKABLE void clear(); + Q_INVOKABLE void readFromObject(const QScriptValue &value); + Q_INVOKABLE void readFromString(const QString &input); + Q_INVOKABLE void readFromFile(const QString &filePath); + Q_INVOKABLE void readFromData(const QByteArray &data); + Q_INVOKABLE void writeToFile(const QString &filePath, const QString &plistFormat); + Q_INVOKABLE QScriptValue format() const; + Q_INVOKABLE QScriptValue toObject() const; + Q_INVOKABLE QString toString(const QString &plistFormat) const; + Q_INVOKABLE QString toXMLString() const; + Q_INVOKABLE QString toJSON(const QString &style = QString()) const; +private: + PropertyListPrivate *d; +}; + +} // namespace Internal +} // namespace qbs + +Q_DECLARE_METATYPE(qbs::Internal::PropertyList *) + +#endif // Q_OS_MAC + +#endif // QBS_PROPERTYLIST_H diff --git a/src/lib/corelib/jsextensions/propertylist.mm b/src/lib/corelib/jsextensions/propertylist.mm new file mode 100644 index 00000000..849b2dca --- /dev/null +++ b/src/lib/corelib/jsextensions/propertylist.mm @@ -0,0 +1,340 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "propertylist.h" + +#include +#include + +#include +#include +#include +#include + +// Same values as CoreFoundation and Foundation APIs +enum { + QPropertyListOpenStepFormat = 1, + QPropertyListXMLFormat_v1_0 = 100, + QPropertyListBinaryFormat_v1_0 = 200, + QPropertyListJSONFormat = 1000 // If this conflicts someday, just change it :) +}; + +namespace qbs { +namespace Internal { + +class PropertyListPrivate +{ +public: + PropertyListPrivate(); + + QVariant propertyListObject; + int propertyListFormat; + + void readFromData(QScriptContext *context, QByteArray data); + QByteArray writeToData(QScriptContext *context, const QString &format); +}; + +void initializeJsExtensionPropertyList(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue obj = engine->newQMetaObject(&PropertyList::staticMetaObject, + engine->newFunction(&PropertyList::ctor)); + extensionObject.setProperty(QLatin1String("PropertyList"), obj); +} + +QScriptValue PropertyList::ctor(QScriptContext *context, QScriptEngine *engine) +{ + ScriptEngine * const se = static_cast(engine); + const DubiousContextList dubiousContexts({ + DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving) + }); + se->checkContext(QLatin1String("qbs.PropertyList"), dubiousContexts); + + PropertyList *p = new PropertyList(context); + QScriptValue obj = engine->newQObject(p, QScriptEngine::ScriptOwnership); + return obj; +} + +PropertyListPrivate::PropertyListPrivate() + : propertyListObject(), propertyListFormat(0) +{ +} + +PropertyList::~PropertyList() +{ + delete d; +} + +PropertyList::PropertyList(QScriptContext *context) +: d(new PropertyListPrivate) +{ + Q_UNUSED(context); + Q_ASSERT(thisObject().engine() == engine()); +} + +bool PropertyList::isEmpty() const +{ + Q_ASSERT(thisObject().engine() == engine()); + PropertyList *p = qscriptvalue_cast(thisObject()); + return p->d->propertyListObject.isNull(); +} + +void PropertyList::clear() +{ + Q_ASSERT(thisObject().engine() == engine()); + PropertyList *p = qscriptvalue_cast(thisObject()); + p->d->propertyListObject = QVariant(); + p->d->propertyListFormat = 0; +} + +void PropertyList::readFromObject(const QScriptValue &value) +{ + Q_ASSERT(thisObject().engine() == engine()); + PropertyList *p = qscriptvalue_cast(thisObject()); + p->d->propertyListObject = value.toVariant(); + p->d->propertyListFormat = 0; // wasn't deserialized from any external format +} + +void PropertyList::readFromString(const QString &input) +{ + readFromData(input.toUtf8()); +} + +void PropertyList::readFromFile(const QString &filePath) +{ + Q_ASSERT(thisObject().engine() == engine()); + PropertyList *p = qscriptvalue_cast(thisObject()); + + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + const QByteArray data = file.readAll(); + if (file.error() == QFile::NoError) { + p->d->readFromData(p->context(), data); + return; + } + } + + p->context()->throwError(QStringLiteral("%1: %2").arg(filePath).arg(file.errorString())); +} + +void PropertyList::readFromData(const QByteArray &data) +{ + Q_ASSERT(thisObject().engine() == engine()); + PropertyList *p = qscriptvalue_cast(thisObject()); + p->d->readFromData(p->context(), data); +} + +void PropertyList::writeToFile(const QString &filePath, const QString &plistFormat) +{ + Q_ASSERT(thisObject().engine() == engine()); + PropertyList *p = qscriptvalue_cast(thisObject()); + + QFile file(filePath); + QByteArray data = p->d->writeToData(p->context(), plistFormat); + if (Q_LIKELY(!data.isEmpty())) { + if (file.open(QIODevice::WriteOnly) && file.write(data) == data.size()) { + return; + } + } + + p->context()->throwError(QStringLiteral("%1: %2").arg(filePath).arg(file.errorString())); +} + +QScriptValue PropertyList::format() const +{ + Q_ASSERT(thisObject().engine() == engine()); + PropertyList *p = qscriptvalue_cast(thisObject()); + switch (p->d->propertyListFormat) + { + case QPropertyListOpenStepFormat: + return QLatin1String("openstep"); + case QPropertyListXMLFormat_v1_0: + return QLatin1String("xml1"); + case QPropertyListBinaryFormat_v1_0: + return QLatin1String("binary1"); + case QPropertyListJSONFormat: + return QLatin1String("json"); + default: + return p->engine()->undefinedValue(); + } +} + +QScriptValue PropertyList::toObject() const +{ + Q_ASSERT(thisObject().engine() == engine()); + PropertyList *p = qscriptvalue_cast(thisObject()); + return p->engine()->toScriptValue(p->d->propertyListObject); +} + +QString PropertyList::toString(const QString &plistFormat) const +{ + Q_ASSERT(thisObject().engine() == engine()); + PropertyList *p = qscriptvalue_cast(thisObject()); + + if (plistFormat == QLatin1String("binary1")) { + p->context()->throwError(QLatin1String("Property list object cannot be converted to a " + "string in the binary1 format; this format can only " + "be written directly to a file")); + return QString(); + } + + if (!isEmpty()) + return QString::fromUtf8(p->d->writeToData(p->context(), plistFormat)); + + return QString(); +} + +QString PropertyList::toXMLString() const +{ + return toString(QLatin1String("xml1")); +} + +QString PropertyList::toJSON(const QString &style) const +{ + QString format = QLatin1String("json"); + if (!style.isEmpty()) + format += QLatin1String("-") + style; + + return toString(format); +} + +} // namespace Internal +} // namespace qbs + +#include "propertylistutils.h" + +namespace qbs { +namespace Internal { + +void PropertyListPrivate::readFromData(QScriptContext *context, QByteArray data) +{ + @autoreleasepool { + NSPropertyListFormat format; + int internalFormat = 0; + NSString *errorString = nil; + id plist = [NSPropertyListSerialization propertyListWithData:data.toNSData() + options:0 + format:&format error:nil]; + if (plist) { + internalFormat = format; + } else { + NSError *error = nil; + plist = [NSJSONSerialization JSONObjectWithData:data.toNSData() + options:0 + error:&error]; + if (Q_UNLIKELY(!plist)) { + errorString = [error localizedDescription]; + } else { + internalFormat = QPropertyListJSONFormat; + } + } + + if (Q_UNLIKELY(!plist)) { + context->throwError(QString::fromNSString(errorString)); + } else { + QVariant obj = QPropertyListUtils::fromPropertyList(plist); + if (!obj.isNull()) { + propertyListObject = obj; + propertyListFormat = internalFormat; + } else { + context->throwError(QLatin1String("error converting property list")); + } + } + } +} + +QByteArray PropertyListPrivate::writeToData(QScriptContext *context, const QString &format) +{ + @autoreleasepool { + NSError *error = nil; + NSString *errorString = nil; + NSData *data = nil; + + id obj = QPropertyListUtils::toPropertyList(propertyListObject); + if (!obj) { + context->throwError(QLatin1String("error converting property list")); + return QByteArray(); + } + + if (format == QLatin1String("json") || format == QLatin1String("json-pretty") || + format == QLatin1String("json-compact")) { + if ([NSJSONSerialization isValidJSONObject:obj]) { + error = nil; + errorString = nil; + const NSJSONWritingOptions options = format == QLatin1String("json-pretty") + ? NSJSONWritingPrettyPrinted : 0; + data = [NSJSONSerialization dataWithJSONObject:obj + options:options + error:&error]; + if (Q_UNLIKELY(!data)) { + errorString = [error localizedDescription]; + } + } else { + errorString = @"Property list object cannot be converted to JSON data"; + } + } else if (format == QLatin1String("xml1") || format == QLatin1String("binary1")) { + const NSPropertyListFormat plistFormat = format == QLatin1String("xml1") + ? NSPropertyListXMLFormat_v1_0 + : NSPropertyListBinaryFormat_v1_0; + + error = nil; + errorString = nil; + data = [NSPropertyListSerialization dataWithPropertyList:obj + format:plistFormat + options:0 + error:&error]; + if (Q_UNLIKELY(!data)) { + errorString = [error localizedDescription]; + } + } else { + errorString = [NSString stringWithFormat:@"Property lists cannot be written in the '%s' " + @"format", format.toUtf8().constData()]; + } + + if (Q_UNLIKELY(!data)) { + context->throwError(QString::fromNSString(errorString)); + } + + return QByteArray::fromNSData(data); + } +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/jsextensions/propertylistutils.h b/src/lib/corelib/jsextensions/propertylistutils.h new file mode 100644 index 00000000..c02e04f1 --- /dev/null +++ b/src/lib/corelib/jsextensions/propertylistutils.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2014 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPROPERTYLISTUTILS_H +#define QPROPERTYLISTUTILS_H + +#include +#import + +#if !defined(__OBJC__) || !defined(__cplusplus) +#error "This file must be included from Objective-C++" +#endif + +class QPropertyListUtils +{ + Q_DISABLE_COPY(QPropertyListUtils) + +public: + static QVariant fromPropertyList(id plist); + static id toPropertyList(const QVariant &map); + +private: + QPropertyListUtils(); +}; + +template +QMap qHashToMap(const QHash &hash) { + QMap map; + QHashIterator i(hash); + while (i.hasNext()) { + i.next(); + map.insert(i.key(), i.value()); + } + return map; +} + +#endif // QPROPERTYLISTUTILS_H diff --git a/src/lib/corelib/jsextensions/propertylistutils.mm b/src/lib/corelib/jsextensions/propertylistutils.mm new file mode 100644 index 00000000..f0587cf4 --- /dev/null +++ b/src/lib/corelib/jsextensions/propertylistutils.mm @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Petroules Corporation. +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import +#include "propertylistutils.h" +#include +#include +#include +#include + +static inline QDateTime QDateTime_fromNSDate(const NSDate *date) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) + return QDateTime::fromNSDate(date); +#else + if (!date) + return QDateTime(); + return QDateTime::fromMSecsSinceEpoch(static_cast([date timeIntervalSince1970] * 1000)); +#endif +} + +static inline NSDate *QDateTime_toNSDate(const QDateTime &qdatetime) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) + return qdatetime.toNSDate(); +#else + return [NSDate + dateWithTimeIntervalSince1970:static_cast(qdatetime.toMSecsSinceEpoch()) / 1000]; +#endif +} + +static QVariant fromObject(id obj); +static QVariantMap fromDictionary(NSDictionary *dict); +static QVariantList fromArray(NSArray *array); + +static QVariant fromObject(id obj) +{ + QVariant value; + if (!obj) { + return value; + } else if ([obj isKindOfClass:[NSDictionary class]]) { + value = fromDictionary(obj); + } else if ([obj isKindOfClass:[NSArray class]]) { + value = fromArray(obj); + } else if ([obj isKindOfClass:[NSString class]]) { + value = QString::fromNSString(obj); + } else if ([obj isKindOfClass:[NSData class]]) { + value = QByteArray::fromNSData(obj); + } else if ([obj isKindOfClass:[NSDate class]]) { + value = QDateTime_fromNSDate(obj); + } else if ([obj isKindOfClass:[NSNumber class]]) { + if (strcmp([(NSNumber *)obj objCType], @encode(BOOL)) == 0) { + value = static_cast([obj boolValue]); + } else if (strcmp([(NSNumber *)obj objCType], @encode(signed char)) == 0) { + value = [obj charValue]; + } else if (strcmp([(NSNumber *)obj objCType], @encode(unsigned char)) == 0) { + value = [obj unsignedCharValue]; + } else if (strcmp([(NSNumber *)obj objCType], @encode(signed short)) == 0) { + value = [obj shortValue]; + } else if (strcmp([(NSNumber *)obj objCType], @encode(unsigned short)) == 0) { + value = [obj unsignedShortValue]; + } else if (strcmp([(NSNumber *)obj objCType], @encode(signed int)) == 0) { + value = [obj intValue]; + } else if (strcmp([(NSNumber *)obj objCType], @encode(unsigned int)) == 0) { + value = [obj unsignedIntValue]; + } else if (strcmp([(NSNumber *)obj objCType], @encode(signed long long)) == 0) { + value = [obj longLongValue]; + } else if (strcmp([(NSNumber *)obj objCType], @encode(unsigned long long)) == 0) { + value = [obj unsignedLongLongValue]; + } else if (strcmp([(NSNumber *)obj objCType], @encode(float)) == 0) { + value = [obj floatValue]; + } else if (strcmp([(NSNumber *)obj objCType], @encode(double)) == 0) { + value = [obj doubleValue]; + } else { + // NSDecimal or unknown + value = [obj doubleValue]; + } + } else if ([obj isKindOfClass:[NSNull class]]) { + // A null variant, close enough... + } else { + // unknown + } + + return value; +} + +static QVariantMap fromDictionary(NSDictionary *dict) +{ + QVariantMap map; + for (NSString *key in dict) + map[QString::fromNSString(key)] = fromObject([dict objectForKey:key]); + return map; +} + +static QVariantList fromArray(NSArray *array) +{ + QVariantList list; + for (id obj in array) + list.append(fromObject(obj)); + return list; +} + +QVariant QPropertyListUtils::fromPropertyList(id plist) +{ + return fromObject(plist); +} + +static id toObject(const QVariant &variant); +static NSDictionary *toDictionary(const QVariantMap &map); +static NSArray *toArray(const QVariantList &list); + +static id toObject(const QVariant &variant) +{ + if (variant.type() == QVariant::Hash) { + return toDictionary(qHashToMap(variant.toHash())); + } else if (variant.type() == QVariant::Map) { + return toDictionary(variant.toMap()); + } else if (variant.type() == QVariant::List) { + return toArray(variant.toList()); + } else if (variant.type() == QVariant::String) { + return variant.toString().toNSString(); + } else if (variant.type() == QVariant::ByteArray) { + return variant.toByteArray().toNSData(); + } else if (variant.type() == QVariant::Date || + variant.type() == QVariant::DateTime) { + return QDateTime_toNSDate(variant.toDateTime()); + } else if (variant.type() == QVariant::Bool) { + return variant.toBool() + ? [NSNumber numberWithBool:YES] + : [NSNumber numberWithBool:NO]; + } else if (variant.type() == QVariant::Char || + variant.type() == QVariant::Int) { + return [NSNumber numberWithInt:variant.toInt()]; + } else if (variant.type() == QVariant::UInt) { + return [NSNumber numberWithUnsignedInt:variant.toUInt()]; + } else if (variant.type() == QVariant::LongLong) { + return [NSNumber numberWithLongLong:variant.toLongLong()]; + } else if (variant.type() == QVariant::ULongLong) { + return [NSNumber numberWithUnsignedLongLong:variant.toULongLong()]; + } else if (variant.type() == QVariant::Double) { + return [NSNumber numberWithDouble:variant.toDouble()]; + } else { + return [NSNull null]; + } +} + +static NSDictionary *toDictionary(const QVariantMap &map) +{ + NSMutableDictionary *dict = [NSMutableDictionary dictionary]; + QMapIterator i(map); + while (i.hasNext()) { + i.next(); + [dict setObject:toObject(i.value()) forKey:i.key().toNSString()]; + } + return [NSDictionary dictionaryWithDictionary:dict]; +} + +static NSArray *toArray(const QVariantList &list) +{ + NSMutableArray *array = [NSMutableArray array]; + foreach (const QVariant &variant, list) + [array addObject:toObject(variant)]; + return [NSArray arrayWithArray:array]; +} + +id QPropertyListUtils::toPropertyList(const QVariant &variant) +{ + return toObject(variant); +} diff --git a/src/lib/corelib/jsextensions/temporarydir.cpp b/src/lib/corelib/jsextensions/temporarydir.cpp new file mode 100644 index 00000000..e7b811f3 --- /dev/null +++ b/src/lib/corelib/jsextensions/temporarydir.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "temporarydir.h" + +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +void initializeJsExtensionTemporaryDir(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue obj = engine->newQMetaObject(&TemporaryDir::staticMetaObject, + engine->newFunction(&TemporaryDir::ctor)); + extensionObject.setProperty(QLatin1String("TemporaryDir"), obj); +} + +QScriptValue TemporaryDir::ctor(QScriptContext *context, QScriptEngine *engine) +{ + ScriptEngine * const se = static_cast(engine); + const DubiousContextList dubiousContexts({ + DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving) + }); + se->checkContext(QLatin1String("qbs.TemporaryDir"), dubiousContexts); + + TemporaryDir *t = new TemporaryDir(context); + QScriptValue obj = engine->newQObject(t, QScriptEngine::ScriptOwnership); + return obj; +} + +TemporaryDir::TemporaryDir(QScriptContext *context) +{ + Q_UNUSED(context); + dir.setAutoRemove(false); +} + +bool TemporaryDir::isValid() const +{ + return dir.isValid(); +} + +QString TemporaryDir::path() const +{ + return dir.path(); +} + +bool TemporaryDir::remove() +{ + return dir.remove(); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/jsextensions/temporarydir.h b/src/lib/corelib/jsextensions/temporarydir.h new file mode 100644 index 00000000..e5666732 --- /dev/null +++ b/src/lib/corelib/jsextensions/temporarydir.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_TEMPORARYDIR_H +#define QBS_TEMPORARYDIR_H + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +void initializeJsExtensionTemporaryDir(QScriptValue extensionObject); + +class TemporaryDir : public QObject, public QScriptable +{ + Q_OBJECT +public: + static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine); + TemporaryDir(QScriptContext *context); + Q_INVOKABLE bool isValid() const; + Q_INVOKABLE QString path() const; + Q_INVOKABLE bool remove(); +private: + QTemporaryDir dir; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_TEMPORARYDIR_H diff --git a/src/lib/corelib/jsextensions/textfile.cpp b/src/lib/corelib/jsextensions/textfile.cpp new file mode 100644 index 00000000..02166c6a --- /dev/null +++ b/src/lib/corelib/jsextensions/textfile.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "textfile.h" + +#include +#include +#include + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +void initializeJsExtensionTextFile(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue obj = engine->newQMetaObject(&TextFile::staticMetaObject, + engine->newFunction(&TextFile::ctor)); + extensionObject.setProperty(QLatin1String("TextFile"), obj); +} + +QScriptValue TextFile::ctor(QScriptContext *context, QScriptEngine *engine) +{ + TextFile *t; + switch (context->argumentCount()) { + case 0: + return context->throwError(Tr::tr("TextFile constructor needs path of file to be opened.")); + case 1: + t = new TextFile(context, context->argument(0).toString()); + break; + case 2: + t = new TextFile(context, + context->argument(0).toString(), + static_cast(context->argument(1).toInt32()) + ); + break; + case 3: + t = new TextFile(context, + context->argument(0).toString(), + static_cast(context->argument(1).toInt32()), + context->argument(2).toString() + ); + break; + default: + return context->throwError(Tr::tr("TextFile constructor takes at most three parameters.")); + } + + ScriptEngine * const se = static_cast(engine); + const DubiousContextList dubiousContexts({ + DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving) + }); + se->checkContext(QLatin1String("qbs.TextFile"), dubiousContexts); + + return engine->newQObject(t, QScriptEngine::ScriptOwnership); +} + +TextFile::~TextFile() +{ + delete m_stream; + delete m_file; +} + +TextFile::TextFile(QScriptContext *context, const QString &filePath, OpenMode mode, + const QString &codec) +{ + Q_UNUSED(codec) + Q_ASSERT(thisObject().engine() == engine()); + + m_file = new QFile(filePath); + m_stream = new QTextStream(m_file); + QIODevice::OpenMode m; + switch (mode) { + case ReadWrite: + m = QIODevice::ReadWrite; + break; + case ReadOnly: + m = QIODevice::ReadOnly; + break; + case WriteOnly: + m = QIODevice::WriteOnly; + break; + } + + if (Q_UNLIKELY(!m_file->open(m))) { + context->throwError(Tr::tr("Unable to open file '%1': %2") + .arg(filePath, m_file->errorString())); + delete m_file; + m_file = 0; + } +} + +void TextFile::close() +{ + if (checkForClosed()) + return; + m_file->close(); + delete m_file; + m_file = 0; + delete m_stream; + m_stream = 0; +} + +void TextFile::setCodec(const QString &codec) +{ + if (checkForClosed()) + return; + m_stream->setCodec(qPrintable(codec)); +} + +QString TextFile::readLine() +{ + if (checkForClosed()) + return QString(); + return m_stream->readLine(); +} + +QString TextFile::readAll() +{ + if (checkForClosed()) + return QString(); + return m_stream->readAll(); +} + +bool TextFile::atEof() const +{ + if (checkForClosed()) + return true; + return m_stream->atEnd(); +} + +void TextFile::truncate() +{ + if (checkForClosed()) + return; + m_file->resize(0); + m_stream->reset(); +} + +void TextFile::write(const QString &str) +{ + if (checkForClosed()) + return; + (*m_stream) << str; +} + +void TextFile::writeLine(const QString &str) +{ + if (checkForClosed()) + return; + (*m_stream) << str; + if (HostOsInfo::isWindowsHost()) + (*m_stream) << '\r'; + (*m_stream) << '\n'; +} + +bool TextFile::checkForClosed() const +{ + if (m_file) + return false; + context()->throwError(Tr::tr("Access to TextFile object that was already closed.")); + return true; +} + +} // 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 00000000..bf898471 --- /dev/null +++ b/src/lib/corelib/jsextensions/textfile.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_TEXTFILE_H +#define QBS_TEXTFILE_H + +#include +#include +#include + +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(); + + 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: + TextFile(QScriptContext *context, const QString &filePath, OpenMode mode = ReadOnly, + const QString &codec = QLatin1String("UTF8")); + + bool checkForClosed() const; + + QFile *m_file; + QTextStream *m_stream; +}; + +} // namespace Internal +} // namespace qbs + +Q_DECLARE_METATYPE(qbs::Internal::TextFile *) + +#endif // QBS_TEXTFILE_H diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp new file mode 100644 index 00000000..1463438f --- /dev/null +++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp @@ -0,0 +1,320 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "utilitiesextension.h" + +#include +#include +#include +#include +#include +#include + +#if defined(Q_OS_MACOS) || defined(Q_OS_OSX) +#include +#endif + +#ifdef Q_OS_WIN +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class UtilitiesExtension : public QObject, QScriptable +{ + Q_OBJECT +public: + static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_canonicalToolchain(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_getHash(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_getNativeSetting(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_nativeSettingGroups(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_rfc1034identifier(QScriptContext *context, QScriptEngine *engine); + + static QScriptValue js_smimeMessageContent(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_certificateInfo(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_signingIdentities(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine); + + static QScriptValue js_versionCompare(QScriptContext *context, QScriptEngine *engine); +}; + +void initializeJsExtensionUtilities(QScriptValue extensionObject) +{ + QScriptEngine *engine = extensionObject.engine(); + QScriptValue environmentObj = engine->newQMetaObject(&UtilitiesExtension::staticMetaObject, + engine->newFunction(&UtilitiesExtension::js_ctor)); + environmentObj.setProperty(QStringLiteral("canonicalArchitecture"), + engine->newFunction(UtilitiesExtension::js_canonicalArchitecture, 1)); + environmentObj.setProperty(QStringLiteral("canonicalToolchain"), + engine->newFunction(UtilitiesExtension::js_canonicalToolchain)); + environmentObj.setProperty(QStringLiteral("getHash"), + engine->newFunction(UtilitiesExtension::js_getHash, 1)); + environmentObj.setProperty(QStringLiteral("getNativeSetting"), + engine->newFunction(UtilitiesExtension::js_getNativeSetting, 3)); + environmentObj.setProperty(QStringLiteral("nativeSettingGroups"), + engine->newFunction(UtilitiesExtension::js_nativeSettingGroups, 1)); + environmentObj.setProperty(QStringLiteral("rfc1034Identifier"), + engine->newFunction(UtilitiesExtension::js_rfc1034identifier, 1)); + environmentObj.setProperty(QStringLiteral("smimeMessageContent"), + engine->newFunction(UtilitiesExtension::js_smimeMessageContent, 1)); + environmentObj.setProperty(QStringLiteral("certificateInfo"), + engine->newFunction(UtilitiesExtension::js_certificateInfo, 1)); + environmentObj.setProperty(QStringLiteral("signingIdentities"), + engine->newFunction(UtilitiesExtension::js_signingIdentities, 0)); + environmentObj.setProperty(QStringLiteral("msvcCompilerInfo"), + engine->newFunction(UtilitiesExtension::js_msvcCompilerInfo, 1)); + environmentObj.setProperty(QStringLiteral("versionCompare"), + engine->newFunction(UtilitiesExtension::js_versionCompare, 2)); + extensionObject.setProperty(QStringLiteral("Utilities"), environmentObj); +} + +QScriptValue UtilitiesExtension::js_ctor(QScriptContext *context, QScriptEngine *engine) +{ + Q_UNUSED(engine); + return context->throwError(Tr::tr("'Utilities' cannot be instantiated.")); +} + +QScriptValue UtilitiesExtension::js_canonicalArchitecture(QScriptContext *context, + QScriptEngine *engine) +{ + const QScriptValue value = context->argument(0); + if (value.isUndefined() || value.isNull()) + return value; + + if (context->argumentCount() == 1 && value.isString()) + return engine->toScriptValue(canonicalArchitecture(value.toString())); + + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("canonicalArchitecture expects one argument of type string")); +} + +QScriptValue UtilitiesExtension::js_canonicalToolchain(QScriptContext *context, + QScriptEngine *engine) +{ + QStringList toolchain; + for (int i = 0; i < context->argumentCount(); ++i) + toolchain << context->argument(i).toString(); + return engine->toScriptValue(canonicalToolchain(toolchain)); +} + +QScriptValue UtilitiesExtension::js_getHash(QScriptContext *context, QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() < 1)) { + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("getHash expects 1 argument")); + } + const QByteArray input = context->argument(0).toString().toLatin1(); + const QByteArray hash + = QCryptographicHash::hash(input, QCryptographicHash::Sha1).toHex().left(16); + return engine->toScriptValue(QString::fromLatin1(hash)); +} + +QScriptValue UtilitiesExtension::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 UtilitiesExtension::js_nativeSettingGroups(QScriptContext *context, + QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) { + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("nativeSettingGroups expects 1 argument")); + } + + QSettings settings(context->argument(0).toString(), QSettings::NativeFormat); + return engine->toScriptValue(settings.childGroups()); +} + +QScriptValue UtilitiesExtension::js_rfc1034identifier(QScriptContext *context, + QScriptEngine *engine) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("rfc1034Identifier expects 1 argument")); + const QString identifier = context->argument(0).toString(); + return engine->toScriptValue(HostOsInfo::rfc1034Identifier(identifier)); +} + +/** + * Reads the contents of the S/MIME message located at \p filePath. + * An equivalent command line would be: + * \code security cms -D -i -o \endcode + * or: + * \code openssl smime -verify -noverify -inform DER -in -out \endcode + * + * \note A provisioning profile is an S/MIME message whose contents are an XML property list, + * so this method can be used to read such files. + */ +QScriptValue UtilitiesExtension::js_smimeMessageContent(QScriptContext *context, + QScriptEngine *engine) +{ +#if !defined(Q_OS_MACOS) && !defined(Q_OS_OSX) + Q_UNUSED(engine); + return context->throwError(QScriptContext::UnknownError, + QLatin1String("smimeMessageContent is not available on this platform")); +#else + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("smimeMessageContent expects 1 argument")); + + const QString filePath = context->argument(0).toString(); + QFile file(filePath); + if (!file.open(QIODevice::ReadOnly)) + return engine->undefinedValue(); + + QByteArray content = smimeMessageContent(file.readAll()); + if (content.isEmpty()) + return engine->undefinedValue(); + return engine->toScriptValue(content); +#endif +} + +QScriptValue UtilitiesExtension::js_certificateInfo(QScriptContext *context, + QScriptEngine *engine) +{ +#if !defined(Q_OS_MACOS) && !defined(Q_OS_OSX) + Q_UNUSED(engine); + return context->throwError(QScriptContext::UnknownError, + QLatin1String("certificateInfo is not available on this platform")); +#else + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("certificateInfo expects 1 argument")); + return engine->toScriptValue(certificateInfo(context->argument(0).toVariant().toByteArray())); +#endif +} + +// Rough command line equivalent: security find-identity -p codesigning -v +QScriptValue UtilitiesExtension::js_signingIdentities(QScriptContext *context, + QScriptEngine *engine) +{ +#if !defined(Q_OS_MACOS) && !defined(Q_OS_OSX) + Q_UNUSED(engine); + return context->throwError(QScriptContext::UnknownError, + QLatin1String("signingIdentities is not available on this platform")); +#else + Q_UNUSED(context); + return engine->toScriptValue(identitiesProperties()); +#endif +} + +QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine) +{ +#ifndef Q_OS_WIN + Q_UNUSED(engine); + return context->throwError(QScriptContext::UnknownError, + QLatin1String("msvcCompilerInfo is not available on this platform")); +#else + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("msvcCompilerInfo expects 1 argument")); + + const QString compilerFilePath = context->argument(0).toString(); + MSVC msvc(compilerFilePath); + VsEnvironmentDetector envdetector; + if (!envdetector.start(&msvc)) + return context->throwError(QScriptContext::UnknownError, + QStringLiteral("Detecting the MSVC build environment failed: ") + + envdetector.errorString()); + + try { + QVariantMap envMap; + for (const QString &key : msvc.environment.keys()) + envMap.insert(key, msvc.environment.value(key)); + + return engine->toScriptValue(QVariantMap { + {QStringLiteral("buildEnvironment"), envMap}, + {QStringLiteral("macros"), msvc.compilerDefines(compilerFilePath)}, + }); + } catch (const qbs::ErrorInfo &info) { + return context->throwError(QScriptContext::UnknownError, + info.toString()); + } +#endif +} + +QScriptValue UtilitiesExtension::js_versionCompare(QScriptContext *context, QScriptEngine *engine) +{ + if (context->argumentCount() == 2) { + const QScriptValue value1 = context->argument(0); + const QScriptValue value2 = context->argument(1); + if (value1.isString() && value2.isString()) { + const auto a = Internal::Version::fromString(value1.toString()); + const auto b = Internal::Version::fromString(value2.toString()); + return engine->toScriptValue(Internal::compare(a, b)); + } + } + + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("versionCompare expects two arguments of type string")); +} + +} // namespace Internal +} // namespace qbs + +Q_DECLARE_METATYPE(qbs::Internal::UtilitiesExtension *) + +#include "utilitiesextension.moc" diff --git a/src/lib/corelib/jsextensions/utilitiesextension.h b/src/lib/corelib/jsextensions/utilitiesextension.h new file mode 100644 index 00000000..6afcdcf7 --- /dev/null +++ b/src/lib/corelib/jsextensions/utilitiesextension.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_UTILITIESEXTENSION_H +#define QBS_UTILITIESEXTENSION_H + +#include + +namespace qbs { +namespace Internal { + +void initializeJsExtensionUtilities(QScriptValue extensionObject); + +} // namespace Internal +} // namespace qbs + +#endif // QBS_UTILITIESEXTENSION_H diff --git a/src/lib/corelib/language/artifactproperties.cpp b/src/lib/corelib/language/artifactproperties.cpp new file mode 100644 index 00000000..eae9fb66 --- /dev/null +++ b/src/lib/corelib/language/artifactproperties.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "artifactproperties.h" +#include +#include + +namespace qbs { +namespace Internal { + +ArtifactPropertiesPtr ArtifactProperties::create() +{ + return ArtifactPropertiesPtr(new ArtifactProperties); +} + +ArtifactProperties::ArtifactProperties() +{ +} + +void ArtifactProperties::load(PersistentPool &pool) +{ + m_fileTagsFilter.load(pool); + m_propertyMap = pool.idLoadS(); +} + +void ArtifactProperties::store(PersistentPool &pool) const +{ + m_fileTagsFilter.store(pool); + 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 00000000..1e0cb010 --- /dev/null +++ b/src/lib/corelib/language/artifactproperties.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ARTIFACTPROPERTIES_H +#define QBS_ARTIFACTPROPERTIES_H + +#include "filetags.h" +#include "forward_decls.h" + +#include + +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/astimportshandler.cpp b/src/lib/corelib/language/astimportshandler.cpp new file mode 100644 index 00000000..8ba18340 --- /dev/null +++ b/src/lib/corelib/language/astimportshandler.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "astimportshandler.h" + +#include "asttools.h" +#include "builtindeclarations.h" +#include "filecontext.h" +#include "itemreadervisitorstate.h" +#include "jsextensions/jsextensions.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { + +ASTImportsHandler::ASTImportsHandler(ItemReaderVisitorState &visitorState, Logger &logger, + const FileContextPtr &file) + : m_visitorState(visitorState) + , m_logger(logger) + , m_file(file) + , m_directory(FileInfo::path(m_file->filePath())) +{ +} + +void ASTImportsHandler::handleImports(const QbsQmlJS::AST::UiImportList *uiImportList) +{ + foreach (const QString &searchPath, m_file->searchPaths()) + collectPrototypes(searchPath + QLatin1String("/imports"), QString()); + + // files in the same directory are available as prototypes + collectPrototypes(m_directory, QString()); + + for (const auto *it = uiImportList; it; it = it->next) + handleImport(it->import); + + for (auto it = m_jsImports.constBegin(); it != m_jsImports.constEnd(); ++it) + m_file->addJsImport(it.value()); +} + +void ASTImportsHandler::handleImport(const QbsQmlJS::AST::UiImport *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_logger.printWarning(ErrorInfo(Tr::tr("Superfluous version specification."), + toCodeLocation(m_file->filePath(), import->versionToken))); + } + } + + QString as; + if (isBase) { + if (Q_UNLIKELY(!import->importId.isNull())) { + throw ErrorInfo(Tr::tr("Import of qbs.base must have no 'as '"), + toCodeLocation(m_file->filePath(), 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), + toCodeLocation(m_file->filePath(), import->asToken)); + } + if (Q_UNLIKELY(m_file->jsExtensions().contains(extensionName))) { + m_logger.printWarning(ErrorInfo(Tr::tr("Built-in extension '%1' already " + "imported.").arg(extensionName), + toCodeLocation(m_file->filePath(), + import->importToken))); + } else { + m_file->addJsExtension(extensionName); + } + return; + } + } + + if (import->importId.isNull()) { + if (!import->fileName.isNull()) { + throw ErrorInfo(Tr::tr("File imports require 'as '"), + toCodeLocation(m_file->filePath(), import->importToken)); + } + if (importUri.isEmpty()) { + throw ErrorInfo(Tr::tr("Invalid import URI."), + toCodeLocation(m_file->filePath(), import->importToken)); + } + as = importUri.last(); + } else { + as = import->importId.toString(); + } + + if (Q_UNLIKELY(m_importAsNames.contains(as))) { + throw ErrorInfo(Tr::tr("Cannot import into the same name more than once."), + toCodeLocation(m_file->filePath(), import->importIdToken)); + } + if (Q_UNLIKELY(JsExtensions::hasExtension(as))) + throw ErrorInfo(Tr::tr("Cannot reuse the name of built-in extension '%1'.").arg(as), + toCodeLocation(m_file->filePath(), import->importIdToken)); + m_importAsNames.insert(as); + } + + if (!import->fileName.isNull()) { + QString filePath = FileInfo::resolvePath(m_directory, import->fileName.toString()); + + QFileInfo fi(filePath); + if (Q_UNLIKELY(!fi.exists())) + throw ErrorInfo(Tr::tr("Cannot find imported file %0.") + .arg(QDir::toNativeSeparators(filePath)), + CodeLocation(m_file->filePath(), import->fileNameToken.startLine, + import->fileNameToken.startColumn)); + filePath = fi.canonicalFilePath(); + if (fi.isDir()) { + collectPrototypesAndJsCollections(filePath, as, + toCodeLocation(m_file->filePath(), import->fileNameToken)); + } else { + if (filePath.endsWith(QLatin1String(".js"), Qt::CaseInsensitive)) { + JsImport &jsImport = m_jsImports[as]; + jsImport.scopeName = as; + jsImport.filePaths.append(filePath); + jsImport.location + = toCodeLocation(m_file->filePath(), import->firstSourceLocation()); + } else if (filePath.endsWith(QLatin1String(".qbs"), Qt::CaseInsensitive)) { + m_typeNameToFile.insert(QStringList(as), filePath); + } else { + throw ErrorInfo(Tr::tr("Can only import .qbs and .js files"), + CodeLocation(m_file->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_file->searchPaths()) { + const QFileInfo fi(FileInfo::resolvePath( + FileInfo::resolvePath(searchPath, + QLatin1String("imports")), + importPath)); + if (fi.isDir()) { + // ### versioning, qbsdir file, etc. + const QString &resultPath = fi.absoluteFilePath(); + collectPrototypesAndJsCollections(resultPath, as, + toCodeLocation(m_file->filePath(), import->importIdToken)); + found = true; + break; + } + } + } + if (Q_UNLIKELY(!found)) { + throw ErrorInfo(Tr::tr("import %1 not found") + .arg(importUri.join(QLatin1Char('.'))), + toCodeLocation(m_file->filePath(), import->fileNameToken)); + } + } +} + +Version ASTImportsHandler::readImportVersion(const QString &str, const CodeLocation &location) +{ + const Version v = Version::fromString(str); + if (Q_UNLIKELY(!v.isValid())) + throw ErrorInfo(Tr::tr("Cannot parse version number in import statement."), location); + if (Q_UNLIKELY(v.patchLevel() != 0)) { + throw ErrorInfo(Tr::tr("Version number in import statement cannot have more than " + "two components."), location); + } + return v; +} + +bool ASTImportsHandler::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 ASTImportsHandler::checkImportVersion(const QbsQmlJS::AST::SourceLocation &versionToken) const +{ + if (!versionToken.length) + return; + const QString importVersionString + = m_file->content().mid(versionToken.offset, versionToken.length); + const Version importVersion = readImportVersion(importVersionString, + toCodeLocation(m_file->filePath(), versionToken)); + if (Q_UNLIKELY(importVersion != BuiltinDeclarations::instance().languageVersion())) + throw ErrorInfo(Tr::tr("Incompatible qbs language version %1. This is version %2.").arg( + importVersionString, + BuiltinDeclarations::instance().languageVersion().toString()), + toCodeLocation(m_file->filePath(), versionToken)); + +} + +void ASTImportsHandler::collectPrototypes(const QString &path, const QString &as) +{ + QStringList fileNames; // Yes, file *names*. + if (m_visitorState.findDirectoryEntries(path, &fileNames)) { + foreach (const QString &fileName, fileNames) + addPrototype(fileName, path + QLatin1Char('/') + fileName, as, false); + return; + } + + QDirIterator dirIter(path, QStringList(QLatin1String("*.qbs"))); + while (dirIter.hasNext()) { + const QString filePath = dirIter.next(); + const QString fileName = dirIter.fileName(); + if (addPrototype(fileName, filePath, as, true)) + fileNames << fileName; + } + m_visitorState.cacheDirectoryEntries(path, fileNames); + +} + +void ASTImportsHandler::collectPrototypesAndJsCollections(const QString &path, const QString &as, + const CodeLocation &location) +{ + collectPrototypes(path, as); + QDirIterator dirIter(path, QStringList(QLatin1String("*.js"))); + while (dirIter.hasNext()) { + dirIter.next(); + JsImport &jsImport = m_jsImports[as]; + if (jsImport.scopeName.isNull()) { + jsImport.scopeName = as; + jsImport.location = location; + } + jsImport.filePaths.append(dirIter.filePath()); + } +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/astimportshandler.h b/src/lib/corelib/language/astimportshandler.h new file mode 100644 index 00000000..5a8d656c --- /dev/null +++ b/src/lib/corelib/language/astimportshandler.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_ASTIMPORTSHANDLER_H +#define QBS_ASTIMPORTSHANDLER_H + +#include "forward_decls.h" + +#include + +#include +#include +#include + +namespace qbs { +class CodeLocation; + +namespace Internal { +class ItemReaderVisitorState; +class JsImport; +class Logger; +class Version; + +class ASTImportsHandler +{ +public: + ASTImportsHandler(ItemReaderVisitorState &visitorState, Logger &logger, + const FileContextPtr &file); + + void handleImports(const QbsQmlJS::AST::UiImportList *uiImportList); + + QHash typeNameFileMap() const { return m_typeNameToFile; } + +private: + static Version readImportVersion(const QString &str, const CodeLocation &location); + + bool addPrototype(const QString &fileName, const QString &filePath, const QString &as, + bool needsCheck); + void checkImportVersion(const QbsQmlJS::AST::SourceLocation &versionToken) const; + void collectPrototypes(const QString &path, const QString &as); + void collectPrototypesAndJsCollections(const QString &path, const QString &as, + const CodeLocation &location); + void handleImport(const QbsQmlJS::AST::UiImport *import); + + ItemReaderVisitorState &m_visitorState; + Logger &m_logger; + const FileContextPtr &m_file; + const QString m_directory; + QHash m_typeNameToFile; + QSet m_importAsNames; + + using JsImportsHash = QHash; + JsImportsHash m_jsImports; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard diff --git a/src/lib/corelib/language/astpropertiesitemhandler.cpp b/src/lib/corelib/language/astpropertiesitemhandler.cpp new file mode 100644 index 00000000..faa04060 --- /dev/null +++ b/src/lib/corelib/language/astpropertiesitemhandler.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "astpropertiesitemhandler.h" + +#include "item.h" +#include "value.h" + +#include +#include +#include + +namespace qbs { +namespace Internal { + +ASTPropertiesItemHandler::ASTPropertiesItemHandler(Item *parentItem) : m_parentItem(parentItem) +{ +} + +void ASTPropertiesItemHandler::handlePropertiesItems() +{ + // TODO: Simply forbid Properties items to have child items and get rid of this check. + if (m_parentItem->type() != ItemType::Properties) + setupAlternatives(); +} + +void ASTPropertiesItemHandler::setupAlternatives() +{ + auto it = m_parentItem->m_children.begin(); + while (it != m_parentItem->m_children.end()) { + Item * const child = *it; + if (child->type() == ItemType::Properties) { + handlePropertiesBlock(child); + it = m_parentItem->m_children.erase(it); + } else { + ++it; + } + } +} + +class PropertiesBlockConverter +{ +public: + PropertiesBlockConverter(const QString &condition, const QString &overrideListProperties, + Item *propertiesBlockContainer, const Item *propertiesBlock) + : m_propertiesBlockContainer(propertiesBlockContainer) + , m_propertiesBlock(propertiesBlock) + { + m_alternative.condition = condition; + m_alternative.overrideListProperties = overrideListProperties; + } + + void apply() + { + doApply(m_propertiesBlockContainer, m_propertiesBlock); + } + +private: + JSSourceValue::Alternative m_alternative; + Item * const m_propertiesBlockContainer; + const Item * const m_propertiesBlock; + + void doApply(Item *outer, const Item *inner) + { + for (auto it = inner->properties().constBegin(); + it != inner->properties().constEnd(); ++it) { + if (inner == m_propertiesBlock + && (it.key() == QLatin1String("condition") + || it.key() == QLatin1String("overrideListProperties"))) { + continue; + } + if (it.value()->type() == Value::ItemValueType) { + ItemValuePtr outerVal = outer->itemProperty(it.key()); + if (!outerVal) { + outerVal = ItemValue::create(Item::create(outer->pool()), true); + outer->setProperty(it.key(), outerVal); + } + doApply(outerVal->item(), it.value().staticCast()->item()); + } else if (it.value()->type() == Value::JSSourceValueType) { + const ValuePtr outerVal = outer->property(it.key()); + if (Q_UNLIKELY(outerVal && outerVal->type() != Value::JSSourceValueType)) { + throw ErrorInfo(Tr::tr("Incompatible value type in unconditional value at %1.") + .arg(outerVal->location().toString())); + } + doApply(it.key(), outer, outerVal.staticCast(), + it.value().staticCast()); + } else { + QBS_CHECK(!"Unexpected value type in conditional value."); + } + } + } + + void doApply(const QString &propertyName, Item *item, JSSourceValuePtr value, + const JSSourceValuePtr &conditionalValue) + { + QBS_ASSERT(!value || value->file() == conditionalValue->file(), return); + if (!value) { + value = JSSourceValue::create(true); + value->setFile(conditionalValue->file()); + item->setProperty(propertyName, value); + static const QString baseKeyword = QLatin1String("base"); + value->setSourceCode(QStringRef(&baseKeyword)); + value->setSourceUsesBaseFlag(); + } + m_alternative.value = conditionalValue; + value->addAlternative(m_alternative); + } +}; + +static QString getPropertyString(const Item *propertiesItem, const QString &name) +{ + const ValuePtr value = propertiesItem->property(name); + if (!value) { + if (name == QLatin1String("condition")) { + throw ErrorInfo(Tr::tr("Properties.condition must be provided."), + propertiesItem->location()); + } + return QLatin1String("false"); + } + if (Q_UNLIKELY(value->type() != Value::JSSourceValueType)) { + throw ErrorInfo(Tr::tr("Properties.%1 must be a value binding.").arg(name), + propertiesItem->location()); + } + if (name == QLatin1String("overrideListProperties")) { + const Item *parent = propertiesItem->parent(); + while (parent) { + if (parent->type() == ItemType::Product) + break; + parent = parent->parent(); + } + if (!parent) { + throw ErrorInfo(Tr::tr("Properties.overrideListProperties can only be set " + "in a Product item.")); + } + + } + const JSSourceValuePtr srcval = value.staticCast(); + return srcval->sourceCodeForEvaluation(); +} + +void ASTPropertiesItemHandler::handlePropertiesBlock(const Item *propertiesItem) +{ + const QString condition = getPropertyString(propertiesItem, QLatin1String("condition")); + const QString overrideListProperties = getPropertyString(propertiesItem, + QLatin1String("overrideListProperties")); + PropertiesBlockConverter(condition, overrideListProperties, m_parentItem, + propertiesItem).apply(); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/astpropertiesitemhandler.h b/src/lib/corelib/language/astpropertiesitemhandler.h new file mode 100644 index 00000000..413512ee --- /dev/null +++ b/src/lib/corelib/language/astpropertiesitemhandler.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_ASTPROPERTIESITEMHANDLER_H +#define QBS_ASTPROPERTIESITEMHANDLER_H + +namespace qbs { +namespace Internal { +class Item; + +class ASTPropertiesItemHandler +{ +public: + ASTPropertiesItemHandler(Item *parentItem); + + void handlePropertiesItems(); + +private: + void setupAlternatives(); + void handlePropertiesBlock(const Item *propertiesItem); + + Item * const m_parentItem; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/language/asttools.cpp b/src/lib/corelib/language/asttools.cpp new file mode 100644 index 00000000..93c9689e --- /dev/null +++ b/src/lib/corelib/language/asttools.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "asttools.h" +#include + +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, const 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()); +} + +QStringRef textRefOf(const QString &source, QbsQmlJS::AST::Node *node) +{ + const quint32 firstBegin = node->firstSourceLocation().begin(); + return source.midRef(firstBegin, node->lastSourceLocation().end() - firstBegin); +} + +} // 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 00000000..3f3d9360 --- /dev/null +++ b/src/lib/corelib/language/asttools.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ASTTOOLS_H +#define QBS_ASTTOOLS_H + +#include +#include +#include + +namespace qbs { +namespace Internal { + +QStringList toStringList(QbsQmlJS::AST::UiQualifiedId *qid); +CodeLocation toCodeLocation(const QString &filePath, const QbsQmlJS::AST::SourceLocation &location); +QString textOf(const QString &source, QbsQmlJS::AST::Node *node); +QStringRef textRefOf(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 00000000..9d319251 --- /dev/null +++ b/src/lib/corelib/language/builtindeclarations.cpp @@ -0,0 +1,482 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "builtindeclarations.h" + +#include "deprecationinfo.h" + +#include +#include +#include + +#include + +#include +#include + +namespace qbs { +namespace Internal { + +class AClassWithPublicConstructor : public BuiltinDeclarations { }; +Q_GLOBAL_STATIC(AClassWithPublicConstructor, theInstance) + +const char QBS_LANGUAGE_VERSION[] = "1.0"; + +BuiltinDeclarations::BuiltinDeclarations() + : m_languageVersion(Version::fromString(QLatin1String(QBS_LANGUAGE_VERSION))) + , m_typeMap(std::initializer_list>({ + { QLatin1String("Artifact"), ItemType::Artifact }, + { QLatin1String("Depends"), ItemType::Depends }, + { QLatin1String("Export"), ItemType::Export }, + { QLatin1String("FileTagger"), ItemType::FileTagger }, + { QLatin1String("Group"), ItemType::Group }, + { QLatin1String("Module"), ItemType::Module }, + { QLatin1String("Probe"), ItemType::Probe }, + { QLatin1String("Product"), ItemType::Product }, + { QLatin1String("Project"), ItemType::Project }, + { QLatin1String("Properties"), ItemType::Properties }, // Callers have to handle the SubProject case. + { QLatin1String("PropertyOptions"), ItemType::PropertyOptions }, + { QLatin1String("Rule"), ItemType::Rule }, + { QLatin1String("Scanner"), ItemType::Scanner }, + { QLatin1String("SubProject"), ItemType::SubProject }, + { QLatin1String("Transformer"), ItemType::Transformer } + })) +{ + addArtifactItem(); + addDependsItem(); + addExportItem(); + addFileTaggerItem(); + addGroupItem(); + addModuleItem(); + addProbeItem(); + addProductItem(); + addProjectItem(); + addPropertiesItem(); + addPropertyOptionsItem(); + addRuleItem(); + addSubprojectItem(); + addTransformerItem(); + addScannerItem(); +} + +const BuiltinDeclarations &BuiltinDeclarations::instance() +{ + return *theInstance; +} + +Version BuiltinDeclarations::languageVersion() const +{ + return m_languageVersion; +} + +QStringList BuiltinDeclarations::allTypeNames() const +{ + return m_typeMap.keys(); +} + +ItemDeclaration BuiltinDeclarations::declarationsForType(ItemType type) const +{ + return m_builtins.value(type); +} + +ItemType BuiltinDeclarations::typeForName(const QString &typeName, + const CodeLocation location) const +{ + const auto it = m_typeMap.constFind(typeName); + if (it == m_typeMap.constEnd()) + throw ErrorInfo(Tr::tr("Unexpected item type '%1'.").arg(typeName), location); + return it.value(); +} + +QString BuiltinDeclarations::nameForType(ItemType itemType) const +{ + // Iterating is okay here, as this mapping is not used in hot code paths. + if (itemType == ItemType::PropertiesInSubProject) + return QLatin1String("Properties"); + for (auto it = m_typeMap.constBegin(); it != m_typeMap.constEnd(); ++it) { + if (it.value() == itemType) + return it.key(); + } + QBS_CHECK(false); + return QString(); +} + +void BuiltinDeclarations::insert(const ItemDeclaration &decl) +{ + m_builtins.insert(decl.type(), decl); +} + +static PropertyDeclaration conditionProperty() +{ + PropertyDeclaration decl(QLatin1String("condition"), PropertyDeclaration::Boolean); + decl.setInitialValueSource(QLatin1String("true")); + return decl; +} + +static PropertyDeclaration alwaysRunProperty() +{ + PropertyDeclaration decl(QLatin1String("alwaysRun"), PropertyDeclaration::Boolean); + decl.setInitialValueSource(QLatin1String("false")); + return decl; +} + +static PropertyDeclaration nameProperty() +{ + return PropertyDeclaration(QLatin1String("name"), PropertyDeclaration::String); +} + +static PropertyDeclaration buildDirProperty() +{ + return PropertyDeclaration(QLatin1String("buildDirectory"), PropertyDeclaration::Path); +} + +static PropertyDeclaration prepareScriptProperty() +{ + PropertyDeclaration decl(QLatin1String("prepare"), PropertyDeclaration::Variant, + PropertyDeclaration::PropertyNotAvailableInConfig); + decl.setFunctionArgumentNames( + QStringList() + << QLatin1String("project") << QLatin1String("product") + << QLatin1String("inputs") << QLatin1String("outputs") + << QLatin1String("input") << QLatin1String("output")); + return decl; +} + +void BuiltinDeclarations::addArtifactItem() +{ + ItemDeclaration item(ItemType::Artifact); + PropertyDeclaration conditionDecl = conditionProperty(); + conditionDecl.setDeprecationInfo(DeprecationInfo(Version(1, 4), Tr::tr("If you need " + "dynamic artifacts, use the Rule.outputArtifacts script instead of Artifact items."))); + item << conditionDecl; + PropertyDeclaration fileNameDecl(QLatin1String("fileName"), PropertyDeclaration::String); + fileNameDecl.setDeprecationInfo(DeprecationInfo(Version(1, 4), + Tr::tr("Please use 'filePath' instead."))); + item << fileNameDecl; + item << PropertyDeclaration(QLatin1String("filePath"), PropertyDeclaration::String); + item << PropertyDeclaration(QLatin1String("fileTags"), PropertyDeclaration::StringList); + PropertyDeclaration decl(QLatin1String("alwaysUpdated"), PropertyDeclaration::Boolean); + decl.setInitialValueSource(QLatin1String("true")); + item << decl; + insert(item); +} + +void BuiltinDeclarations::addDependsItem() +{ + ItemDeclaration item(ItemType::Depends); + item << conditionProperty(); + item << nameProperty(); + item << PropertyDeclaration(QLatin1String("submodules"), PropertyDeclaration::StringList); + PropertyDeclaration requiredDecl(QLatin1String("required"), PropertyDeclaration::Boolean); + requiredDecl.setInitialValueSource(QLatin1String("true")); + item << requiredDecl; + item << PropertyDeclaration(QLatin1String("versionAtLeast"), PropertyDeclaration::String); + item << PropertyDeclaration(QLatin1String("versionBelow"), PropertyDeclaration::String); + PropertyDeclaration profileDecl(QLatin1String("profiles"), PropertyDeclaration::StringList); + profileDecl.setInitialValueSource(QLatin1String("[product.profile]")); + item << profileDecl; + item << PropertyDeclaration(QLatin1String("productTypes"), PropertyDeclaration::StringList); + PropertyDeclaration limitDecl(QLatin1String("limitToSubProject"), PropertyDeclaration::Boolean); + limitDecl.setInitialValueSource(QLatin1String("false")); + item << limitDecl; + insert(item); +} + +void BuiltinDeclarations::addExportItem() +{ + addModuleLikeItem(ItemType::Export); +} + +void BuiltinDeclarations::addFileTaggerItem() +{ + ItemDeclaration item(ItemType::FileTagger); + item << PropertyDeclaration(QLatin1String("patterns"), PropertyDeclaration::StringList); + item << PropertyDeclaration(QLatin1String("fileTags"), PropertyDeclaration::StringList); + insert(item); +} + +void BuiltinDeclarations::addGroupItem() +{ + ItemDeclaration item(ItemType::Group); + item.setAllowedChildTypes({ ItemType::Group }); + item << conditionProperty(); + item << PropertyDeclaration(QLatin1String("name"), PropertyDeclaration::String, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("files"), PropertyDeclaration::PathList, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("fileTagsFilter"), PropertyDeclaration::StringList, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("excludeFiles"), PropertyDeclaration::PathList, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("fileTags"), PropertyDeclaration::StringList, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("prefix"), PropertyDeclaration::String, + PropertyDeclaration::PropertyNotAvailableInConfig); + PropertyDeclaration declaration; + declaration.setName(QLatin1String("overrideTags")); + declaration.setType(PropertyDeclaration::Boolean); + declaration.setFlags(PropertyDeclaration::PropertyNotAvailableInConfig); + declaration.setInitialValueSource(QLatin1String("true")); + item << declaration; + insert(item); +} + +void BuiltinDeclarations::addModuleItem() +{ + addModuleLikeItem(ItemType::Module); +} + +void BuiltinDeclarations::addModuleLikeItem(ItemType type) +{ + ItemDeclaration item(type); + item.setAllowedChildTypes(ItemDeclaration::TypeNames() + << ItemType::Group + << ItemType::Depends + << ItemType::FileTagger + << ItemType::Rule + << ItemType::Probe + << ItemType::PropertyOptions + << ItemType::Scanner); + item << nameProperty(); + item << conditionProperty(); + item << PropertyDeclaration(QLatin1String("setupBuildEnvironment"), + PropertyDeclaration::Variant, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("setupRunEnvironment"), + PropertyDeclaration::Variant, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("validate"), + PropertyDeclaration::Boolean, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("additionalProductTypes"), + PropertyDeclaration::StringList); + item << PropertyDeclaration(QLatin1String("version"), PropertyDeclaration::String); + PropertyDeclaration presentDecl(QLatin1String("present"), PropertyDeclaration::Boolean); + presentDecl.setInitialValueSource(QLatin1String("true")); + item << presentDecl; + insert(item); +} + +void BuiltinDeclarations::addProbeItem() +{ + ItemDeclaration item(ItemType::Probe); + item << conditionProperty(); + PropertyDeclaration foundProperty(QLatin1String("found"), PropertyDeclaration::Boolean); + foundProperty.setInitialValueSource(QLatin1String("false")); + item << foundProperty; + item << PropertyDeclaration(QLatin1String("configure"), PropertyDeclaration::Variant, + PropertyDeclaration::PropertyNotAvailableInConfig); + insert(item); +} + +void BuiltinDeclarations::addProductItem() +{ + ItemDeclaration item(ItemType::Product); + item.setAllowedChildTypes(ItemDeclaration::TypeNames() + << ItemType::Depends + << ItemType::Group + << ItemType::FileTagger + << ItemType::Export + << ItemType::Probe + << ItemType::PropertyOptions + << ItemType::Rule); + item << conditionProperty(); + PropertyDeclaration decl(QLatin1String("type"), PropertyDeclaration::StringList); + decl.setInitialValueSource(QLatin1String("[]")); + item << decl; + item << nameProperty(); + decl = PropertyDeclaration(QLatin1String("builtByDefault"), PropertyDeclaration::Boolean); + decl.setInitialValueSource(QLatin1String("true")); + item << decl; + decl = PropertyDeclaration(QLatin1String("profiles"), PropertyDeclaration::StringList); + decl.setInitialValueSource(QLatin1String("[project.profile]")); + item << decl; + item << PropertyDeclaration(QLatin1String("profile"), PropertyDeclaration::String); // Internal + decl = PropertyDeclaration(QLatin1String("targetName"), PropertyDeclaration::String); + decl.setInitialValueSource(QLatin1String("new String(name)" + ".replace(/[/\\\\?%*:|\"<>]/g, '_').valueOf()")); + item << buildDirProperty(); + item << decl; + decl = PropertyDeclaration(QLatin1String("destinationDirectory"), PropertyDeclaration::String); + decl.setInitialValueSource(QStringLiteral("buildDirectory")); + item << decl; + item << PropertyDeclaration(QLatin1String("consoleApplication"), + PropertyDeclaration::Boolean); + item << PropertyDeclaration(QLatin1String("files"), PropertyDeclaration::PathList, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("excludeFiles"), PropertyDeclaration::PathList, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("qbsSearchPaths"), + PropertyDeclaration::StringList); + item << PropertyDeclaration(QLatin1String("version"), PropertyDeclaration::String); + insert(item); +} + +void BuiltinDeclarations::addProjectItem() +{ + ItemDeclaration item(ItemType::Project); + item.setAllowedChildTypes(ItemDeclaration::TypeNames() + << ItemType::Project + << ItemType::PropertyOptions + << ItemType::SubProject + << ItemType::Product + << ItemType::FileTagger + << ItemType::Rule); + item << nameProperty(); + item << conditionProperty(); + item << buildDirProperty(); + item << PropertyDeclaration(QLatin1String("minimumQbsVersion"), PropertyDeclaration::String); + item << PropertyDeclaration(QLatin1String("sourceDirectory"), PropertyDeclaration::Path); + item << PropertyDeclaration(QLatin1String("profile"), PropertyDeclaration::String); + item << PropertyDeclaration(QLatin1String("references"), PropertyDeclaration::PathList, + PropertyDeclaration::PropertyNotAvailableInConfig); + item << PropertyDeclaration(QLatin1String("qbsSearchPaths"), + PropertyDeclaration::StringList, PropertyDeclaration::PropertyNotAvailableInConfig); + insert(item); +} + +void BuiltinDeclarations::addPropertiesItem() +{ + insert(ItemDeclaration(ItemType::Properties)); +} + +void BuiltinDeclarations::addPropertyOptionsItem() +{ + ItemDeclaration item(ItemType::PropertyOptions); + item << nameProperty(); + item << PropertyDeclaration(QLatin1String("allowedValues"), PropertyDeclaration::Variant); + item << PropertyDeclaration(QLatin1String("description"), PropertyDeclaration::String); + item << PropertyDeclaration(QLatin1String("removalVersion"), PropertyDeclaration::String); + insert(item); +} + +void BuiltinDeclarations::addRuleItem() +{ + ItemDeclaration item(ItemType::Rule); + item.setAllowedChildTypes(ItemDeclaration::TypeNames() + << ItemType::Artifact); + item << conditionProperty(); + item << alwaysRunProperty(); + PropertyDeclaration decl(QLatin1String("multiplex"), PropertyDeclaration::Boolean); + decl.setInitialValueSource(QLatin1String("false")); + item << decl; + item << PropertyDeclaration(QLatin1String("name"), PropertyDeclaration::String); + item << PropertyDeclaration(QLatin1String("inputs"), PropertyDeclaration::StringList); + item << PropertyDeclaration(QLatin1String("outputFileTags"), PropertyDeclaration::StringList); + decl = PropertyDeclaration(QLatin1String("outputArtifacts"), PropertyDeclaration::Variant, + PropertyDeclaration::PropertyNotAvailableInConfig); + decl.setFunctionArgumentNames( + QStringList() + << QLatin1String("project") << QLatin1String("product") + << QLatin1String("inputs") << QLatin1String("input")); + item << decl; + decl = PropertyDeclaration(QLatin1String("usings"), PropertyDeclaration::StringList); + decl.setDeprecationInfo(DeprecationInfo(Version(1, 5), + Tr::tr("Use 'inputsFromDependencies' instead"))); + item << decl; + item << PropertyDeclaration(QLatin1String("inputsFromDependencies"), PropertyDeclaration::StringList); + item << PropertyDeclaration(QLatin1String("auxiliaryInputs"), + PropertyDeclaration::StringList); + item << PropertyDeclaration(QLatin1String("excludedAuxiliaryInputs"), + PropertyDeclaration::StringList); + item << PropertyDeclaration(QLatin1String("explicitlyDependsOn"), + PropertyDeclaration::StringList); + item << prepareScriptProperty(); + insert(item); +} + +void BuiltinDeclarations::addSubprojectItem() +{ + ItemDeclaration item(ItemType::SubProject); + item.setAllowedChildTypes(ItemDeclaration::TypeNames() + << ItemType::Project // needed, because we're adding this internally + << ItemType::PropertiesInSubProject + << ItemType::PropertyOptions); + item << PropertyDeclaration(QLatin1String("filePath"), PropertyDeclaration::Path); + PropertyDeclaration inheritProperty; + inheritProperty.setName(QLatin1String("inheritProperties")); + inheritProperty.setType(PropertyDeclaration::Boolean); + inheritProperty.setInitialValueSource(QLatin1String("true")); + item << inheritProperty; + insert(item); +} + +void BuiltinDeclarations::addTransformerItem() +{ + ItemDeclaration item(ItemType::Transformer); + item.setDeprecationInfo(DeprecationInfo(Version(1, 7), Tr::tr("Use the 'Rule' item instead."))); + item.setAllowedChildTypes(ItemDeclaration::TypeNames() + << ItemType::Artifact); + item << conditionProperty(); + item << alwaysRunProperty(); + item << PropertyDeclaration(QLatin1String("inputs"), PropertyDeclaration::PathList); + item << prepareScriptProperty(); + item << PropertyDeclaration(QLatin1String("explicitlyDependsOn"), + PropertyDeclaration::StringList); + insert(item); +} + +void BuiltinDeclarations::addScannerItem() +{ + ItemDeclaration item(ItemType::Scanner); + item << conditionProperty(); + item << PropertyDeclaration(QLatin1String("inputs"), PropertyDeclaration::StringList); + PropertyDeclaration recursive(QLatin1String("recursive"), PropertyDeclaration::Boolean); + recursive.setInitialValueSource(QLatin1String("false")); + item << recursive; + PropertyDeclaration searchPaths(QLatin1String("searchPaths"), PropertyDeclaration::StringList); + searchPaths.setFunctionArgumentNames( + QStringList() + << QLatin1String("project") + << QLatin1String("product") + << QLatin1String("input")); + item << searchPaths; + PropertyDeclaration scan(QLatin1String("scan"), PropertyDeclaration::Variant, + PropertyDeclaration::PropertyNotAvailableInConfig); + scan.setFunctionArgumentNames( + QStringList() + << QLatin1String("project") + << QLatin1String("product") + << QLatin1String("input")); + item << scan; + 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 00000000..0548bbd4 --- /dev/null +++ b/src/lib/corelib/language/builtindeclarations.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_BUILTINDECLARATIONS_H +#define QBS_BUILTINDECLARATIONS_H + +#include "itemdeclaration.h" +#include "itemtype.h" + +#include +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +class BuiltinDeclarations +{ +public: + static const BuiltinDeclarations &instance(); + + Version languageVersion() const; + QStringList allTypeNames() const; + ItemDeclaration declarationsForType(ItemType type) const; + ItemType typeForName(const QString &typeName, + const CodeLocation location = CodeLocation()) const; + QString nameForType(ItemType itemType) const; + +protected: + BuiltinDeclarations(); + +private: + void insert(const ItemDeclaration &decl); + void addArtifactItem(); + void addDependsItem(); + void addExportItem(); + void addFileTaggerItem(); + void addGroupItem(); + void addModuleItem(); + void addModuleLikeItem(ItemType type); + void addProbeItem(); + void addProductItem(); + void addProjectItem(); + void addPropertiesItem(); + void addPropertyOptionsItem(); + void addRuleItem(); + void addSubprojectItem(); + void addTransformerItem(); + void addScannerItem(); + + const Version m_languageVersion; + QMap m_builtins; + const QHash m_typeMap; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_BUILTINDECLARATIONS_H diff --git a/src/lib/corelib/language/deprecationinfo.h b/src/lib/corelib/language/deprecationinfo.h new file mode 100644 index 00000000..c52c1f99 --- /dev/null +++ b/src/lib/corelib/language/deprecationinfo.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_DEPRECATIONINFO_H +#define QBS_DEPRECATIONINFO_H + +#include + +#include + +namespace qbs { +namespace Internal { + +class DeprecationInfo +{ +public: + explicit DeprecationInfo(const Version &removalVersion, + const QString &additionalUserInfo = QString()) + : m_removalVersion(removalVersion) + , m_additionalUserInfo(additionalUserInfo) + {} + DeprecationInfo() {} + + bool isValid() const { return m_removalVersion.isValid(); } + Version removalVersion() const { return m_removalVersion; } + QString additionalUserInfo() const { return m_additionalUserInfo; } + +private: + Version m_removalVersion; + QString m_additionalUserInfo; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/language/evaluationdata.h b/src/lib/corelib/language/evaluationdata.h new file mode 100644 index 00000000..03f47b1a --- /dev/null +++ b/src/lib/corelib/language/evaluationdata.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_EVALUATIONDATA_H +#define QBS_EVALUATIONDATA_H + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class Evaluator; +class Item; + +class EvaluationData +{ +public: + Evaluator *evaluator; + const Item *item; + mutable QHash valueCache; +}; + +} // 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 00000000..8e4fdde5 --- /dev/null +++ b/src/lib/corelib/language/evaluator.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "evaluator.h" + +#include "evaluationdata.h" +#include "evaluatorscriptclass.h" +#include "filecontext.h" +#include "filetags.h" +#include "item.h" +#include "scriptengine.h" +#include "value.h" + +#include +#include +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { + +Evaluator::Evaluator(ScriptEngine *scriptEngine, Logger &logger) + : m_scriptEngine(scriptEngine) + , m_scriptClass(new EvaluatorScriptClass(scriptEngine, logger)) +{ +} + +Evaluator::~Evaluator() +{ + QHash::iterator it = m_scriptValueMap.begin(); + for (; it != m_scriptValueMap.end(); ++it) { + EvaluationData *data = attachedPointer(*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::value(const Item *item, const QString &name, bool *propertyWasSet) +{ + QScriptValue v; + evaluateProperty(&v, item, name, propertyWasSet); + return v; +} + +bool Evaluator::boolValue(const Item *item, const QString &name, bool defaultValue, + bool *propertyWasSet) +{ + QScriptValue v; + if (!evaluateProperty(&v, item, name, propertyWasSet)) + return defaultValue; + return v.toBool(); +} + +FileTags Evaluator::fileTagsValue(const Item *item, const QString &name, bool *propertySet) +{ + return FileTags::fromStringList(stringListValue(item, name, propertySet)); +} + +QString Evaluator::stringValue(const Item *item, const QString &name, + const QString &defaultValue, bool *propertyWasSet) +{ + QScriptValue v; + if (!evaluateProperty(&v, item, name, propertyWasSet)) + return defaultValue; + return v.toString(); +} + +static QStringList toStringList(const QScriptValue &scriptValue) +{ + 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; + 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); +} + +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); + attachPointerTo(scriptValue, edata); + return scriptValue; +} + +void Evaluator::onItemPropertyChanged(Item *item) +{ + EvaluationData *data = attachedPointer(m_scriptValueMap.value(item)); + if (data) + data->valueCache.clear(); +} + +void Evaluator::onItemDestroyed(Item *item) +{ + delete attachedPointer(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; + QString message; + QString filePath; + int line = -1; + const QScriptValue value = scriptValue.isError() ? scriptValue + : m_scriptEngine->uncaughtException(); + if (value.isError()) { + QScriptValue v = value.property(QStringLiteral("message")); + if (v.isString()) + message = v.toString(); + v = value.property(QStringLiteral("fileName")); + if (v.isString()) + filePath = v.toString(); + v = value.property(QStringLiteral("lineNumber")); + if (v.isNumber()) + line = v.toInt32(); + } else { + message = value.toString(); + const ValueConstPtr value = item->property(name); + if (value) { + const CodeLocation location = value->location(); + filePath = location.filePath(); + line = location.line(); + } + } + throw ErrorInfo(message, CodeLocation(filePath, line, -1, false)); +} + +bool Evaluator::evaluateProperty(QScriptValue *result, const Item *item, const QString &name, + bool *propertyWasSet) +{ + *result = property(item, name); + handleEvaluationError(item, name, *result); + if (!result->isValid() || result->isUndefined()) { + if (propertyWasSet) + *propertyWasSet = false; + return false; + } + if (propertyWasSet) + *propertyWasSet = true; + return true; +} + +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()); + return result; +} + +QScriptValue Evaluator::importScope(const FileContextConstPtr &file) +{ + QScriptValue &result = m_importScopeMap[file]; + if (result.isObject()) + return result; + result = m_scriptEngine->newObject(); + m_scriptEngine->import(file, result); + JsExtensions::setupExtensions(file->jsExtensions(), result); + return result; +} + +void Evaluator::setCachingEnabled(bool enabled) +{ + m_scriptClass->setValueCacheEnabled(enabled); +} + +PropertyDependencies Evaluator::propertyDependencies() const +{ + return m_scriptClass->propertyDependencies(); +} + +void Evaluator::clearPropertyDependencies() +{ + m_scriptClass->clearPropertyDependencies(); +} + +} // 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 00000000..e20c0f8d --- /dev/null +++ b/src/lib/corelib/language/evaluator.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_EVALUATOR_H +#define QBS_EVALUATOR_H + +#include "forward_decls.h" +#include "itemobserver.h" +#include "qualifiedid.h" + +#include +#include + +namespace qbs { +namespace Internal { +class EvaluatorScriptClass; +class FileTags; +class Logger; +class ScriptEngine; + +class Evaluator : private ItemObserver +{ + friend class SVConverter; + +public: + Evaluator(ScriptEngine *scriptEngine, Logger &logger); + virtual ~Evaluator(); + + ScriptEngine *engine() const { return m_scriptEngine; } + QScriptValue property(const Item *item, const QString &name); + + QScriptValue value(const Item *item, const QString &name, bool *propertySet = 0); + bool boolValue(const Item *item, const QString &name, bool defaultValue = false, + bool *propertyWasSet = 0); + FileTags fileTagsValue(const Item *item, const QString &name, bool *propertySet = 0); + 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); + QScriptValue importScope(const FileContextConstPtr &file); + + void setCachingEnabled(bool enabled); + + PropertyDependencies propertyDependencies() const; + void clearPropertyDependencies(); + + void handleEvaluationError(const Item *item, const QString &name, + const QScriptValue &scriptValue); +private: + void onItemPropertyChanged(Item *item); + void onItemDestroyed(Item *item); + bool evaluateProperty(QScriptValue *result, const Item *item, const QString &name, + bool *propertyWasSet); + + ScriptEngine *m_scriptEngine; + EvaluatorScriptClass *m_scriptClass; + mutable QHash m_scriptValueMap; + mutable QHash m_fileScopeMap; + mutable QHash m_importScopeMap; +}; + +class EvalCacheEnabler +{ +public: + EvalCacheEnabler(Evaluator *evaluator) : m_evaluator(evaluator) + { + m_evaluator->setCachingEnabled(true); + } + + ~EvalCacheEnabler() { m_evaluator->setCachingEnabled(false); } + +private: + Evaluator * const m_evaluator; +}; + +} // 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 00000000..0a2e96c1 --- /dev/null +++ b/src/lib/corelib/language/evaluatorscriptclass.cpp @@ -0,0 +1,616 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "evaluatorscriptclass.h" + +#include "evaluationdata.h" +#include "evaluator.h" +#include "filecontext.h" +#include "item.h" +#include "scriptengine.h" +#include "propertydeclaration.h" +#include "value.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class SVConverter : ValueHandler +{ + EvaluatorScriptClass * const scriptClass; + ScriptEngine * const engine; + QScriptContext * const scriptContext; + const QScriptValue * const object; + const ValuePtr &valuePtr; + const Item * const itemOfProperty; + const QScriptString * const propertyName; + const EvaluationData * const data; + QScriptValue * const result; + QStack * const sourceValueStack; + char pushedScopesCount; + +public: + + SVConverter(EvaluatorScriptClass *esc, const QScriptValue *obj, const ValuePtr &v, + const Item *_itemOfProperty, const QScriptString *propertyName, const EvaluationData *data, + QScriptValue *result, QStack *sourceValueStack) + : scriptClass(esc) + , engine(static_cast(esc->engine())) + , scriptContext(esc->engine()->currentContext()) + , object(obj) + , valuePtr(v) + , itemOfProperty(_itemOfProperty) + , propertyName(propertyName) + , data(data) + , result(result) + , sourceValueStack(sourceValueStack) + , 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(); + const PropertyDeclaration::Type type + = itemOfProperty->propertyDeclaration(propertyName->toString()).type(); + const bool isArray = type == PropertyDeclaration::StringList + || type == PropertyDeclaration::PathList + || type == PropertyDeclaration::Variant; + QScriptValue valueToSet = scriptValue; + if (isArray) { + if (!valueToSet.isValid() || valueToSet.isUndefined()) + valueToSet = engine->newArray(); + } else if (!valueToSet.isValid()) { + valueToSet = engine->undefinedValue(); + } + extraScope->setProperty(conveniencePropertyName, valueToSet); + } + + 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) { + // We have to differentiate between module instances and normal items here. + // + // The module instance case: + // Product { + // property bool something: true + // Properties { + // condition: something + // cpp.defines: ["ABC"] + // } + // } + // + // data->item points to cpp and the condition's scope chain must contain cpp's + // scope, which is the item where cpp is instantiated. The scope chain must not + // contain data->item itself. + // + // The normal item case: + // Product { + // property bool something: true + // property string value: "ABC" + // Properties { + // condition: something + // value: "DEF" + // } + // } + // + // data->item points to the product and the condition's scope chain must contain + // the product item. + conditionScopeItem = data->item->type() == ItemType::ModuleInstance + ? data->item->scope() : data->item; + conditionScope = data->evaluator->scriptValue(conditionScopeItem); + QBS_ASSERT(conditionScope.isObject(), return); + conditionFileScope = data->evaluator->fileScope(value->file()); + } + engine->currentContext()->pushScope(conditionFileScope); + pushItemScopes(conditionScopeItem); + if (alternative->value->definingItem()) + pushItemScopes(alternative->value->definingItem()); + engine->currentContext()->pushScope(conditionScope); + engine->currentContext()->pushScope(data->evaluator->importScope(value->file())); + const QScriptValue cr = engine->evaluate(alternative->condition); + const QScriptValue overrides = engine->evaluate(alternative->overrideListProperties); + engine->currentContext()->popScope(); + engine->currentContext()->popScope(); + engine->currentContext()->popScope(); + popScopes(); + if (engine->hasErrorOrException(cr)) { + *result = engine->lastErrorValue(cr); + return; + } + if (cr.toBool()) { + // condition is true, let's use the value of this alternative + if (alternative->value->sourceUsesOuter() && !outerItem) { + // Clone value but without alternatives. + JSSourceValuePtr outerValue = JSSourceValue::create(); + outerValue->setFile(value->file()); + outerValue->setHasFunctionForm(value->hasFunctionForm()); + outerValue->setSourceCode(value->sourceCode()); + outerValue->setBaseValue(value->baseValue()); + if (value->sourceUsesBase()) + outerValue->setSourceUsesBaseFlag(); + outerValue->setLocation(value->line(), value->column()); + outerItem = Item::create(data->item->pool()); + outerItem->setProperty(propertyName->toString(), outerValue); + } + if (overrides.toBool()) + value->setIsExclusiveListValue(); + value = alternative->value.data(); + break; + } + } + + QScriptValue extraScope; + if (value->sourceUsesBase()) { + QScriptValue baseValue; + if (value->baseValue()) { + SVConverter converter(scriptClass, object, value->baseValue(), itemOfProperty, + propertyName, data, &baseValue, sourceValueStack); + converter.start(); + } + setupConvenienceProperty(QLatin1String("base"), &extraScope, baseValue); + } + if (value->sourceUsesOuter() && outerItem) { + const QScriptValue v = data->evaluator->property(outerItem, *propertyName); + if (engine->hasErrorOrException(v)) { + *result = engine->lastErrorValue(v); + return; + } + setupConvenienceProperty(QLatin1String("outer"), &extraScope, v); + } + if (value->sourceUsesOriginal()) { + QScriptValue originalValue; + if (data->item->propertyDeclaration(propertyName->toString()).isScalar()) { + const Item *item = itemOfProperty; + while (item->type() == ItemType::ModuleInstance) + item = item->prototype(); + SVConverter converter(scriptClass, object, item->property(*propertyName), item, + propertyName, data, &originalValue, sourceValueStack); + converter.start(); + } else { + originalValue = engine->newArray(0); + } + setupConvenienceProperty(QLatin1String("original"), &extraScope, originalValue); + } + + pushScope(data->evaluator->fileScope(value->file())); + pushItemScopes(data->item); + if (itemOfProperty && itemOfProperty->type() != ItemType::ModuleInstance) { + // Own properties of module instances must not have the instance itself in the scope. + pushScope(*object); + } + if (value->definingItem()) + pushItemScopes(value->definingItem()); + pushScope(extraScope); + pushScope(data->evaluator->importScope(value->file())); + *result = engine->evaluate(value->sourceCodeForEvaluation(), value->file()->filePath(), + value->line()); + popScopes(); + } + + void handle(ItemValue *value) + { + *result = data->evaluator->scriptValue(value->item()); + if (!result->isValid()) + qDebug() << "SVConverter returned invalid script value."; + } + + void handle(VariantValue *variantValue) + { + *result = scriptClass->engine()->toScriptValue(variantValue->value()); + } +}; + +bool debugProperties = false; + +enum QueryPropertyType +{ + QPTDefault, + QPTParentProperty +}; + +EvaluatorScriptClass::EvaluatorScriptClass(ScriptEngine *scriptEngine, Logger &logger) + : QScriptClass(scriptEngine) + , m_logger(logger) + , m_valueCacheEnabled(false) +{ + Q_UNUSED(m_logger); +} + +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) + qDebug() << "[SC] queryProperty " << object.objectId() << " " << name; + + EvaluationData *const data = attachedPointer(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) + qDebug() << "[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; + m_queryResult.itemOfProperty = item; + return HandlesReadAccess; + } + } + + if (!ignoreParent && data->item && data->item->parent()) { + if (debugProperties) + qDebug() << "[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) + qDebug() << "[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()); +} + +void EvaluatorScriptClass::collectValuesFromNextChain(const EvaluationData *data, QScriptValue *result, + const QString &propertyName, const ValuePtr &value) +{ + QScriptValueList lst; + QSet oldNextChain = m_currentNextChain; + for (ValuePtr next = value; next; next = next->next()) + m_currentNextChain.insert(next.data()); + + for (ValuePtr next = value; next; next = next->next()) { + QScriptValue v = data->evaluator->property(next->definingItem(), propertyName); + ScriptEngine * const se = static_cast(engine()); + if (se->hasErrorOrException(v)) { + *result = se->lastErrorValue(v); + return; + } + if (v.isUndefined()) + continue; + lst << v; + if (next->type() == Value::JSSourceValueType + && next.staticCast()->isExclusiveListValue()) { + lst = lst.mid(lst.length() - 2); + break; + } + } + m_currentNextChain = oldNextChain; + + *result = engine()->newArray(); + quint32 k = 0; + for (int i = 0; i < lst.count(); ++i) { + const QScriptValue &v = lst.at(i); + QBS_ASSERT(!v.isError(), continue); + if (v.isArray()) { + const quint32 vlen = v.property(QStringLiteral("length")).toInt32(); + for (quint32 j = 0; j < vlen; ++j) + result->setProperty(k++, v.property(j)); + } else { + result->setProperty(k++, v); + } + } +} + +static QString overriddenSourceDirectory(const Item *item) +{ + const VariantValuePtr v = item->variantProperty(QLatin1String("_qbs_sourceDir")); + return v ? v->value().toString() : QString(); +} + +static void makeTypeError(const ErrorInfo &error, QScriptValue &v) +{ + v = v.engine()->currentContext()->throwError(QScriptContext::TypeError, + error.toString()); +} + +static void makeTypeError(const PropertyDeclaration &decl, const CodeLocation &location, + QScriptValue &v) +{ + const ErrorInfo error(Tr::tr("Value assigned to property '%1' does not have type '%2'.") + .arg(decl.name(), decl.typeString()), location); + makeTypeError(error, v); +} + +static void convertToPropertyType(const Item *item, const PropertyDeclaration& decl, + const Value *value, QScriptValue &v) +{ + if (value->type() == Value::VariantValueType && v.isUndefined() && !decl.isScalar()) { + v = v.engine()->newArray(); // QTBUG-51237 + return; + } + + if (v.isUndefined() || v.isError()) + return; + QString srcDir; + const CodeLocation &location = value->location(); + switch (decl.type()) { + case PropertyDeclaration::UnknownType: + case PropertyDeclaration::Variant: + break; + case PropertyDeclaration::Boolean: + if (!v.isBool()) + v = v.toBool(); + break; + case PropertyDeclaration::Integer: + if (!v.isNumber()) + makeTypeError(decl, location, v); + break; + case PropertyDeclaration::Path: + { + if (!v.isString()) { + makeTypeError(decl, location, v); + break; + } + const QString srcDir = overriddenSourceDirectory(item); + if (!srcDir.isEmpty()) + v = v.engine()->toScriptValue(FileInfo::resolvePath(srcDir, v.toString())); + break; + } + case PropertyDeclaration::String: + if (!v.isString()) + makeTypeError(decl, location, v); + break; + case PropertyDeclaration::PathList: + srcDir = overriddenSourceDirectory(item); + // Fall-through. + case PropertyDeclaration::StringList: + { + if (!v.isArray()) { + QScriptValue x = v.engine()->newArray(1); + x.setProperty(0, v); + v = x; + } + const quint32 c = v.property(QLatin1String("length")).toUInt32(); + for (quint32 i = 0; i < c; ++i) { + QScriptValue elem = v.property(i); + if (elem.isUndefined()) { + ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' is undefined. " + "String expected.").arg(i).arg(decl.name()), location); + makeTypeError(error, v); + break; + } + if (elem.isNull()) { + ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' is null. " + "String expected.").arg(i).arg(decl.name()), location); + makeTypeError(error, v); + break; + } + if (!elem.isString()) { + ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' does not have " + "string type.").arg(i).arg(decl.name()), location); + makeTypeError(error, v); + break; + } + if (srcDir.isEmpty()) + continue; + elem = v.engine()->toScriptValue(FileInfo::resolvePath(srcDir, elem.toString())); + v.setProperty(i, elem); + } + break; + } + } +} + +class PropertyStackManager +{ +public: + PropertyStackManager(const Item *itemOfProperty, const QScriptString &name, const Value *value, + QStack &requestedProperties, + PropertyDependencies &propertyDependencies) + : m_requestedProperties(requestedProperties) + { + if (value->type() == Value::JSSourceValueType + && (itemOfProperty->type() == ItemType::ModuleInstance + || itemOfProperty->type() == ItemType::Module)) { + const VariantValueConstPtr varValue + = itemOfProperty->variantProperty(QLatin1String("name")); + // QBS_CHECK(varValue); + // TODO: Check why the base module sometimes has no name. Code suggests it has to have one. + if (varValue) { + m_stackUpdate = true; + const QualifiedId fullPropName + = QualifiedId::fromString(varValue->value().toString()) << name.toString(); + if (!requestedProperties.isEmpty()) + propertyDependencies[fullPropName].insert(requestedProperties.top()); + m_requestedProperties.push(fullPropName); + } + } + } + + ~PropertyStackManager() + { + if (m_stackUpdate) + m_requestedProperties.pop(); + } + +private: + QStack &m_requestedProperties; + bool m_stackUpdate = false; +}; + +QScriptValue EvaluatorScriptClass::property(const QScriptValue &object, const QScriptString &name, + uint id) +{ + const EvaluationData *data = m_queryResult.data; + const Item * const itemOfProperty = m_queryResult.itemOfProperty; + m_queryResult.data = 0; + m_queryResult.itemOfProperty = 0; + QBS_ASSERT(data, return QScriptValue()); + + const QueryPropertyType qpt = static_cast(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) + qDebug() << "[SC] property " << name; + + PropertyStackManager propStackmanager(itemOfProperty, name, value.data(), + m_requestedProperties, m_propertyDependencies); + + QScriptValue result; + if (m_valueCacheEnabled) { + result = data->valueCache.value(name); + if (result.isValid()) { + if (debugProperties) + qDebug() << "[SC] cache hit " << name << ": " << resultToString(result); + return result; + } + } + + if (value->next() && !m_currentNextChain.contains(value.data())) { + collectValuesFromNextChain(data, &result, name.toString(), value); + } else { + SVConverter converter(this, &object, value, itemOfProperty, &name, data, &result, + &m_sourceValueStack); + converter.start(); + + const PropertyDeclaration decl = data->item->propertyDeclaration(name.toString()); + convertToPropertyType(data->item, decl, value.data(), result); + } + + if (debugProperties) + qDebug() << "[SC] cache miss " << name << ": " << resultToString(result); + if (m_valueCacheEnabled) + data->valueCache.insert(name, result); + return result; +} + +void EvaluatorScriptClass::setValueCacheEnabled(bool enabled) +{ + m_valueCacheEnabled = enabled; +} + +} // 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 00000000..5739f7d8 --- /dev/null +++ b/src/lib/corelib/language/evaluatorscriptclass.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_EVALUATORSCRIPTCLASS_H +#define QBS_EVALUATORSCRIPTCLASS_H + +#include "forward_decls.h" +#include "qualifiedid.h" + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QScriptContext; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { +class EvaluationData; +class Item; +class ScriptEngine; + +class EvaluatorScriptClass : public QScriptClass +{ +public: + EvaluatorScriptClass(ScriptEngine *scriptEngine, Logger &logger); + + QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id); + QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + + void setValueCacheEnabled(bool enabled); + + PropertyDependencies propertyDependencies() const { return m_propertyDependencies; } + void clearPropertyDependencies() { m_propertyDependencies.clear(); } + +private: + QueryFlags queryItemProperty(const EvaluationData *data, + const QString &name, + bool ignoreParent = false); + static QString resultToString(const QScriptValue &scriptValue); + void collectValuesFromNextChain(const EvaluationData *data, QScriptValue *result, const QString &propertyName, const ValuePtr &value); + + struct QueryResult + { + QueryResult() + : data(0), itemOfProperty(0) + {} + + bool isNull() const + { + return !data; + } + + const EvaluationData *data; + const Item *itemOfProperty; // The item that owns the property. + ValuePtr value; + }; + QueryResult m_queryResult; + Logger &m_logger; + bool m_valueCacheEnabled; + QStack m_sourceValueStack; + QSet m_currentNextChain; + PropertyDependencies m_propertyDependencies; + QStack m_requestedProperties; +}; + +} // 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 00000000..27107ff4 --- /dev/null +++ b/src/lib/corelib/language/filecontext.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filecontext.h" + +#include "item.h" + +namespace qbs { +namespace Internal { + +FileContext::FileContext() + : m_idScope(0) +{ +} + +FileContextPtr FileContext::create() +{ + return FileContextPtr(new FileContext); +} + +void FileContext::ensureIdScope(ItemPool *itemPool) +{ + if (!m_idScope) + m_idScope = Item::create(itemPool, ItemType::IdScope); +} + +} // 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 00000000..148f3008 --- /dev/null +++ b/src/lib/corelib/language/filecontext.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_FILECONTEXT_H +#define QBS_FILECONTEXT_H + +#include "filecontextbase.h" +#include "forward_decls.h" + +#include + +namespace qbs { +namespace Internal { +class Item; +class ItemPool; + +class FileContext : public FileContextBase +{ +public: + static FileContextPtr create(); + + void setContent(const QString &content) { m_content = content; } + const QString &content() const { return m_content; } + + Item *idScope() const { return m_idScope; } + void ensureIdScope(ItemPool *itemPool); + +private: + FileContext(); + + QString m_content; + Item *m_idScope; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_FILECONTEXT_H diff --git a/src/lib/corelib/language/filecontextbase.cpp b/src/lib/corelib/language/filecontextbase.cpp new file mode 100644 index 00000000..96a383fa --- /dev/null +++ b/src/lib/corelib/language/filecontextbase.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filecontextbase.h" + +#include + +namespace qbs { +namespace Internal { + +QString FileContextBase::dirPath() const +{ + return FileInfo::path(m_filePath); +} + +FileContextBase::FileContextBase(const FileContextBase &other) + : m_filePath(other.m_filePath) + , m_jsImports(other.m_jsImports) + , m_jsExtensions(other.m_jsExtensions) + , m_searchPaths(other.m_searchPaths) +{ +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/filecontextbase.h b/src/lib/corelib/language/filecontextbase.h new file mode 100644 index 00000000..7c60da4d --- /dev/null +++ b/src/lib/corelib/language/filecontextbase.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_FILECONTEXTBASE_H +#define QBS_FILECONTEXTBASE_H + +#include "jsimports.h" + +namespace qbs { +namespace Internal { + +class FileContextBase +{ +public: + void setFilePath(const QString &filePath) { m_filePath = filePath; } + QString filePath() const { return m_filePath; } + + void addJsImport(const JsImport &jsImport) { m_jsImports << jsImport; } + JsImports jsImports() const { return m_jsImports; } + + void addJsExtension(const QString &extension) { m_jsExtensions << extension; } + QStringList jsExtensions() const { return m_jsExtensions; } + + void setSearchPaths(const QStringList &paths) { m_searchPaths = paths; } + QStringList searchPaths() const { return m_searchPaths; } + + QString dirPath() const; + +protected: + FileContextBase() {} + FileContextBase(const FileContextBase &other); + + QString m_filePath; + JsImports m_jsImports; + QStringList m_jsExtensions; + QStringList m_searchPaths; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_FILECONTEXTBASE_H diff --git a/src/lib/corelib/language/filetags.cpp b/src/lib/corelib/language/filetags.cpp new file mode 100644 index 00000000..c451a24b --- /dev/null +++ b/src/lib/corelib/language/filetags.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filetags.h" +#include + +#include + +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; +} + +void FileTags::store(PersistentPool &pool) const +{ + pool.storeStringList(toStringList()); +} + +void FileTags::load(PersistentPool &pool) +{ + *this = fromStringList(pool.idLoadStringList()); +} + +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; +} + +} // 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 00000000..63e92e44 --- /dev/null +++ b/src/lib/corelib/language/filetags.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_FILETAGS_H +#define QBS_FILETAGS_H + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { +class PersistentPool; + +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 +{ +public: + QStringList toStringList() const; + static FileTags fromStringList(const QStringList &strings); + bool matches(const FileTags &other) const; + + void store(PersistentPool &pool) const; + void load(PersistentPool &pool); +}; + +LogWriter operator <<(LogWriter w, 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 00000000..0c45f009 --- /dev/null +++ b/src/lib/corelib/language/forward_decls.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_LANG_FORWARD_DECLS_H +#define QBS_LANG_FORWARD_DECLS_H + +#include + +namespace qbs { +namespace Internal { + +class Value; +typedef QSharedPointer ValuePtr; +typedef QSharedPointer ValueConstPtr; + +class ItemValue; +typedef QSharedPointer ItemValuePtr; +typedef QSharedPointer ItemValueConstPtr; + +class JSSourceValue; +typedef QSharedPointer JSSourceValuePtr; +typedef QSharedPointer JSSourceValueConstPtr; + +class VariantValue; +typedef QSharedPointer VariantValuePtr; +typedef QSharedPointer VariantValueConstPtr; + +class FileContext; +typedef QSharedPointer FileContextPtr; +typedef QSharedPointer FileContextConstPtr; + +class FileContextBase; +typedef QSharedPointer FileContextBasePtr; +typedef QSharedPointer FileContextBaseConstPtr; + +class Probe; +typedef QSharedPointer ProbePtr; +typedef QSharedPointer ProbeConstPtr; + +class PropertyMapInternal; +typedef QSharedPointer PropertyMapPtr; +typedef QSharedPointer PropertyMapConstPtr; + +class FileTagger; +typedef QSharedPointer FileTaggerPtr; +typedef QSharedPointer FileTaggerConstPtr; + +class ResolvedProduct; +typedef QSharedPointer ResolvedProductPtr; +typedef QSharedPointer ResolvedProductConstPtr; + +class ResolvedProject; +typedef QSharedPointer ResolvedProjectPtr; +typedef QSharedPointer ResolvedProjectConstPtr; + +class TopLevelProject; +typedef QSharedPointer TopLevelProjectPtr; +typedef QSharedPointer TopLevelProjectConstPtr; + +class ResolvedFileContext; +typedef QSharedPointer ResolvedFileContextPtr; +typedef QSharedPointer ResolvedFileContextConstPtr; + +class Rule; +typedef QSharedPointer RulePtr; +typedef QSharedPointer RuleConstPtr; + +class ResolvedScanner; +typedef QSharedPointer ResolvedScannerPtr; +typedef QSharedPointer ResolvedScannerConstPtr; + +class SourceArtifactInternal; +typedef QSharedPointer SourceArtifactPtr; +typedef QSharedPointer SourceArtifactConstPtr; + +class ScriptFunction; +typedef QSharedPointer ScriptFunctionPtr; +typedef QSharedPointer ScriptFunctionConstPtr; + +class RuleArtifact; +typedef QSharedPointer RuleArtifactPtr; +typedef QSharedPointer RuleArtifactConstPtr; + +class ResolvedModule; +typedef QSharedPointer ResolvedModulePtr; +typedef QSharedPointer ResolvedModuleConstPtr; + +class ResolvedGroup; +typedef QSharedPointer GroupPtr; +typedef QSharedPointer GroupConstPtr; + +class ArtifactProperties; +typedef QSharedPointer ArtifactPropertiesPtr; +typedef QSharedPointer 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 00000000..e737cfcf --- /dev/null +++ b/src/lib/corelib/language/functiondeclaration.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_FUNCTIONDECLARATION_H +#define QBS_FUNCTIONDECLARATION_H + +#include + +#include + +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 00000000..0edc885c --- /dev/null +++ b/src/lib/corelib/language/identifiersearch.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "identifiersearch.h" +#include + +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 00000000..f3e26750 --- /dev/null +++ b/src/lib/corelib/language/identifiersearch.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_IDENTIFIERSEARCHVISITOR_H +#define QBS_IDENTIFIERSEARCHVISITOR_H + +#include +#include +#include +#include + +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 m_requests; + int m_numberOfFoundIds; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_IDENTIFIERSEARCHVISITOR_H diff --git a/src/lib/corelib/language/item.cpp b/src/lib/corelib/language/item.cpp new file mode 100644 index 00000000..f1d7fefc --- /dev/null +++ b/src/lib/corelib/language/item.cpp @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "item.h" + +#include "builtindeclarations.h" +#include "deprecationinfo.h" +#include "filecontext.h" +#include "itemobserver.h" +#include "itempool.h" +#include "value.h" + +#include +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { + +Item::Item(ItemPool *pool, ItemType type) + : m_pool(pool) + , m_propertyObserver(0) + , m_prototype(0) + , m_scope(0) + , m_outerItem(0) + , m_parent(0) + , m_type(type) +{ +} + +Item::~Item() +{ + if (m_propertyObserver) + m_propertyObserver->onItemDestroyed(this); +} + +Item *Item::create(ItemPool *pool, ItemType type) +{ + return pool->allocateItem(type); +} + +Item *Item::clone() const +{ + Item *dup = create(pool()); + dup->m_id = m_id; + dup->m_type = m_type; + 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_file = m_file; + dup->m_propertyDeclarations = m_propertyDeclarations; + dup->m_functions = m_functions; + dup->m_modules = m_modules; + + dup->m_children.reserve(m_children.count()); + foreach (const Item *child, m_children) { + Item *clonedChild = child->clone(); + clonedChild->m_parent = dup; + dup->m_children.append(clonedChild); + } + + for (PropertyMap::const_iterator it = m_properties.constBegin(); it != m_properties.constEnd(); + ++it) { + dup->m_properties.insert(it.key(), it.value()->clone()); + } + + return dup; +} + +bool Item::hasProperty(const QString &name) const +{ + const Item *item = this; + do { + if (item->m_properties.contains(name)) + return true; + item = item->m_prototype; + } while (item); + return false; +} + +bool Item::hasOwnProperty(const QString &name) const +{ + return m_properties.contains(name); +} + +ValuePtr Item::property(const QString &name) const +{ + ValuePtr value; + const Item *item = this; + do { + if ((value = item->m_properties.value(name))) + break; + item = item->m_prototype; + } while (item); + 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(); + } 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(); +} + +VariantValuePtr Item::variantProperty(const QString &name) const +{ + ValuePtr v = property(name); + if (!v || v->type() != Value::VariantValueType) + return VariantValuePtr(); + return v.staticCast(); +} + +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::addModule(const Item::Module &module) +{ + const auto it = std::lower_bound(m_modules.begin(), m_modules.end(), module); + QBS_CHECK(it == m_modules.end() || (module.name != it->name && module.item != it->item)); + m_modules.insert(it, module); +} + +void Item::setPropertyObserver(ItemObserver *observer) const +{ + QBS_ASSERT(!observer || !m_propertyObserver, return); // warn if accidentally overwritten + m_propertyObserver = observer; +} + +void Item::setProperty(const QString &name, const ValuePtr &value) +{ + m_properties.insert(name, value); + if (m_propertyObserver) + m_propertyObserver->onItemPropertyChanged(this); +} + +void Item::dump() const +{ + dump(0); +} + +bool Item::isPresentModule() const +{ + // Initial value is "true" as JS source, overwritten one is always QVariant(false). + const ValueConstPtr v = property(QLatin1String("present")); + return v && v->type() == Value::JSSourceValueType; +} + +void Item::setupForBuiltinType(Logger &logger) +{ + const BuiltinDeclarations &builtins = BuiltinDeclarations::instance(); + foreach (const PropertyDeclaration &pd, builtins.declarationsForType(type()).properties()) { + m_propertyDeclarations.insert(pd.name(), pd); + const ValuePtr value = m_properties.value(pd.name()); + if (!value) { + if (pd.isDeprecated()) + continue; + JSSourceValuePtr sourceValue = JSSourceValue::create(); + sourceValue->setFile(file()); + static const QString undefinedKeyword = QLatin1String("undefined"); + sourceValue->setSourceCode(pd.initialValueSource().isEmpty() + ? QStringRef(&undefinedKeyword) + : QStringRef(&pd.initialValueSource())); + m_properties.insert(pd.name(), sourceValue); + } else if (pd.isDeprecated()) { + const DeprecationInfo &di = pd.deprecationInfo(); + if (di.removalVersion() <= Version::qbsVersion()) { + QString message = Tr::tr("The property '%1' is no longer valid for %2 items. " + "It was removed in qbs %3.") + .arg(pd.name(), typeName(), di.removalVersion().toString()); + ErrorInfo error(message, value->location()); + if (!di.additionalUserInfo().isEmpty()) + error.append(di.additionalUserInfo()); + throw error; + } + QString warning = Tr::tr("The property '%1' is deprecated and will be removed in " + "qbs %2.").arg(pd.name(), di.removalVersion().toString()); + ErrorInfo error(warning, value->location()); + if (!di.additionalUserInfo().isEmpty()) + error.append(di.additionalUserInfo()); + logger.printWarning(error); + } + } +} + +void Item::copyProperty(const QString &propertyName, Item *target) const +{ + target->setProperty(propertyName, property(propertyName)); +} + +static const char *valueType(const Value *v) +{ + switch (v->type()) { + case Value::JSSourceValueType: return "JS source"; + case Value::ItemValueType: return "Item"; + case Value::VariantValueType: return "Variant"; + } + return ""; // For dumb compilers. +} + +void Item::dump(int indentation) const +{ + const QByteArray indent(indentation, ' '); + qDebug("%stype: %s, pointer value: %p", indent.constData(), qPrintable(m_typeName), this); + if (!m_properties.isEmpty()) + qDebug("%sproperties:", indent.constData()); + for (auto it = m_properties.constBegin(); it != m_properties.constEnd(); ++it) { + const QByteArray nextIndent(indentation + 4, ' '); + qDebug("%skey: %s, value type: %s", nextIndent.constData(), qPrintable(it.key()), + valueType(it.value().data())); + switch (it.value()->type()) { + case Value::JSSourceValueType: + qDebug("%svalue: %s", nextIndent.constData(), + qPrintable(it.value().staticCast()->sourceCodeForEvaluation())); + break; + case Value::ItemValueType: + qDebug("%svalue:", nextIndent.constData()); + it.value().staticCast()->item()->dump(indentation + 8); + break; + case Value::VariantValueType: + qDebug("%svalue: %s", nextIndent.constData(), + qPrintable(it.value().staticCast()->value().toString())); + break; + } + } + if (!m_children.isEmpty()) + qDebug("%schildren:", indent.constData()); + foreach (const Item * const child, m_children) + child->dump(indentation + 4); + if (prototype()) { + qDebug("%sprototype:", indent.constData()); + prototype()->dump(indentation + 4); + } +} + +void Item::removeProperty(const QString &name) +{ + m_properties.remove(name); +} + +Item *Item::child(ItemType type, bool checkForMultiple) const +{ + Item *child = 0; + foreach (Item * const currentChild, children()) { + if (currentChild->type() == type) { + if (!checkForMultiple) + return currentChild; + if (child) { + ErrorInfo error(Tr::tr("Multiple instances of item '%1' found where at most one " + "is allowed.") + .arg(BuiltinDeclarations::instance().nameForType(type))); + error.append(Tr::tr("First item"), child->location()); + error.append(Tr::tr("Second item"), currentChild->location()); + throw error; + } + child = currentChild; + } + } + return child; +} + +void Item::addChild(Item *parent, Item *child) +{ + parent->m_children.append(child); + child->setParent(parent); +} + +void Item::setPropertyDeclaration(const QString &name, const PropertyDeclaration &declaration) +{ + m_propertyDeclarations.insert(name, declaration); +} + +} // 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 00000000..3b4314e7 --- /dev/null +++ b/src/lib/corelib/language/item.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ITEM_H +#define QBS_ITEM_H + +#include "forward_decls.h" +#include "functiondeclaration.h" +#include "itemtype.h" +#include "propertydeclaration.h" +#include "qualifiedid.h" +#include +#include +#include +#include + +#include +#include + +namespace qbs { +namespace Internal { +class ItemObserver; +class ItemPool; +class Logger; + +class Item : public QbsQmlJS::Managed +{ + friend class ASTPropertiesItemHandler; + friend class ItemPool; + friend class ItemReaderASTVisitor; + Q_DISABLE_COPY(Item) + Item(ItemPool *pool, ItemType type); + +public: + ~Item(); + + struct Module + { + Module() + : item(0), isProduct(false), required(true) + {} + + QualifiedId name; + Item *item; + bool isProduct; + bool required; + VersionRange versionRange; + }; + typedef QList Modules; + typedef QMap PropertyDeclarationMap; + typedef QMap PropertyMap; + + static Item *create(ItemPool *pool, ItemType type = ItemType::Unknown); + Item *clone() const; + ItemPool *pool() const { return m_pool; } + + const QString &id() const { return m_id; } + const QString &typeName() const { return m_typeName; } + const CodeLocation &location() const { return m_location; } + Item *prototype() const { return m_prototype; } + Item *scope() const { return m_scope; } + Item *outerItem() const { return m_outerItem; } + Item *parent() const { return m_parent; } + const FileContextPtr &file() const { return m_file; } + QList children() const { return m_children; } + Item *child(ItemType type, bool checkForMultiple = true) const; + const PropertyMap &properties() const { return m_properties; } + const PropertyDeclarationMap &propertyDeclarations() const { return m_propertyDeclarations; } + PropertyDeclaration propertyDeclaration(const QString &name) const; + const Modules &modules() const { return m_modules; } + void addModule(const Module &module); + void removeModules() { m_modules.clear(); } + void setModules(const Modules &modules) { m_modules = modules; } + + ItemType type() const { return m_type; } + void setType(ItemType type) { m_type = type; } + + 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; + VariantValuePtr variantProperty(const QString &name) const; + void setPropertyObserver(ItemObserver *observer) const; + void setProperty(const QString &name, const ValuePtr &value); + void setProperties(const PropertyMap &props) { m_properties = props; } + void removeProperty(const QString &name); + void setPropertyDeclaration(const QString &name, const PropertyDeclaration &declaration); + void setTypeName(const QString &name) { m_typeName = name; } + void setLocation(const CodeLocation &location) { m_location = location; } + void setPrototype(Item *prototype) { m_prototype = prototype; } + void setFile(const FileContextPtr &file) { m_file = file; } + void setScope(Item *item) { m_scope = item; } + void setOuterItem(Item *item) { m_outerItem = item; } + void setChildren(const QList &children) { m_children = children; } + void setParent(Item *item) { m_parent = item; } + static void addChild(Item *parent, Item *child); + void dump() const; + bool isPresentModule() const; + void setupForBuiltinType(Logger &logger); + void copyProperty(const QString &propertyName, Item *target) const; + + void setDelayedError(const ErrorInfo &e) { m_delayedError = e; } + ErrorInfo delayedError() const { return m_delayedError; } + +private: + void dump(int indentation) const; + + ItemPool *m_pool; + mutable ItemObserver *m_propertyObserver; + QString m_id; + QString m_typeName; + CodeLocation m_location; + Item *m_prototype; + Item *m_scope; + Item *m_outerItem; + Item *m_parent; + QList m_children; + FileContextPtr m_file; + PropertyMap m_properties; + PropertyDeclarationMap m_propertyDeclarations; + QList m_functions; + Modules m_modules; + ErrorInfo m_delayedError; + ItemType m_type; +}; + +inline bool operator<(const Item::Module &m1, const Item::Module &m2) { return m1.name < m2.name; } +inline bool operator==(const Item::Module &m1, const Item::Module &m2) { return m1.item == m2.item; } + +} // 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 00000000..956b6b06 --- /dev/null +++ b/src/lib/corelib/language/itemdeclaration.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "itemdeclaration.h" + +namespace qbs { +namespace Internal { + +ItemDeclaration::ItemDeclaration(ItemType type) + : m_type(type) +{ +} + +ItemDeclaration &ItemDeclaration::operator<<(const PropertyDeclaration &decl) +{ + m_properties.append(decl); + return *this; +} + +bool ItemDeclaration::isChildTypeAllowed(ItemType type) const +{ + if (m_type > ItemType::LastActualItem || type > ItemType::LastActualItem) + return true; + return m_allowedChildTypes.contains(type); +} + +} // 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 00000000..3d42b275 --- /dev/null +++ b/src/lib/corelib/language/itemdeclaration.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ITEMDECLARATION_H +#define QBS_ITEMDECLARATION_H + +#include "deprecationinfo.h" +#include "itemtype.h" +#include "propertydeclaration.h" + +#include +#include + +namespace qbs { +namespace Internal { + +class ItemDeclaration +{ +public: + ItemDeclaration(ItemType type = ItemType::Unknown); + + ItemType type() const { return m_type; } + + typedef QList Properties; + void setProperties(const Properties &props) { m_properties = props; } + const Properties &properties() const { return m_properties; } + + void setDeprecationInfo(const DeprecationInfo &di) { m_deprecationInfo = di; } + DeprecationInfo deprecationInfo() const { return m_deprecationInfo; } + + ItemDeclaration &operator<<(const PropertyDeclaration &decl); + + typedef QSet TypeNames; + void setAllowedChildTypes(const TypeNames &typeNames) { m_allowedChildTypes = typeNames; } + const TypeNames &allowedChildTypes() const { return m_allowedChildTypes; } + bool isChildTypeAllowed(ItemType type) const; + +private: + ItemType m_type; + Properties m_properties; + TypeNames m_allowedChildTypes; + DeprecationInfo m_deprecationInfo; +}; + +} // 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 00000000..bce68255 --- /dev/null +++ b/src/lib/corelib/language/itemobserver.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ITEMOBSERVER_H +#define QBS_ITEMOBSERVER_H + +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 00000000..e651051a --- /dev/null +++ b/src/lib/corelib/language/itempool.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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(const ItemType &type) +{ + Item *item = new (&m_pool) Item(this, type); + 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 00000000..63c94edb --- /dev/null +++ b/src/lib/corelib/language/itempool.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ITEMPOOL_H +#define QBS_ITEMPOOL_H + +#include + +#include + +namespace qbs { +namespace Internal { + +class Item; +enum class ItemType; + +class ItemPool +{ + Q_DISABLE_COPY(ItemPool) +public: + ItemPool(); + ~ItemPool(); + + Item *allocateItem(const ItemType &type); + +private: + QbsQmlJS::MemoryPool m_pool; + typedef QList 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 00000000..4f6a315b --- /dev/null +++ b/src/lib/corelib/language/itemreader.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "itemreader.h" + +#include "itemreadervisitorstate.h" + +#include + +namespace qbs { +namespace Internal { + +ItemReader::ItemReader(Logger &logger) : m_visitorState(new ItemReaderVisitorState(logger)) +{ +} + +ItemReader::~ItemReader() +{ + delete m_visitorState; +} + +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(); +} + +QStack ItemReader::extraSearchPathsStack() const +{ + return m_extraSearchPaths; +} + +QStringList ItemReader::searchPaths() const +{ + QStringList paths; + for (int i = m_extraSearchPaths.count(); --i >= 0;) + paths += m_extraSearchPaths.at(i); + paths += m_searchPaths; + return paths; +} + +Item *ItemReader::readFile(const QString &filePath) +{ + AccumulatingTimer readFileTimer(m_elapsedTime != -1 ? &m_elapsedTime : nullptr); + return m_visitorState->readFile(filePath, searchPaths(), m_pool); +} + +QSet ItemReader::filesRead() const +{ + return m_visitorState->filesRead(); +} + +void ItemReader::setEnableTiming(bool on) +{ + m_elapsedTime = on ? 0 : -1; +} + +} // 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 00000000..20cfc0e1 --- /dev/null +++ b/src/lib/corelib/language/itemreader.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ITEMREADER_H +#define QBS_ITEMREADER_H + +#include "forward_decls.h" +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +class Item; +class ItemPool; +class ItemReaderVisitorState; + +/* + * 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 +{ +public: + ItemReader(Logger &logger); + ~ItemReader(); + + void setPool(ItemPool *pool) { m_pool = pool; } + void setSearchPaths(const QStringList &searchPaths); + void pushExtraSearchPaths(const QStringList &extraSearchPaths); + void popExtraSearchPaths(); + QStack extraSearchPathsStack() const; + void setExtraSearchPathsStack(const QStack &s) { m_extraSearchPaths = s; } + void clearExtraSearchPathsStack() { m_extraSearchPaths.clear(); } + QStringList searchPaths() const; + + Item *readFile(const QString &filePath); + + QSet filesRead() const; + + void setEnableTiming(bool on); + qint64 elapsedTime() const { return m_elapsedTime; } + +private: + ItemPool *m_pool = nullptr; + QStringList m_searchPaths; + QStack m_extraSearchPaths; + ItemReaderVisitorState * const m_visitorState; + qint64 m_elapsedTime = -1; +}; + +} // 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 00000000..79f66687 --- /dev/null +++ b/src/lib/corelib/language/itemreaderastvisitor.cpp @@ -0,0 +1,381 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "itemreaderastvisitor.h" + +#include "astimportshandler.h" +#include "astpropertiesitemhandler.h" +#include "asttools.h" +#include "builtindeclarations.h" +#include "filecontext.h" +#include "identifiersearch.h" +#include "item.h" +#include "itemreadervisitorstate.h" +#include "value.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace QbsQmlJS; + +namespace qbs { +namespace Internal { + +ItemReaderASTVisitor::ItemReaderASTVisitor(ItemReaderVisitorState &visitorState, + const FileContextPtr &file, ItemPool *itemPool, Logger &logger) + : m_visitorState(visitorState) + , m_file(file) + , m_itemPool(itemPool) + , m_logger(logger) +{ +} + +bool ItemReaderASTVisitor::visit(AST::UiImportList *uiImportList) +{ + ASTImportsHandler importsHandler(m_visitorState, m_logger, m_file); + importsHandler.handleImports(uiImportList); + m_typeNameToFile = importsHandler.typeNameFileMap(); + return false; +} + +bool ItemReaderASTVisitor::visit(AST::UiObjectDefinition *ast) +{ + const QString typeName = ast->qualifiedTypeNameId->name.toString(); + Item *item = Item::create(m_itemPool); + item->setFile(m_file); + item->setTypeName(typeName); + item->setLocation(toCodeLocation(ast->qualifiedTypeNameId->identifierToken)); + + if (m_item) + Item::addChild(m_item, item); // Add this item to the children of the parent item. + else + m_item = item; // This is the root item. + + const Item *inheritorItem = nullptr; + + // Inheritance resolving, part 1: Find out our actual type name (needed for setting + // up children and alternatives). + const QStringList fullTypeName = toStringList(ast->qualifiedTypeNameId); + const QString baseTypeFileName = m_typeNameToFile.value(fullTypeName); + if (!baseTypeFileName.isEmpty()) { + inheritorItem = m_visitorState.readFile(baseTypeFileName, m_file->searchPaths(), + m_itemPool); + QBS_CHECK(inheritorItem->type() <= ItemType::LastActualItem); + item->setType(inheritorItem->type()); + } else { + const ItemType itemType + = BuiltinDeclarations::instance().typeForName(typeName, item->location()); + checkDeprecationStatus(itemType, typeName, item->location()); + item->setType(itemType); + if (item->type() == ItemType::Properties && item->parent() + && item->parent()->type() == ItemType::SubProject) { + item->setType(ItemType::PropertiesInSubProject); + } + } + + if (ast->initializer) { + qSwap(m_item, item); + ast->initializer->accept(this); + qSwap(m_item, item); + } + + ASTPropertiesItemHandler(item).handlePropertiesItems(); + + // Inheritance resolving, part 2 (depends on alternatives having been set up). + if (inheritorItem) { + inheritItem(item, inheritorItem); + if (inheritorItem->file()->idScope()) { + // Make ids from the derived file visible in the base file. + // ### Do we want to turn off this feature? It's QMLish but kind of strange. + item->file()->ensureIdScope(m_itemPool); + inheritorItem->file()->idScope()->setPrototype(item->file()->idScope()); + } + } else { + // Only the item at the top of the inheritance chain is a built-in item. + // We cannot do this in "part 1", because then the visitor would complain about duplicate + // bindings. + item->setupForBuiltinType(m_logger); + } + + return false; +} + +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(QLatin1Char('.'))), + toCodeLocation(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.setName(ast->name.toString()); + p.setType(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.setFlags(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); + + const JSSourceValuePtr value = JSSourceValue::create(); + value->setFile(m_file); + if (ast->statement) { + handleBindingRhs(ast->statement, value); + const QStringList bindingName(p.name()); + checkDuplicateBinding(m_item, bindingName, ast->colonToken); + } + + m_item->setProperty(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")) { + const auto * const expStmt = AST::cast(ast->statement); + if (Q_UNLIKELY(!expStmt)) + throw ErrorInfo(Tr::tr("id: must be followed by identifier")); + const auto * const idExp = AST::cast(expStmt->expression); + if (Q_UNLIKELY(!idExp || idExp->name.isEmpty())) + throw ErrorInfo(Tr::tr("id: must be followed by identifier")); + m_item->m_id = idExp->name.toString(); + m_file->ensureIdScope(m_itemPool); + ItemValueConstPtr existingId = m_file->idScope()->itemProperty(m_item->id()); + if (existingId) { + ErrorInfo e(Tr::tr("The id '%1' is not unique.").arg(m_item->id())); + e.append(Tr::tr("First occurrence is here."), existingId->item()->location()); + e.append(Tr::tr("Next occurrence is here."), m_item->location()); + throw e; + } + m_file->idScope()->setProperty(m_item->id(), ItemValue::create(m_item)); + return false; + } + + const JSSourceValuePtr value = JSSourceValue::create(); + handleBindingRhs(ast->statement, value); + + Item * const targetItem = targetItemForBinding(bindingName, value); + checkDuplicateBinding(targetItem, bindingName, ast->qualifiedId->identifierToken); + targetItem->setProperty(bindingName.last(), value); + return false; +} + +bool ItemReaderASTVisitor::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_file->content(), ast); + funcNoName.replace(QRegExp(QLatin1String("^(\\s*function\\s*)\\w*")), QLatin1String("(\\1")); + funcNoName.append(QLatin1Char(')')); + f.setSourceCode(funcNoName); + + f.setLocation(toCodeLocation(ast->firstSourceLocation())); + m_item->m_functions += f; + return false; +} + +bool ItemReaderASTVisitor::handleBindingRhs(AST::Statement *statement, + const JSSourceValuePtr &value) +{ + QBS_CHECK(statement); + QBS_CHECK(value); + + if (AST::cast(statement)) + value->m_flags |= JSSourceValue::HasFunctionForm; + + value->setFile(m_file); + value->setSourceCode(textRefOf(m_file->content(), statement)); + value->setLocation(statement->firstSourceLocation().startLine, + statement->firstSourceLocation().startColumn); + + bool usesBase, usesOuter, usesOriginal; + IdentifierSearch idsearch; + idsearch.add(QLatin1String("base"), &usesBase); + idsearch.add(QLatin1String("outer"), &usesOuter); + idsearch.add(QLatin1String("original"), &usesOriginal); + idsearch.start(statement); + if (usesBase) + value->m_flags |= JSSourceValue::SourceUsesBase; + if (usesOuter) + value->m_flags |= JSSourceValue::SourceUsesOuter; + if (usesOriginal) + value->m_flags |= JSSourceValue::SourceUsesOriginal; + return false; +} + +CodeLocation ItemReaderASTVisitor::toCodeLocation(const AST::SourceLocation &location) const +{ + return CodeLocation(m_file->filePath(), location.startLine, location.startColumn); +} + +Item *ItemReaderASTVisitor::targetItemForBinding(const QStringList &bindingName, + const JSSourceValueConstPtr &value) +{ + Item *targetItem = m_item; + const int c = bindingName.count() - 1; + for (int i = 0; i < c; ++i) { + ValuePtr v = targetItem->properties().value(bindingName.at(i)); + if (!v) { + Item *newItem = Item::create(m_itemPool); + v = ItemValue::create(newItem); + targetItem->setProperty(bindingName.at(i), v); + } + if (Q_UNLIKELY(v->type() != Value::ItemValueType)) { + QString msg = Tr::tr("Binding to non-item property."); + throw ErrorInfo(msg, value->location()); + } + targetItem = v.staticCast()->item(); + } + return targetItem; +} + +void ItemReaderASTVisitor::inheritItem(Item *dst, const Item *src) +{ + int insertPos = 0; + for (int i = 0; i < src->m_children.count(); ++i) { + Item *child = src->m_children.at(i); + dst->m_children.insert(insertPos++, child); + child->m_parent = dst; + } + + for (auto it = src->properties().constBegin(); it != src->properties().constEnd(); ++it) { + ValuePtr &v = dst->m_properties[it.key()]; + if (!v) { + v = it.value(); + continue; + } + if (v->type() == Value::ItemValueType && it.value()->type() != Value::ItemValueType) + throw ErrorInfo(Tr::tr("Binding to non-item property."), v->location()); + if (v->type() != it.value()->type()) + continue; + switch (v->type()) { + case Value::JSSourceValueType: { + JSSourceValuePtr sv = v.staticCast(); + QBS_CHECK(!sv->baseValue()); + const JSSourceValuePtr baseValue = it.value().staticCast(); + sv->setBaseValue(baseValue); + for (auto it = sv->m_alternatives.begin(); it != sv->m_alternatives.end(); ++it) + it->value->setBaseValue(baseValue); + break; + } + case Value::ItemValueType: + inheritItem(v.staticCast()->item(), + it.value().staticCast()->item()); + break; + default: + QBS_CHECK(!"unexpected value type"); + } + } + + for (auto it = src->propertyDeclarations().constBegin(); + it != src->propertyDeclarations().constEnd(); ++it) { + dst->setPropertyDeclaration(it.key(), it.value()); + } +} + +void ItemReaderASTVisitor::checkDeprecationStatus(ItemType itemType, const QString &itemName, + const CodeLocation &itemLocation) +{ + const ItemDeclaration itemDecl = BuiltinDeclarations::instance().declarationsForType(itemType); + const DeprecationInfo &di = itemDecl.deprecationInfo(); + if (!di.isValid()) + return; + if (di.removalVersion() <= Version::qbsVersion()) { + QString message = Tr::tr("The item '%1' cannot be used anymore. " + "It was removed in qbs %2.") + .arg(itemName, di.removalVersion().toString()); + ErrorInfo error(message, itemLocation); + if (!di.additionalUserInfo().isEmpty()) + error.append(di.additionalUserInfo()); + throw error; + } + QString warning = Tr::tr("The item '%1' is deprecated and will be removed in " + "qbs %2.").arg(itemName, di.removalVersion().toString()); + ErrorInfo error(warning, itemLocation); + if (!di.additionalUserInfo().isEmpty()) + error.append(di.additionalUserInfo()); + m_logger.printWarning(error); +} + +void ItemReaderASTVisitor::doCheckItemTypes(const Item *item) +{ + const ItemDeclaration decl = BuiltinDeclarations::instance().declarationsForType(item->type()); + foreach (const Item * const child, item->children()) { + if (!decl.isChildTypeAllowed(child->type())) { + throw ErrorInfo(Tr::tr("Items of type '%1' cannot contain items of type '%2'.") + .arg(item->typeName(), child->typeName()), child->location()); + } + doCheckItemTypes(child); + } +} + +} // 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 00000000..96d3f9e3 --- /dev/null +++ b/src/lib/corelib/language/itemreaderastvisitor.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ITEMREADERASTVISITOR_H +#define QBS_ITEMREADERASTVISITOR_H + +#include "forward_decls.h" +#include "itemtype.h" + +#include +#include + +#include +#include + +namespace qbs { +class CodeLocation; + +namespace Internal { +class Item; +class ItemPool; +class ItemReaderVisitorState; + +class ItemReaderASTVisitor : public QbsQmlJS::AST::Visitor +{ +public: + ItemReaderASTVisitor(ItemReaderVisitorState &visitorState, const FileContextPtr &file, + ItemPool *itemPool, Logger &logger); + void checkItemTypes() { doCheckItemTypes(rootItem()); } + + Item *rootItem() const { return m_item; } + +private: + bool visit(QbsQmlJS::AST::UiImportList *uiImportList) override; + bool visit(QbsQmlJS::AST::UiObjectDefinition *ast) override; + bool visit(QbsQmlJS::AST::UiPublicMember *ast) override; + bool visit(QbsQmlJS::AST::UiScriptBinding *ast) override; + bool visit(QbsQmlJS::AST::FunctionDeclaration *ast) override; + + bool handleBindingRhs(QbsQmlJS::AST::Statement *statement, const JSSourceValuePtr &value); + CodeLocation toCodeLocation(const QbsQmlJS::AST::SourceLocation &location) const; + void checkDuplicateBinding(Item *item, const QStringList &bindingName, + const QbsQmlJS::AST::SourceLocation &sourceLocation); + Item *targetItemForBinding(const QStringList &binding, const JSSourceValueConstPtr &value); + static void inheritItem(Item *dst, const Item *src); + void checkDeprecationStatus(ItemType itemType, const QString &itemName, + const CodeLocation &itemLocation); + void doCheckItemTypes(const Item *item); + + ItemReaderVisitorState &m_visitorState; + const FileContextPtr m_file; + ItemPool * const m_itemPool; + Logger &m_logger; + QHash m_typeNameToFile; + Item *m_item = nullptr; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_ITEMREADERASTVISITOR_H diff --git a/src/lib/corelib/language/itemreadervisitorstate.cpp b/src/lib/corelib/language/itemreadervisitorstate.cpp new file mode 100644 index 00000000..58aec11e --- /dev/null +++ b/src/lib/corelib/language/itemreadervisitorstate.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "itemreadervisitorstate.h" + +#include "asttools.h" +#include "filecontext.h" +#include "itemreaderastvisitor.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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 d; +}; + +class ItemReaderVisitorState::ASTCache : public QHash {}; + + +ItemReaderVisitorState::ItemReaderVisitorState(Logger &logger) + : m_logger(logger) + , m_astCache(new ASTCache) +{ + +} + +ItemReaderVisitorState::~ItemReaderVisitorState() +{ + delete m_astCache; +} + +Item *ItemReaderVisitorState::readFile(const QString &filePath, const QStringList &searchPaths, + ItemPool *itemPool) +{ + 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("Cannot open '%1'.").arg(filePath)); + + m_filesRead.insert(filePath); + QTextStream stream(&file); + stream.setCodec("UTF-8"); + const QString &code = stream.readAll(); + QbsQmlJS::Lexer lexer(cacheValue.engine()); + lexer.setCode(code, 1); + QbsQmlJS::Parser parser(cacheValue.engine()); + + file.close(); + if (!parser.parse()) { + QList 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()); + } + + const FileContextPtr file = FileContext::create(); + file->setFilePath(QFileInfo(filePath).absoluteFilePath()); + file->setContent(cacheValue.code()); + file->setSearchPaths(searchPaths); + + ItemReaderASTVisitor astVisitor(*this, file, itemPool, m_logger); + cacheValue.setProcessingFlag(true); + cacheValue.ast()->accept(&astVisitor); + cacheValue.setProcessingFlag(false); + astVisitor.checkItemTypes(); + return astVisitor.rootItem(); +} + +void ItemReaderVisitorState::cacheDirectoryEntries(const QString &dirPath, const QStringList &entries) +{ + m_directoryEntries.insert(dirPath, entries); +} + +bool ItemReaderVisitorState::findDirectoryEntries(const QString &dirPath, QStringList *entries) const +{ + const auto it = m_directoryEntries.constFind(dirPath); + if (it == m_directoryEntries.constEnd()) + return false; + *entries = it.value(); + return true; +} + + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/itemreadervisitorstate.h b/src/lib/corelib/language/itemreadervisitorstate.h new file mode 100644 index 00000000..261436eb --- /dev/null +++ b/src/lib/corelib/language/itemreadervisitorstate.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_ITEMREADERVISITORSTATE_H +#define QBS_ITEMREADERVISITORSTATE_H + +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { +class Item; +class ItemPool; + +class ItemReaderVisitorState +{ +public: + ItemReaderVisitorState(Logger &logger); + ~ItemReaderVisitorState(); + + QSet filesRead() const { return m_filesRead; } + + Item *readFile(const QString &filePath, const QStringList &searchPaths, ItemPool *itemPool); + + void cacheDirectoryEntries(const QString &dirPath, const QStringList &entries); + bool findDirectoryEntries(const QString &dirPath, QStringList *entries) const; + +private: + Logger &m_logger; + QSet m_filesRead; + QHash m_directoryEntries; + + class ASTCache; + ASTCache * const m_astCache; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/language/itemtype.h b/src/lib/corelib/language/itemtype.h new file mode 100644 index 00000000..ad62dddd --- /dev/null +++ b/src/lib/corelib/language/itemtype.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ITEMTYPE_H +#define QBS_ITEMTYPE_H + +#include + +namespace qbs { +namespace Internal { + +enum class ItemType { + // Actual user-visible items. + FirstActualItem, + Artifact = FirstActualItem, + Depends, + Export, + FileTagger, + Group, + Module, + Probe, + Product, + Project, + Properties, + PropertiesInSubProject, + PropertyOptions, + Rule, + Scanner, + SubProject, + Transformer, + LastActualItem = Transformer, + + // Internal items created mainly by the module loader. + IdScope, + ModuleInstance, + ModulePrefix, + Scope, + + Unknown +}; + +inline uint qHash(ItemType t) { return QT_PREPEND_NAMESPACE(qHash)(uint(t)); } + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. + diff --git a/src/lib/corelib/language/jsimports.h b/src/lib/corelib/language/jsimports.h new file mode 100644 index 00000000..7f99af24 --- /dev/null +++ b/src/lib/corelib/language/jsimports.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_JSIMPORTS_H +#define QBS_JSIMPORTS_H + +#include + +#include +#include + +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 filePaths; + CodeLocation location; +}; +inline uint qHash(const JsImport &jsi) { return qHash(jsi.scopeName); } + +typedef QList JsImports; + +inline bool operator==(const JsImport &jsi1, const JsImport &jsi2) +{ + return jsi1.scopeName == jsi2.scopeName && jsi1.filePaths.toSet() == jsi2.filePaths.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 00000000..82d0bb10 --- /dev/null +++ b/src/lib/corelib/language/language.cpp @@ -0,0 +1,1292 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "language.h" + +#include "artifactproperties.h" +#include "propertymapinternal.h" +#include "resolvedfilecontext.h" +#include "scriptengine.h" + +#include +#include +#include +#include +#include // TODO: Move to language? +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { + +template bool equals(const T *v1, const T *v2) +{ + if (v1 == v2) + return true; + if (!v1 != !v2) + return false; + return *v1 == *v2; +} + + +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()); + m_fileTags.load(pool); +} + +void FileTagger::store(PersistentPool &pool) const +{ + QStringList patterns; + foreach (const QRegExp ®Exp, m_patterns) + patterns << regExp.pattern(); + pool.storeStringList(patterns); + m_fileTags.store(pool); +} + + +void Probe::load(PersistentPool &pool) +{ + m_location.load(pool); + pool.stream() >> m_condition; + m_configureScript = pool.idLoadString(); + m_properties = pool.loadVariantMap(); + m_initialProperties = pool.loadVariantMap(); +} + +void Probe::store(PersistentPool &pool) const +{ + m_location.store(pool); + pool.stream() << condition(); + pool.storeString(m_configureScript); + pool.store(m_properties); + pool.store(m_initialProperties); +} + + +/*! + * \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 SourceArtifactInternal::load(PersistentPool &pool) +{ + absoluteFilePath = pool.idLoadString(); + fileTags.load(pool); + pool.stream() >> overrideFileTags; + properties = pool.idLoadS(); +} + +void SourceArtifactInternal::store(PersistentPool &pool) const +{ + pool.storeString(absoluteFilePath); + fileTags.store(pool); + 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 ResolvedGroup::allFiles() const +{ + QList lst = files; + if (wildcards) + lst.append(wildcards->files); + return lst; +} + +void ResolvedGroup::load(PersistentPool &pool) +{ + name = pool.idLoadString(); + pool.stream() >> enabled; + location.load(pool); + prefix = pool.idLoadString(); + pool.loadContainerS(files); + wildcards = pool.idLoadS(); + properties = pool.idLoadS(); + fileTags.load(pool); + pool.stream() >> overrideTags; +} + +void ResolvedGroup::store(PersistentPool &pool) const +{ + pool.storeString(name); + pool.stream() << enabled; + location.store(pool); + pool.storeString(prefix); + pool.storeContainer(files); + pool.store(wildcards); + pool.store(properties); + fileTags.store(pool); + pool.stream() << 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) +{ + filePath = pool.idLoadString(); + fileTags.load(pool); + pool.stream() >> alwaysUpdated; + location.load(pool); + filePathLocation.load(pool); + + int i; + pool.stream() >> i; + bindings.clear(); + bindings.reserve(i); + Binding binding; + for (; --i >= 0;) { + binding.name = pool.idLoadStringList(); + binding.code = pool.idLoadString(); + binding.location.load(pool); + bindings += binding; + } +} + +void RuleArtifact::store(PersistentPool &pool) const +{ + pool.storeString(filePath); + fileTags.store(pool); + pool.stream() << alwaysUpdated; + location.store(pool); + filePathLocation.store(pool); + + pool.stream() << bindings.count(); + for (int i = bindings.count(); --i >= 0;) { + const Binding &binding = bindings.at(i); + pool.storeStringList(binding.name); + pool.storeString(binding.code); + binding.location.store(pool); + } +} + + +/*! + * \class ScriptFunction + * \brief The \c ScriptFunction class represents the JavaScript code found in the "prepare" binding + * of a \c Rule item in a qbs file. + * \sa Rule + */ + +ScriptFunction::ScriptFunction() +{ + +} + +ScriptFunction::~ScriptFunction() +{ + +} + + /*! + * \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. + */ + +bool ScriptFunction::isValid() const +{ + return location.line() != -1; +} + +void ScriptFunction::load(PersistentPool &pool) +{ + sourceCode = pool.idLoadString(); + argumentNames = pool.idLoadStringList(); + location.load(pool); + fileContext = pool.idLoadS(); +} + +void ScriptFunction::store(PersistentPool &pool) const +{ + pool.storeString(sourceCode); + pool.storeStringList(argumentNames); + location.store(pool); + pool.store(fileContext); +} + +bool operator==(const ScriptFunction &a, const ScriptFunction &b) +{ + return a.sourceCode == b.sourceCode + && a.location == b.location + && a.argumentNames == b.argumentNames + && equals(a.fileContext.data(), b.fileContext.data()); +} + +void ResolvedModule::load(PersistentPool &pool) +{ + name = pool.idLoadString(); + moduleDependencies = pool.idLoadStringList(); + setupBuildEnvironmentScript = pool.idLoadS(); + setupRunEnvironmentScript = pool.idLoadS(); +} + +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) +{ + return m1.name == m2.name + && m1.moduleDependencies.toSet() == m2.moduleDependencies.toSet() + && equals(m1.setupBuildEnvironmentScript.data(), m2.setupBuildEnvironmentScript.data()) + && equals(m1.setupRunEnvironmentScript.data(), m2.setupRunEnvironmentScript.data()); +} + +QString Rule::toString() const +{ + QStringList outputTagsSorted = collectedOutputFileTags().toStringList(); + outputTagsSorted.sort(); + FileTags inputTags = inputs; + inputTags.unite(inputsFromDependencies); + QStringList inputTagsSorted = inputTags.toStringList(); + inputTagsSorted.sort(); + return QLatin1Char('[') + outputTagsSorted.join(QLatin1Char(',')) + + QLatin1String("][") + + inputTagsSorted.join(QLatin1Char(',')) + QLatin1Char(']'); +} + +bool Rule::acceptsAsInput(Artifact *artifact) const +{ + return artifact->fileTags().matches(inputs); +} + +FileTags Rule::staticOutputFileTags() const +{ + FileTags result; + foreach (const RuleArtifactConstPtr &artifact, artifacts) + result.unite(artifact->fileTags); + return result; +} + +FileTags Rule::collectedOutputFileTags() const +{ + return outputFileTags.isEmpty() ? staticOutputFileTags() : outputFileTags; +} + +bool Rule::isDynamic() const +{ + return outputArtifactsScript->isValid(); +} + +bool Rule::requiresInputs() const +{ + return !inputs.isEmpty() || !inputsFromDependencies.isEmpty(); +} + +void Rule::load(PersistentPool &pool) +{ + name = pool.idLoadString(); + prepareScript = pool.idLoadS(); + outputArtifactsScript = pool.idLoadS(); + module = pool.idLoadS(); + inputs.load(pool); + outputFileTags.load(pool); + auxiliaryInputs.load(pool); + excludedAuxiliaryInputs.load(pool); + inputsFromDependencies.load(pool); + explicitlyDependsOn.load(pool); + pool.stream() >> multiplex >> alwaysRun; + pool.loadContainerS(artifacts); +} + +void Rule::store(PersistentPool &pool) const +{ + pool.storeString(name); + pool.store(prepareScript); + pool.store(outputArtifactsScript); + pool.store(module); + inputs.store(pool); + outputFileTags.store(pool); + auxiliaryInputs.store(pool); + excludedAuxiliaryInputs.store(pool); + inputsFromDependencies.store(pool); + explicitlyDependsOn.store(pool); + pool.stream() << multiplex << alwaysRun; + pool.storeContainer(artifacts); +} + +ResolvedProduct::ResolvedProduct() + : enabled(true) +{ +} + +ResolvedProduct::~ResolvedProduct() +{ +} + +void ResolvedProduct::accept(BuildGraphVisitor *visitor) const +{ + if (!buildData) + return; + foreach (BuildGraphNode * const node, buildData->roots) + node->accept(visitor); +} + +/*! + * \brief Returns all files of all groups as source artifacts. + * This includes the expanded list of wildcards. + */ +QList ResolvedProduct::allFiles() const +{ + QList 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 ResolvedProduct::allEnabledFiles() const +{ + QList 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.load(pool); + name = pool.idLoadString(); + profile = pool.idLoadString(); + targetName = pool.idLoadString(); + sourceDirectory = pool.idLoadString(); + destinationDirectory = pool.idLoadString(); + location.load(pool); + productProperties = pool.loadVariantMap(); + moduleProperties = pool.idLoadS(); + pool.loadContainerS(rules); + pool.loadContainerS(dependencies); + pool.loadContainerS(fileTaggers); + pool.loadContainerS(modules); + pool.loadContainerS(scanners); + pool.loadContainerS(groups); + pool.loadContainerS(artifactProperties); + pool.loadContainerS(probes); + buildData.reset(pool.idLoad()); +} + +void ResolvedProduct::store(PersistentPool &pool) const +{ + pool.stream() << enabled; + fileTags.store(pool); + pool.storeString(name); + pool.storeString(profile); + pool.storeString(targetName); + pool.storeString(sourceDirectory); + pool.storeString(destinationDirectory); + location.store(pool); + pool.store(productProperties); + pool.store(moduleProperties); + pool.storeContainer(rules); + pool.storeContainer(dependencies); + pool.storeContainer(fileTaggers); + pool.storeContainer(modules); + pool.storeContainer(scanners); + pool.storeContainer(groups); + pool.storeContainer(artifactProperties); + pool.storeContainer(probes); + pool.store(buildData.data()); +} + +QList topSortModules(const QHash > &moduleChildren, + const QList &modules, + QSet &seenModuleNames) +{ + QList 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; +} + +enum EnvType +{ + BuildEnv, RunEnv +}; + +static bool findModuleMapRecursively_impl(const QVariantMap &cfg, const QString &moduleName, + QVariantMap *result) +{ + for (QVariantMap::const_iterator it = cfg.constBegin(); it != cfg.constEnd(); ++it) { + if (it.key() == moduleName) { + *result = it.value().toMap(); + return true; + } + if (findModuleMapRecursively_impl(it.value().toMap().value(QStringLiteral("modules")).toMap(), + moduleName, result)) { + return true; + } + } + return false; +} + +static QVariantMap findModuleMapRecursively(const QVariantMap &cfg, const QString &moduleName) +{ + QVariantMap result; + findModuleMapRecursively_impl(cfg, moduleName, &result); + return result; +} + +static QProcessEnvironment getProcessEnvironment(ScriptEngine *engine, EnvType envType, + const QList &modules, + const PropertyMapConstPtr &productConfiguration, + const QProcessEnvironment &env) +{ + QMap moduleMap; + foreach (const ResolvedModuleConstPtr &module, modules) + moduleMap.insert(module->name, module.data()); + + QHash > moduleParents; + QHash > 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 rootModules; + foreach (ResolvedModuleConstPtr module, modules) { + if (moduleParents.value(module.data()).isEmpty()) { + QBS_ASSERT(module, return env); + rootModules.append(module.data()); + } + } + + QProcessEnvironment procenv = env; + + { + QVariant v; + v.setValue(&procenv); + engine->setProperty("_qbs_procenv", v); + } + + QScriptValue scope = engine->newObject(); + scope.setPrototype(engine->globalObject()); + TemporaryGlobalObjectSetter tgos(scope); + + QSet seenModuleNames; + QList 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, scope); + JsExtensions::setupExtensions(setupScript->fileContext->jsExtensions(), scope); + + // expose properties of direct module dependencies + QScriptValue scriptValue; + QVariantMap productModules = productConfiguration->value() + .value(QLatin1String("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 = findModuleMapRecursively(productModules, module->name); + for (QVariantMap::const_iterator it = moduleCfg.constBegin(); it != moduleCfg.constEnd(); ++it) + scope.setProperty(it.key(), engine->toScriptValue(it.value())); + + scriptValue = engine->evaluate(setupScript->sourceCode + QLatin1String("()")); + if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue))) { + QString envTypeStr = (envType == BuildEnv + ? QLatin1String("build") : QLatin1String("run")); + throw ErrorInfo(Tr::tr("Error while setting up %1 environment: %2") + .arg(envTypeStr, engine->lastErrorString(scriptValue)), + setupScript->location); + } + } + + 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, moduleProperties, env); +} + +void ResolvedProduct::setupRunEnvironment(ScriptEngine *engine, const QProcessEnvironment &env) const +{ + if (!runEnvironment.isEmpty()) + return; + + runEnvironment = getProcessEnvironment(engine, RunEnv, modules, moduleProperties, env); +} + +void ResolvedProduct::registerArtifactWithChangedInputs(Artifact *artifact) +{ + QBS_CHECK(buildData); + QBS_CHECK(artifact->product == this); + QBS_CHECK(artifact->transformer); + if (artifact->transformer->rule->multiplex) { + // Reapplication of rules only makes sense for multiplex rules (e.g. linker). + buildData->artifactsWithChangedInputsPerRule[artifact->transformer->rule] += artifact; + } +} + +void ResolvedProduct::unregisterArtifactWithChangedInputs(Artifact *artifact) +{ + QBS_CHECK(buildData); + QBS_CHECK(artifact->product == this); + QBS_CHECK(artifact->transformer); + buildData->artifactsWithChangedInputsPerRule[artifact->transformer->rule] -= artifact; +} + +void ResolvedProduct::unmarkForReapplication(const RuleConstPtr &rule) +{ + QBS_CHECK(buildData); + buildData->artifactsWithChangedInputsPerRule.remove(rule); +} + +bool ResolvedProduct::isMarkedForReapplication(const RuleConstPtr &rule) const +{ + return !buildData->artifactsWithChangedInputsPerRule.value(rule).isEmpty(); +} + +ArtifactSet ResolvedProduct::lookupArtifactsByFileTag(const FileTag &tag) const +{ + QBS_CHECK(buildData); + return buildData->artifactsByFileTag.value(tag); +} + +ArtifactSet ResolvedProduct::lookupArtifactsByFileTags(const FileTags &tags) const +{ + QBS_CHECK(buildData); + ArtifactSet set; + for (const FileTag &tag : tags) + set = set.unite(buildData->artifactsByFileTag.value(tag)); + return set; +} + +ArtifactSet ResolvedProduct::targetArtifacts() const +{ + QBS_CHECK(buildData); + ArtifactSet taSet; + foreach (Artifact * const a, buildData->rootArtifacts()) { + if (a->fileTags().matches(fileTags)) + taSet << a; + } + return taSet; +} + +TopLevelProject *ResolvedProduct::topLevelProject() const +{ + return project->topLevelProject(); +} + +QString ResolvedProduct::uniqueName(const QString &name, const QString &profile) +{ + QBS_CHECK(!profile.isEmpty()); + return name + QLatin1Char('.') + profile; +} + +QString ResolvedProduct::uniqueName() const +{ + return uniqueName(name, profile); +} + +static QStringList findGeneratedFiles(const Artifact *base, bool recursive, const FileTags &tags) +{ + QStringList result; + for (const Artifact *parent : base->parentArtifacts()) { + if (tags.isEmpty() || parent->fileTags().matches(tags)) + result << parent->filePath(); + if (recursive) + result << findGeneratedFiles(parent, true, tags); + } + return result; +} + +QStringList ResolvedProduct::generatedFiles(const QString &baseFile, bool recursive, + const FileTags &tags) const +{ + ProductBuildData *data = buildData.data(); + if (!data) + return QStringList(); + + for (const Artifact *art : filterByType(data->nodes)) { + if (art->filePath() == baseFile) + return findGeneratedFiles(art, recursive, tags); + } + return QStringList(); +} + +QString ResolvedProduct::deriveBuildDirectoryName(const QString &name, const QString &profile) +{ + QString dirName = uniqueName(name, profile); + const QByteArray hash = QCryptographicHash::hash(dirName.toUtf8(), QCryptographicHash::Sha1); + return HostOsInfo::rfc1034Identifier(dirName) + .append(QLatin1Char('.')) + .append(QString::fromLatin1(hash.toHex().left(8))); +} + +QString ResolvedProduct::buildDirectory() const +{ + return productProperties.value(QLatin1String("buildDirectory")).toString(); +} + +bool ResolvedProduct::isInParentProject(const ResolvedProductConstPtr &other) const +{ + for (const ResolvedProject *otherParent = other->project.data(); otherParent; + otherParent = otherParent->parentProject.data()) { + if (otherParent == project.data()) + return true; + } + return false; +} + +bool ResolvedProduct::builtByDefault() const +{ + return productProperties.value(QLatin1String("builtByDefault"), true).toBool(); +} + +void ResolvedProduct::cacheExecutablePath(const QString &origFilePath, const QString &fullFilePath) +{ + QMutexLocker locker(&m_executablePathCacheLock); + m_executablePathCache.insert(origFilePath, fullFilePath); +} + +QString ResolvedProduct::cachedExecutablePath(const QString &origFilePath) const +{ + QMutexLocker locker(&m_executablePathCacheLock); + return m_executablePathCache.value(origFilePath); +} + + +ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(0) +{ +} + +void ResolvedProject::accept(BuildGraphVisitor *visitor) const +{ + foreach (const ResolvedProductPtr &product, products) + product->accept(visitor); + foreach (const ResolvedProjectPtr &subProject, subProjects) + subProject->accept(visitor); +} + +TopLevelProject *ResolvedProject::topLevelProject() +{ + if (m_topLevelProject) + return m_topLevelProject; + TopLevelProject *tlp = dynamic_cast(this); + if (tlp) { + m_topLevelProject = tlp; + return m_topLevelProject; + } + QBS_CHECK(!parentProject.isNull()); + m_topLevelProject = parentProject->topLevelProject(); + return m_topLevelProject; +} + +QList ResolvedProject::allSubProjects() const +{ + QList projectList = subProjects; + foreach (const ResolvedProjectConstPtr &subProject, subProjects) + projectList << subProject->allSubProjects(); + return projectList; +} + +QList ResolvedProject::allProducts() const +{ + QList productList = products; + foreach (const ResolvedProjectConstPtr &subProject, subProjects) + productList << subProject->allProducts(); + return productList; +} + +void ResolvedProject::load(PersistentPool &pool) +{ + name = pool.idLoadString(); + location.load(pool); + int count; + pool.stream() + >> enabled + >> count; + products.clear(); + products.reserve(count); + for (; --count >= 0;) { + ResolvedProductPtr rProduct = pool.idLoadS(); + if (rProduct->buildData) { + foreach (BuildGraphNode * const node, rProduct->buildData->nodes) { + node->product = rProduct; + + // restore parent links + foreach (BuildGraphNode *child, node->children) + child->parents.insert(node); + } + } + products.append(rProduct); + } + + pool.stream() >> count; + subProjects.clear(); + subProjects.reserve(count); + for (; --count >= 0;) { + ResolvedProjectPtr p = pool.idLoadS(); + subProjects.append(p); + } + + m_projectProperties = pool.loadVariantMap(); +} + +void ResolvedProject::store(PersistentPool &pool) const +{ + pool.storeString(name); + location.store(pool); + pool.stream() + << enabled + << products.count(); + foreach (const ResolvedProductConstPtr &product, products) + pool.store(product); + pool.stream() << subProjects.count(); + foreach (const ResolvedProjectConstPtr &project, subProjects) + pool.store(project); + pool.store(m_projectProperties); +} + + +TopLevelProject::TopLevelProject() + : bgLocker(0), locked(false), lastResolveTime(FileTime::oldestTime()) +{ +} + +TopLevelProject::~TopLevelProject() +{ + delete bgLocker; +} + +QString TopLevelProject::deriveId(const QVariantMap &config) +{ + const QVariantMap qbsProperties = config.value(QLatin1String("qbs")).toMap(); + const QString configurationName = qbsProperties.value(QLatin1String("configurationName")) + .toString(); + return configurationName; +} + +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::profile() const +{ + return projectProperties().value(QLatin1String("profile")).toString(); +} + +QString TopLevelProject::buildGraphFilePath() const +{ + return ProjectBuildData::deriveBuildGraphFilePath(buildDirectory, id()); +} + +void TopLevelProject::store(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); + pool.finalizeWriteStream(); + buildData->isDirty = false; +} + +void TopLevelProject::load(PersistentPool &pool) +{ + ResolvedProject::load(pool); + m_id = pool.idLoadString(); + pool.stream() >> usedEnvironment; + pool.stream() >> canonicalFilePathResults; + pool.stream() >> fileExistsResults; + pool.stream() >> directoryEntriesResults; + pool.stream() >> fileLastModifiedResults; + QHash envHash; + pool.stream() >> envHash; + for (QHash::const_iterator i = envHash.begin(); i != envHash.end(); ++i) + environment.insert(i.key(), i.value()); + pool.stream() >> profileConfigs; + pool.stream() >> buildSystemFiles; + pool.stream() >> lastResolveTime; + int warningsCount; + pool.stream() >> warningsCount; + for (int i = 0; i < warningsCount; ++i) { + ErrorInfo e; + e.load(pool); + warningsEncountered << e; + } + buildData.reset(pool.idLoad()); + QBS_CHECK(buildData); + buildData->isDirty = false; +} + +void TopLevelProject::store(PersistentPool &pool) const +{ + ResolvedProject::store(pool); + pool.storeString(m_id); + pool.stream() << usedEnvironment + << canonicalFilePathResults + << fileExistsResults + << directoryEntriesResults + << fileLastModifiedResults; + QHash envHash; + foreach (const QString &key, environment.keys()) + envHash.insert(key, environment.value(key)); + pool.stream() << envHash; + pool.stream() << profileConfigs; + pool.stream() << buildSystemFiles; + pool.stream() << lastResolveTime; + pool.stream() << warningsEncountered.count(); + std::for_each(warningsEncountered.constBegin(), warningsEncountered.constEnd(), + [&pool](const ErrorInfo &e) { e.store(pool); }); + 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 SourceWildCards::expandPatterns(const GroupConstPtr &group, + const QString &baseDir) const +{ + QSet files = expandPatterns(group, patterns, baseDir); + files -= expandPatterns(group, excludePatterns, baseDir); + return files; +} + +QSet SourceWildCards::expandPatterns(const GroupConstPtr &group, + const QStringList &patterns, const QString &baseDir) const +{ + QSet files; + QString expandedPrefix = prefix; + if (expandedPrefix.startsWith(QLatin1String("~/"))) + expandedPrefix.replace(0, 1, QDir::homePath()); + foreach (QString pattern, patterns) { + pattern.prepend(expandedPrefix); + pattern.replace(QLatin1Char('\\'), QLatin1Char('/')); + QStringList parts = pattern.split(QLatin1Char('/'), QString::SkipEmptyParts); + if (FileInfo::isAbsolute(pattern)) { + QString rootDir; + if (HostOsInfo::isWindowsHost() && pattern.at(0) != QLatin1Char('/')) { + 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; +} + +static bool isQbsBuildDir(const QDir &dir) +{ + return dir.exists(dir.dirName() + QLatin1String(".bg")); +} + +void SourceWildCards::expandPatterns(QSet &result, const GroupConstPtr &group, + const QStringList &parts, + const QString &baseDir) const +{ + // People might build directly in the project source directory. This is okay, since + // we keep the build data in a "container" directory. However, we must make sure we don't + // match any generated files therein as source files. + if (isQbsBuildDir(baseDir)) + return; + + 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 | QDir::System + | QDir::Dirs; // This one is needed to get symbolic links to directories + + 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(); + if (isQbsBuildDir(it.fileInfo().dir())) + continue; // See above. + if (!isDir && it.fileInfo().isDir() && !it.fileInfo().isSymLink()) + continue; + if (isDir) + expandPatterns(result, group, changed_parts, filePath); + else + result += QDir::cleanPath(filePath); + } +} + +template QMap listToMap(const QList &list) +{ + QMap map; + foreach (const T &elem, list) + map.insert(keyFromElem(elem), elem); + return map; +} + +template bool listsAreEqual(const QList &l1, const QList &l2) +{ + if (l1.count() != l2.count()) + return false; + const QMap map1 = listToMap(l1); + const QMap map2 = listToMap(l2); + foreach (const QString &key, map1.keys()) { + const T value2 = map2.value(key); + if (!value2) + return false; + if (!equals(map1.value(key).data(), value2.data())) + return false; + } + return true; +} + +QString keyFromElem(const SourceArtifactPtr &sa) { return sa->absoluteFilePath; } +QString keyFromElem(const RulePtr &r) { + QString key = r->toString() + r->prepareScript->sourceCode; + if (r->outputArtifactsScript) + key += r->outputArtifactsScript->sourceCode; + foreach (const auto &a, r->artifacts) { + key += a->filePath; + } + return key; +} + +QString keyFromElem(const ArtifactPropertiesPtr &ap) +{ + QStringList lst = ap->fileTagsFilter().toStringList(); + lst.sort(); + return lst.join(QLatin1Char(',')); +} + +bool operator==(const SourceArtifactInternal &sa1, const SourceArtifactInternal &sa2) +{ + return sa1.absoluteFilePath == sa2.absoluteFilePath + && sa1.fileTags == sa2.fileTags + && sa1.overrideFileTags == sa2.overrideFileTags + && sa1.properties->value() == sa2.properties->value(); +} + +bool sourceArtifactSetsAreEqual(const QList &l1, + const QList &l2) +{ + return listsAreEqual(l1, l2); +} + +bool operator==(const Rule &r1, const Rule &r2) +{ + if (r1.artifacts.count() != r2.artifacts.count()) + return false; + for (int i = 0; i < r1.artifacts.count(); ++i) { + if (!equals(r1.artifacts.at(i).data(), r2.artifacts.at(i).data())) + return false; + } + + return r1.module->name == r2.module->name + && equals(r1.prepareScript.data(), r2.prepareScript.data()) + && equals(r1.outputArtifactsScript.data(), r2.outputArtifactsScript.data()) + && r1.inputs == r2.inputs + && r1.outputFileTags == r2.outputFileTags + && r1.auxiliaryInputs == r2.auxiliaryInputs + && r1.excludedAuxiliaryInputs == r2.excludedAuxiliaryInputs + && r1.inputsFromDependencies == r2.inputsFromDependencies + && r1.explicitlyDependsOn == r2.explicitlyDependsOn + && r1.multiplex == r2.multiplex + && r1.alwaysRun == r2.alwaysRun; +} + +bool ruleListsAreEqual(const QList &l1, const QList &l2) +{ + return listsAreEqual(l1, l2); +} + +bool operator==(const RuleArtifact &a1, const RuleArtifact &a2) +{ + return a1.filePath == a2.filePath + && 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(QLatin1Char(',')))); +} + +bool artifactPropertyListsAreEqual(const QList &l1, + const QList &l2) +{ + return listsAreEqual(l1, l2); +} + +void ResolvedScanner::load(PersistentPool &pool) +{ + module = pool.idLoadS(); + inputs.load(pool); + pool.stream() >> recursive; + searchPathsScript = pool.idLoadS(); + scanScript = pool.idLoadS(); +} + +void ResolvedScanner::store(PersistentPool &pool) const +{ + pool.store(module); + inputs.store(pool); + pool.stream() << recursive; + pool.store(searchPathsScript); + pool.store(scanScript); +} + +} // 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 00000000..8d5c401f --- /dev/null +++ b/src/lib/corelib/language/language.h @@ -0,0 +1,534 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_LANGUAGE_H +#define QBS_LANGUAGE_H + +#include "filetags.h" +#include "forward_decls.h" +#include "jsimports.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QScriptEngine; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { +class BuildGraphLocker; +class BuildGraphLoader; +class BuildGraphVisitor; + +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 &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 m_patterns; + FileTags m_fileTags; +}; + +class Probe : public PersistentObject +{ +public: + static ProbePtr create() { return ProbePtr(new Probe); } + static ProbeConstPtr create(const CodeLocation &location, bool condition, + const QString &configureScript, const QVariantMap &properties, + const QVariantMap &initialProperties) + { + return ProbeConstPtr(new Probe(location, condition, configureScript, properties, + initialProperties)); + } + + const CodeLocation &location() const { return m_location; } + bool condition() const { return m_condition; } + const QString &configureScript() const { return m_configureScript; } + const QVariantMap &properties() const { return m_properties; } + const QVariantMap &initialProperties() const { return m_initialProperties; } + +private: + Probe() {} + Probe(const CodeLocation &location, bool condition, const QString &configureScript, + const QVariantMap &properties, const QVariantMap &initialProperties) + : m_location(location) + , m_configureScript(configureScript) + , m_properties(properties) + , m_initialProperties(initialProperties) + , m_condition(condition) + {} + + void load(PersistentPool &pool) override; + void store(PersistentPool &pool) const override; + + CodeLocation m_location; + QString m_configureScript; + QVariantMap m_properties; + QVariantMap m_initialProperties; + bool m_condition; +}; + +class RuleArtifact : public PersistentObject +{ +public: + static RuleArtifactPtr create() { return RuleArtifactPtr(new RuleArtifact); } + + QString filePath; + FileTags fileTags; + bool alwaysUpdated; + CodeLocation location; + CodeLocation filePathLocation; + + class Binding + { + public: + QStringList name; + QString code; + CodeLocation location; + }; + + QVector 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 SourceArtifactInternal : public PersistentObject +{ +public: + static SourceArtifactPtr create() { return SourceArtifactPtr(new SourceArtifactInternal); } + + QString absoluteFilePath; + FileTags fileTags; + bool overrideFileTags; + PropertyMapPtr properties; + +private: + SourceArtifactInternal() : overrideFileTags(true) {} + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; +}; +bool operator==(const SourceArtifactInternal &sa1, const SourceArtifactInternal &sa2); +inline bool operator!=(const SourceArtifactInternal &sa1, const SourceArtifactInternal &sa2) { + return !(sa1 == sa2); +} + +bool sourceArtifactSetsAreEqual(const QList &l1, + const QList &l2); + +class SourceWildCards : public PersistentObject +{ +public: + typedef QSharedPointer Ptr; + typedef QSharedPointer ConstPtr; + + static Ptr create() { return Ptr(new SourceWildCards); } + + QSet expandPatterns(const GroupConstPtr &group, const QString &baseDir) const; + + // TODO: Use back pointer to Group instead? + QString prefix; + + QStringList patterns; + QStringList excludePatterns; + QList files; + +private: + SourceWildCards() {} + + QSet expandPatterns(const GroupConstPtr &group, const QStringList &patterns, + const QString &baseDir) const; + void expandPatterns(QSet &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 files; + SourceWildCards::Ptr wildcards; + PropertyMapPtr properties; + FileTags fileTags; + bool overrideTags; + + QList allFiles() const; + +private: + ResolvedGroup() + : enabled(true) + {} + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; +}; + +class ScriptFunction : public PersistentObject +{ +public: + static ScriptFunctionPtr create() { return ScriptFunctionPtr(new ScriptFunction); } + + ~ScriptFunction(); + + QString sourceCode; + QStringList argumentNames; + CodeLocation location; + ResolvedFileContextConstPtr fileContext; + mutable QScriptValue scriptFunction; // cache + + bool isValid() const; + +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; + QString name; + ScriptFunctionPtr prepareScript; + FileTags outputFileTags; // unused, if artifacts is non-empty + ScriptFunctionPtr outputArtifactsScript; // unused, if artifacts is non-empty + FileTags inputs; + FileTags auxiliaryInputs; + FileTags excludedAuxiliaryInputs; + FileTags inputsFromDependencies; + FileTags explicitlyDependsOn; + bool multiplex; + QList artifacts; // unused, if outputFileTags/outputArtifactsScript is non-empty + bool alwaysRun; + + // members that we don't need to save + int ruleGraphId; + + QString toString() const; + bool acceptsAsInput(Artifact *artifact) const; + FileTags staticOutputFileTags() const; + FileTags collectedOutputFileTags() const; + bool isDynamic() const; + bool requiresInputs() const; +private: + Rule() : multiplex(false), alwaysRun(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 &l1, const QList &l2); + +class ResolvedScanner : public PersistentObject +{ +public: + static ResolvedScannerPtr create() { return ResolvedScannerPtr(new ResolvedScanner); } + + ResolvedModuleConstPtr module; + FileTags inputs; + bool recursive; + ScriptFunctionPtr searchPathsScript; + ScriptFunctionPtr scanScript; + +private: + ResolvedScanner() : + recursive(false) + {} + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; +}; + +class TopLevelProject; +class ScriptEngine; + +class ResolvedProduct : public PersistentObject +{ +public: + static ResolvedProductPtr create() { return ResolvedProductPtr(new ResolvedProduct); } + + ~ResolvedProduct(); + + bool enabled; + FileTags fileTags; + QString name; + QString targetName; + QString profile; + QString sourceDirectory; + QString destinationDirectory; + CodeLocation location; + WeakPointer project; + QVariantMap productProperties; + PropertyMapPtr moduleProperties; + QSet rules; + QSet dependencies; + QList fileTaggers; + QList modules; + QList scanners; + QList groups; + QList probes; + QList artifactProperties; + QScopedPointer buildData; + + mutable QProcessEnvironment buildEnvironment; // must not be saved + mutable QProcessEnvironment runEnvironment; // must not be saved + + void accept(BuildGraphVisitor *visitor) const; + QList allFiles() const; + QList allEnabledFiles() const; + FileTags fileTagsForFileName(const QString &fileName) const; + void setupBuildEnvironment(ScriptEngine *scriptEngine, const QProcessEnvironment &env) const; + void setupRunEnvironment(ScriptEngine *scriptEngine, const QProcessEnvironment &env) const; + + void registerArtifactWithChangedInputs(Artifact *artifact); + void unregisterArtifactWithChangedInputs(Artifact *artifact); + void unmarkForReapplication(const RuleConstPtr &rule); + bool isMarkedForReapplication(const RuleConstPtr &rule) const; + ArtifactSet lookupArtifactsByFileTag(const FileTag &tag) const; + ArtifactSet lookupArtifactsByFileTags(const FileTags &tags) const; + ArtifactSet targetArtifacts() const; + + TopLevelProject *topLevelProject() const; + + static QString uniqueName(const QString &name, const QString &profile); + QString uniqueName() const; + + QStringList generatedFiles(const QString &baseFile, bool recursive, const FileTags &tags) const; + + static QString deriveBuildDirectoryName(const QString &name, const QString &profile); + QString buildDirectory() const; + + bool isInParentProject(const ResolvedProductConstPtr &other) const; + bool builtByDefault() const; + + void cacheExecutablePath(const QString &origFilePath, const QString &fullFilePath); + QString cachedExecutablePath(const QString &origFilePath) const; + +private: + ResolvedProduct(); + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + + QHash m_executablePathCache; + mutable QMutex m_executablePathCacheLock; +}; + +class ResolvedProject : public PersistentObject +{ +public: + static ResolvedProjectPtr create() { return ResolvedProjectPtr(new ResolvedProject); } + + QString name; + CodeLocation location; + bool enabled; + QList products; + QList subProjects; + WeakPointer parentProject; + + void accept(BuildGraphVisitor *visitor) const; + + void setProjectProperties(const QVariantMap &config) { m_projectProperties = config; } + const QVariantMap &projectProperties() const { return m_projectProperties; } + + TopLevelProject *topLevelProject(); + QList allSubProjects() const; + QList 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; + + // Environment variables requested by the project while resolving. + // TODO: This information is currently not used. Remove in 1.5 or use elaborate change tracking + // logic where rules declare the environment variables that could influence their + // behavior. + QHash usedEnvironment; + + QHash canonicalFilePathResults; // Results of calls to "File.canonicalFilePath()." + QHash fileExistsResults; // Results of calls to "File.exists()". + QHash, QStringList> directoryEntriesResults; // Results of calls to "File.directoryEntries()". + QHash fileLastModifiedResults; // Results of calls to "File.lastModified()". + QScopedPointer buildData; + BuildGraphLocker *bgLocker; // This holds the system-wide build graph file lock. + bool locked; // This is the API-level lock for the project instance. + + QSet buildSystemFiles; + FileTime lastResolveTime; + QList warningsEncountered; + + void setBuildConfiguration(const QVariantMap &config); + const QVariantMap &buildConfiguration() const { return m_buildConfiguration; } + QString id() const { return m_id; } + QString profile() const; + QVariantMap profileConfigs; + + QString buildGraphFilePath() const; + void store(Logger logger) const; + +private: + TopLevelProject(); + + void load(PersistentPool &pool); + void store(PersistentPool &pool) const; + + QString m_id; + QVariantMap m_buildConfiguration; +}; + +bool artifactPropertyListsAreEqual(const QList &l1, + const QList &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 00000000..ca8fad22 --- /dev/null +++ b/src/lib/corelib/language/language.pri @@ -0,0 +1,87 @@ +include(../../../install_prefix.pri) + +HEADERS += \ + $$PWD/artifactproperties.h \ + $$PWD/astimportshandler.h \ + $$PWD/astpropertiesitemhandler.h \ + $$PWD/asttools.h \ + $$PWD/builtindeclarations.h \ + $$PWD/deprecationinfo.h \ + $$PWD/evaluationdata.h \ + $$PWD/evaluator.h \ + $$PWD/evaluatorscriptclass.h \ + $$PWD/filecontext.h \ + $$PWD/filecontextbase.h \ + $$PWD/filetags.h \ + $$PWD/forward_decls.h \ + $$PWD/functiondeclaration.h \ + $$PWD/identifiersearch.h \ + $$PWD/item.h \ + $$PWD/itemdeclaration.h \ + $$PWD/itemobserver.h \ + $$PWD/itempool.h \ + $$PWD/itemreader.h \ + $$PWD/itemreaderastvisitor.h \ + $$PWD/itemreadervisitorstate.h \ + $$PWD/itemtype.h \ + $$PWD/jsimports.h \ + $$PWD/language.h \ + $$PWD/loader.h \ + $$PWD/moduleloader.h \ + $$PWD/modulemerger.h \ + $$PWD/preparescriptobserver.h \ + $$PWD/projectresolver.h \ + $$PWD/property.h \ + $$PWD/propertydeclaration.h \ + $$PWD/propertymapinternal.h \ + $$PWD/qualifiedid.h \ + $$PWD/resolvedfilecontext.h \ + $$PWD/scriptengine.h \ + $$PWD/scriptimporter.h \ + $$PWD/scriptpropertyobserver.h \ + $$PWD/value.h + +SOURCES += \ + $$PWD/artifactproperties.cpp \ + $$PWD/astimportshandler.cpp \ + $$PWD/astpropertiesitemhandler.cpp \ + $$PWD/asttools.cpp \ + $$PWD/builtindeclarations.cpp \ + $$PWD/evaluator.cpp \ + $$PWD/evaluatorscriptclass.cpp \ + $$PWD/filecontext.cpp \ + $$PWD/filecontextbase.cpp \ + $$PWD/filetags.cpp \ + $$PWD/identifiersearch.cpp \ + $$PWD/item.cpp \ + $$PWD/itemdeclaration.cpp \ + $$PWD/itempool.cpp \ + $$PWD/itemreader.cpp \ + $$PWD/itemreaderastvisitor.cpp \ + $$PWD/itemreadervisitorstate.cpp \ + $$PWD/language.cpp \ + $$PWD/loader.cpp \ + $$PWD/moduleloader.cpp \ + $$PWD/modulemerger.cpp \ + $$PWD/preparescriptobserver.cpp \ + $$PWD/projectresolver.cpp \ + $$PWD/property.cpp \ + $$PWD/propertydeclaration.cpp \ + $$PWD/propertymapinternal.cpp \ + $$PWD/qualifiedid.cpp \ + $$PWD/resolvedfilecontext.cpp \ + $$PWD/scriptengine.cpp \ + $$PWD/scriptimporter.cpp \ + $$PWD/value.cpp + +qbs_enable_unit_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 00000000..4384febd --- /dev/null +++ b/src/lib/corelib/language/loader.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "loader.h" + +#include "language.h" +#include "moduleloader.h" +#include "projectresolver.h" +#include "scriptengine.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +Loader::Loader(ScriptEngine *engine, const Logger &logger) + : m_logger(logger) + , m_progressObserver(0) + , m_engine(engine) +{ + m_logger.storeWarnings(); +} + +void Loader::setProgressObserver(ProgressObserver *observer) +{ + m_progressObserver = 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_searchPaths = searchPaths; +} + +void Loader::setOldProbes(const QHash > &oldProbes) +{ + m_oldProbes = oldProbes; +} + +TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters ¶meters) +{ + QBS_CHECK(QFileInfo(parameters.projectFilePath()).isAbsolute()); + + m_engine->setEnvironment(parameters.adjustedEnvironment()); + m_engine->clearExceptions(); + m_engine->clearImportsCache(); + m_engine->clearRequestedProperties(); + m_engine->enableProfiling(parameters.logElapsedTime()); + m_logger.clearWarnings(); + EvalContextSwitcher evalContextSwitcher(m_engine, EvalContext::PropertyEvaluation); + + QTimer cancelationTimer; + + // 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.finalBuildConfigurationTree())), 1); + cancelationTimer.setSingleShot(false); + QObject::connect(&cancelationTimer, &QTimer::timeout, [this]() { + QBS_ASSERT(m_progressObserver, return); + if (m_progressObserver->canceled()) + m_engine->cancel(); + }); + cancelationTimer.start(1000); + } + + const FileTime resolveTime = FileTime::currentTime(); + ModuleLoader moduleLoader(m_engine, m_logger); + moduleLoader.setProgressObserver(m_progressObserver); + moduleLoader.setSearchPaths(m_searchPaths); + moduleLoader.setOldProbes(m_oldProbes); + const ModuleLoaderResult loadResult = moduleLoader.load(parameters); + ProjectResolver resolver(moduleLoader.evaluator(), loadResult, parameters, m_logger); + resolver.setProgressObserver(m_progressObserver); + const TopLevelProjectPtr project = resolver.resolve(); + project->lastResolveTime = resolveTime; + + // E.g. if the top-level project is disabled. + if (m_progressObserver) + m_progressObserver->setFinished(); + + return project; +} + +} // 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 00000000..04821843 --- /dev/null +++ b/src/lib/corelib/language/loader.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_LOADER_H +#define QBS_LOADER_H + +#include "forward_decls.h" +#include + +#include + +namespace qbs { +class Settings; +class SetupProjectParameters; +namespace Internal { +class Logger; +class ProgressObserver; +class ScriptEngine; + +class Loader +{ +public: + Loader(ScriptEngine *engine, const Logger &logger); + + void setProgressObserver(ProgressObserver *observer); + void setSearchPaths(const QStringList &searchPaths); + void setOldProbes(const QHash> &oldProbes); + TopLevelProjectPtr loadProject(const SetupProjectParameters ¶meters); + +private: + Logger m_logger; + ProgressObserver *m_progressObserver; + ScriptEngine * const m_engine; + QStringList m_searchPaths; + QHash> m_oldProbes; +}; + +} // 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 00000000..babd727b --- /dev/null +++ b/src/lib/corelib/language/moduleloader.cpp @@ -0,0 +1,2456 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "moduleloader.h" + +#include "builtindeclarations.h" +#include "evaluator.h" +#include "filecontext.h" +#include "item.h" +#include "itemreader.h" +#include "language.h" +#include "modulemerger.h" +#include "qualifiedid.h" +#include "scriptengine.h" +#include "value.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { + +class ModuleLoader::ItemModuleList : public QList {}; + +const QString moduleSearchSubDir = QLatin1String("modules"); + +class ModuleLoader::ProductSortByDependencies +{ +public: + ProductSortByDependencies(TopLevelProjectContext &tlp) : m_tlp(tlp) + { + } + + void apply() + { + QHash productsMap; + QList allProducts; + for (auto projIt = m_tlp.projects.begin(); projIt != m_tlp.projects.end(); ++projIt) { + QVector &products = (*projIt)->products; + for (auto prodIt = products.begin(); prodIt != products.end(); ++prodIt) { + allProducts << prodIt; + productsMap.insert(prodIt->name, prodIt); + } + } + QSet allDependencies; + foreach (auto productContext, allProducts) { + auto &productDependencies = m_dependencyMap[productContext]; + foreach (const auto &dep, productContext->info.usedProducts) { + if (!dep.productTypes.isEmpty()) + continue; + QBS_CHECK(!dep.name.isEmpty()); + ProductContext * const depProduct = productsMap.value(dep.name); + QBS_CHECK(depProduct); + productDependencies << depProduct; + allDependencies << depProduct; + } + } + QSet rootProducts = allProducts.toSet() - allDependencies; + foreach (ProductContext * const rootProduct, rootProducts) + traverse(rootProduct); + if (m_sortedProducts.count() < allProducts.count()) { + foreach (auto * const product, allProducts) { + QList path; + findCycle(product, path); + } + } + QBS_CHECK(m_sortedProducts.count() == allProducts.count()); + } + + // No product at position i has dependencies to a product at position j > i. + QList sortedProducts() + { + return m_sortedProducts; + } + +private: + void traverse(ModuleLoader::ProductContext *product) + { + if (m_seenProducts.contains(product)) + return; + m_seenProducts << product; + foreach (auto dependency, m_dependencyMap.value(product)) + traverse(dependency); + m_sortedProducts << product; + } + + void findCycle(ModuleLoader::ProductContext *product, + QList &path) + { + if (path.contains(product)) { + ErrorInfo error(Tr::tr("Cyclic dependencies detected.")); + foreach (const auto * const p, path) + error.append(p->name, p->item->location()); + error.append(product->name, product->item->location()); + throw error; + } + path << product; + foreach (auto * const dep, m_dependencyMap.value(product)) + findCycle(dep, path); + path.removeLast(); + } + + TopLevelProjectContext &m_tlp; + QHash> m_dependencyMap; + QSet m_seenProducts; + QList m_sortedProducts; +}; + +class SearchPathsManager { +public: + SearchPathsManager(ItemReader *itemReader, const QStringList &extraSearchPaths) + : m_itemReader(itemReader) + { + m_itemReader->pushExtraSearchPaths(extraSearchPaths); + } + ~SearchPathsManager() { m_itemReader->popExtraSearchPaths(); } + +private: + ItemReader * const m_itemReader; +}; + +ModuleLoader::ModuleLoader(ScriptEngine *engine, + Logger &logger) + : m_engine(engine) + , m_pool(0) + , m_logger(logger) + , m_progressObserver(0) + , m_reader(new ItemReader(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); + + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << "[MODLDR] module search paths:"; + foreach (const QString &path, m_moduleSearchPaths) + m_logger.qbsTrace() << " " << path; + } +} + +void ModuleLoader::setOldProbes(const QHash> &oldProbes) +{ + m_oldProbes = oldProbes; +} + +ModuleLoaderResult ModuleLoader::load(const SetupProjectParameters ¶meters) +{ + TimedActivityLogger moduleLoaderTimer(m_logger, Tr::tr("ModuleLoader"), + parameters.logElapsedTime()); + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] load" << parameters.projectFilePath(); + m_parameters = parameters; + m_productModuleCache.clear(); + m_modulePrototypeItemCache.clear(); + m_disabledItems.clear(); + m_reader->clearExtraSearchPathsStack(); + m_reader->setEnableTiming(parameters.logElapsedTime()); + m_elapsedTimeProbes = 0; + + ModuleLoaderResult result; + m_pool = result.itemPool.data(); + m_reader->setPool(m_pool); + + const QStringList topLevelSearchPaths = parameters.finalBuildConfigurationTree() + .value(QLatin1String("project")).toMap() + .value(QLatin1String("qbsSearchPaths")).toStringList(); + Item *root; + { + SearchPathsManager searchPathsManager(m_reader, topLevelSearchPaths); + root = m_reader->readFile(parameters.projectFilePath()); + if (!root) + return ModuleLoaderResult(); + } + + root = wrapInProjectIfNecessary(root); + + const QString buildDirectory = TopLevelProject::deriveBuildDirectory(parameters.buildRoot(), + TopLevelProject::deriveId(parameters.finalBuildConfigurationTree())); + root->setProperty(QLatin1String("sourceDirectory"), + VariantValue::create(QFileInfo(root->file()->filePath()).absolutePath())); + root->setProperty(QLatin1String("buildDirectory"), VariantValue::create(buildDirectory)); + root->setProperty(QLatin1String("profile"), + VariantValue::create(m_parameters.topLevelProfile())); + handleTopLevelProject(&result, root, buildDirectory, + QSet() << QDir::cleanPath(parameters.projectFilePath())); + result.root = root; + result.qbsFiles = m_reader->filesRead(); + printProfilingInfo(); + return result; +} + +static void handlePropertyError(const ErrorInfo &error, const SetupProjectParameters ¶ms, + Logger &logger) +{ + if (params.propertyCheckingMode() == ErrorHandlingMode::Strict) + throw error; + logger.printWarning(error); +} + +class PropertyDeclarationCheck : public ValueHandler +{ + const QSet &m_disabledItems; + QSet m_handledItems; + Item *m_parentItem; + QString m_currentName; + SetupProjectParameters m_params; + Logger &m_logger; +public: + PropertyDeclarationCheck(const QSet &disabledItems, + const SetupProjectParameters ¶ms, Logger &logger) + : m_disabledItems(disabledItems) + , m_parentItem(0) + , m_params(params) + , m_logger(logger) + { + } + + void operator()(Item *item) + { + handleItem(item); + } + +private: + void handle(JSSourceValue *value) + { + if (!value->createdByPropertiesBlock()) { + const ErrorInfo error(Tr::tr("Property '%1' is not declared.") + .arg(m_currentName), value->location()); + handlePropertyError(error, m_params, m_logger); + } + } + + void handle(ItemValue *value) + { + // TODO: Remove once QBS-1030 is fixed. + if (m_parentItem->type() == ItemType::Artifact) + return; + + if (value->item()->type() != ItemType::ModuleInstance + && value->item()->type() != ItemType::ModulePrefix + && m_parentItem->file() + && (!m_parentItem->file()->idScope() + || !m_parentItem->file()->idScope()->hasProperty(m_currentName)) + && !value->createdByPropertiesBlock()) { + const ErrorInfo error(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()); + handlePropertyError(error, m_params, m_logger); + } else { + handleItem(value->item()); + } + } + + void handleItem(Item *item) + { + if (m_handledItems.contains(item)) + return; + m_handledItems.insert(item); + if (m_disabledItems.contains(item) + // TODO: We never checked module prototypes, apparently. Should we? + // It's currently not possible because of e.g. things like "cpp.staticLibraries" + // inside Artifact items... + || item->type() == ItemType::Module + + // The Properties child of a SubProject item is not a regular item. + || item->type() == ItemType::PropertiesInSubProject) { + return; + } + + Item *oldParentItem = m_parentItem; + m_parentItem = item; + for (Item::PropertyMap::const_iterator it = item->properties().constBegin(); + it != item->properties().constEnd(); ++it) { + const PropertyDeclaration decl = item->propertyDeclaration(it.key()); + if (decl.isValid()) { + if (!decl.isDeprecated()) + continue; + const DeprecationInfo &di = decl.deprecationInfo(); + QString message; + bool warningOnly; + if (di.removalVersion() <= Version::qbsVersion()) { + message = Tr::tr("The property '%1' can no longer be used. " + "It was removed in Qbs %2.") + .arg(decl.name(), di.removalVersion().toString()); + warningOnly = false; + } else { + message = Tr::tr("The property '%1' is deprecated and will be removed " + "in Qbs %2.").arg(decl.name(), di.removalVersion().toString()); + warningOnly = true; + } + ErrorInfo error(message, it.value()->location()); + if (!di.additionalUserInfo().isEmpty()) + error.append(di.additionalUserInfo()); + if (warningOnly) + m_logger.printWarning(error); + else + handlePropertyError(error, m_params, m_logger); + continue; + } + m_currentName = it.key(); + it.value()->apply(this); + } + m_parentItem = oldParentItem; + foreach (Item *child, item->children()) { + if (child->type () != ItemType::Export) + handleItem(child); + } + + // Properties that don't refer to an existing module with a matching Depends item + // only exist in the prototype of an Export item, not in the instance. + // Example 1 - setting a property of an unknown module: Export { abc.def: true } + // Example 2 - setting a non-existing Export property: Export { blubb: true } + if (item->type() == ItemType::ModuleInstance && item->prototype()) + handleItem(item->prototype()); + } + + void handle(VariantValue *) { /* only created internally - no need to check */ } +}; + +void ModuleLoader::handleTopLevelProject(ModuleLoaderResult *loadResult, Item *projectItem, + const QString &buildDirectory, const QSet &referencedFilePaths) +{ + TopLevelProjectContext tlp; + tlp.buildDirectory = buildDirectory; + handleProject(loadResult, &tlp, projectItem, referencedFilePaths); + + foreach (ProjectContext *projectContext, tlp.projects) { + m_reader->setExtraSearchPathsStack(projectContext->searchPathsStack); + for (auto it = projectContext->products.begin(); it != projectContext->products.end(); + ++it) { + try { + setupProductDependencies(it); + } catch (const ErrorInfo &err) { + if (it->name.isEmpty()) + throw err; + handleProductError(err, it); + } + } + } + + ProductSortByDependencies productSorter(tlp); + productSorter.apply(); + foreach (ProductContext * const p, productSorter.sortedProducts()) { + try { + handleProduct(p); + } catch (const ErrorInfo &err) { + handleProductError(err, p); + } + } + + m_reader->clearExtraSearchPathsStack(); + PropertyDeclarationCheck check(m_disabledItems, m_parameters, m_logger); + check(projectItem); +} + +void ModuleLoader::handleProject(ModuleLoaderResult *loadResult, + TopLevelProjectContext *topLevelProjectContext, Item *projectItem, + const QSet &referencedFilePaths) +{ + auto *p = new ProjectContext; + auto &projectContext = *p; + projectContext.topLevelProject = topLevelProjectContext; + projectContext.result = loadResult; + ItemValuePtr itemValue = ItemValue::create(projectItem); + projectContext.scope = Item::create(m_pool); + projectContext.scope->setFile(projectItem->file()); + projectContext.scope->setProperty(QLatin1String("project"), itemValue); + ProductContext dummyProductContext; + dummyProductContext.project = &projectContext; + dummyProductContext.moduleProperties = m_parameters.finalBuildConfigurationTree(); + projectItem->addModule(loadBaseModule(&dummyProductContext, projectItem)); + overrideItemProperties(projectItem, QLatin1String("project"), + m_parameters.overriddenValuesTree()); + const QString projectName = m_evaluator->stringValue(projectItem, QLatin1String("name")); + if (!projectName.isEmpty()) + overrideItemProperties(projectItem, projectName, m_parameters.overriddenValuesTree()); + if (!checkItemCondition(projectItem)) { + delete p; + return; + } + topLevelProjectContext->projects << &projectContext; + m_reader->pushExtraSearchPaths(readExtraSearchPaths(projectItem) + << projectItem->file()->dirPath()); + projectContext.searchPathsStack = m_reader->extraSearchPathsStack(); + projectContext.item = projectItem; + + const QString minVersionStr + = m_evaluator->stringValue(projectItem, QLatin1String("minimumQbsVersion"), + QLatin1String("1.3.0")); + const Version minVersion = Version::fromString(minVersionStr); + if (!minVersion.isValid()) { + throw ErrorInfo(Tr::tr("The value '%1' of Project.minimumQbsVersion " + "is not a valid version string.").arg(minVersionStr), projectItem->location()); + } + if (!m_qbsVersion.isValid()) + m_qbsVersion = Version::fromString(QLatin1String(QBS_VERSION)); + if (m_qbsVersion < minVersion) { + throw ErrorInfo(Tr::tr("The project requires at least qbs version %1, but " + "this is qbs version %2.").arg(minVersion.toString(), + m_qbsVersion.toString())); + } + + foreach (Item *child, projectItem->children()) { + child->setScope(projectContext.scope); + if (child->type() == ItemType::Product) { + foreach (Item * const additionalProductItem, + multiplexProductItem(&dummyProductContext, child)) { + Item::addChild(projectItem, additionalProductItem); + } + } + } + + foreach (Item *child, projectItem->children()) { + switch (child->type()) { + case ItemType::Product: + prepareProduct(&projectContext, child); + break; + case ItemType::SubProject: + handleSubProject(&projectContext, child, referencedFilePaths); + break; + case ItemType::Project: + copyProperties(projectItem, child); + handleProject(loadResult, topLevelProjectContext, child, referencedFilePaths); + break; + default: + break; + } + } + + const QStringList refs = m_evaluator->stringListValue(projectItem, QLatin1String("references")); + const CodeLocation referencingLocation + = projectItem->property(QLatin1String("references"))->location(); + QList additionalProjectChildren; + foreach (const QString &filePath, refs) { + try { + additionalProjectChildren << loadReferencedFile(filePath, referencingLocation, + referencedFilePaths, dummyProductContext); + } catch (const ErrorInfo &error) { + if (m_parameters.productErrorMode() == ErrorHandlingMode::Strict) + throw; + m_logger.printWarning(error); + } + } + foreach (Item * const subItem, additionalProjectChildren) { + Item::addChild(projectContext.item, subItem); + switch (subItem->type()) { + case ItemType::Product: + prepareProduct(&projectContext, subItem); + break; + case ItemType::Project: + copyProperties(projectItem, subItem); + handleProject(loadResult, topLevelProjectContext, subItem, + QSet(referencedFilePaths) << subItem->file()->filePath()); + break; + default: + break; + } + } + m_reader->popExtraSearchPaths(); +} + +QList ModuleLoader::multiplexProductItem(ProductContext *dummyContext, Item *productItem) +{ + // Temporarily attach the qbs module here, in case we need to access one of its properties + // to evaluate the profiles property. + const QString qbsKey = QLatin1String("qbs"); + ValuePtr qbsValue = productItem->property(qbsKey); // Retrieve now to restore later. + if (qbsValue) + qbsValue = qbsValue->clone(); + productItem->addModule(loadBaseModule(dummyContext, productItem)); + + // Overriding the product item properties must be done here already, because otherwise + // the "profiles" property would not be overridable. + QString productName = m_evaluator->stringValue(productItem, QLatin1String("name")); + if (productName.isEmpty()) { + productName = FileInfo::completeBaseName(productItem->file()->filePath()); + productItem->setProperty(QLatin1String("name"), VariantValue::create(productName)); + } + overrideItemProperties(productItem, productName, m_parameters.overriddenValuesTree()); + + const QString profilesKey = QLatin1String("profiles"); + const ValueConstPtr profilesValue = productItem->property(profilesKey); + QBS_CHECK(profilesValue); // Default value set in BuiltinDeclarations. + const QStringList profileNames = m_evaluator->stringListValue(productItem, profilesKey); + if (profileNames.isEmpty()) { + throw ErrorInfo(Tr::tr("The 'profiles' property cannot be an empty list."), + profilesValue->location()); + } + foreach (const QString &profileName, profileNames) { + if (profileNames.count(profileName) > 1) { + throw ErrorInfo(Tr::tr("The profile '%1' appears in the 'profiles' list twice, " + "which is not allowed.").arg(profileName), profilesValue->location()); + } + } + + // "Unload" the qbs module again. + if (qbsValue) + productItem->setProperty(qbsKey, qbsValue); + else + productItem->removeProperty(qbsKey); + productItem->removeModules(); + + QList additionalProductItems; + const QString profileKey = QLatin1String("profile"); + productItem->setProperty(profileKey, VariantValue::create(profileNames.first())); + Settings settings(m_parameters.settingsDirectory()); + for (int i = 0; i < profileNames.count(); ++i) { + Profile profile(profileNames.at(i), &settings); + if (!profile.exists()) { + throw ErrorInfo(Tr::tr("The profile '%1' does not exist.").arg(profile.name()), + productItem->location()); // TODO: profilesValue->location() is invalid, why? + } + if (i == 0) + continue; // We use the original item for the first profile. + Item * const cloned = productItem->clone(); + cloned->setProperty(profileKey, VariantValue::create(profileNames.at(i))); + additionalProductItems << cloned; + } + return additionalProductItems; +} + +void ModuleLoader::prepareProduct(ProjectContext *projectContext, Item *productItem) +{ + checkCancelation(); + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] prepareProduct " << productItem->file()->filePath(); + + ProductContext productContext; + productContext.name = m_evaluator->stringValue(productItem, QLatin1String("name")); + QBS_CHECK(!productContext.name.isEmpty()); + bool profilePropertySet; + productContext.profileName = m_evaluator->stringValue(productItem, QLatin1String("profile"), + QString(), &profilePropertySet); + QBS_CHECK(profilePropertySet); + const QVariantMap::ConstIterator it + = projectContext->result->profileConfigs.find(productContext.profileName); + if (it == projectContext->result->profileConfigs.constEnd()) { + const QVariantMap buildConfig = SetupProjectParameters::expandedBuildConfiguration( + m_parameters.settingsDirectory(), productContext.profileName, + m_parameters.configurationName()); + productContext.moduleProperties = SetupProjectParameters::finalBuildConfigurationTree( + buildConfig, m_parameters.overriddenValues(), m_parameters.buildRoot()); + projectContext->result->profileConfigs.insert(productContext.profileName, + productContext.moduleProperties); + } else { + productContext.moduleProperties = it.value().toMap(); + } + productContext.item = productItem; + productContext.project = projectContext; + initProductProperties(productContext); + + ItemValuePtr itemValue = ItemValue::create(productItem); + productContext.scope = Item::create(m_pool); + productContext.scope->setProperty(QLatin1String("product"), itemValue); + productContext.scope->setFile(productItem->file()); + productContext.scope->setScope(productContext.project->scope); + + mergeExportItems(productContext); + + setScopeForDescendants(productItem, productContext.scope); + + projectContext->products << productContext; +} + +void ModuleLoader::setupProductDependencies(ProductContext *productContext) +{ + checkCancelation(); + Item *item = productContext->item; + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] handleProduct " << item->file()->filePath(); + + QStringList extraSearchPaths = readExtraSearchPaths(item); + Settings settings(m_parameters.settingsDirectory()); + const QStringList prefsSearchPaths + = Preferences(&settings, productContext->profileName).searchPaths(); + foreach (const QString &p, prefsSearchPaths) { + if (!m_moduleSearchPaths.contains(p) && FileInfo(p).exists()) + extraSearchPaths << p; + } + SearchPathsManager searchPathsManager(m_reader, extraSearchPaths); + + DependsContext dependsContext; + dependsContext.product = productContext; + dependsContext.productDependencies = &productContext->info.usedProducts; + resolveDependencies(&dependsContext, item); + addTransitiveDependencies(productContext); + productContext->project->result->productInfos.insert(item, productContext->info); +} + +// Non-dependencies first. +static void createSortedModuleList(const Item::Module &parentModule, QVector &modules) +{ + if (modules.contains(parentModule)) + return; + foreach (const Item::Module &dep, parentModule.item->modules()) + createSortedModuleList(dep, modules); + modules << parentModule; + return; +} + +template bool insertIntoSet(std::set &set, const T &value) +{ + const auto insertionResult = set.insert(value); + return insertionResult.second; +} + +void ModuleLoader::setupReverseModuleDependencies(const Item::Module &module, + ModuleDependencies &deps, + QualifiedIdSet &seenModules) +{ + if (!insertIntoSet(seenModules, module.name)) + return; + const Item::Modules &modules = module.item->modules(); + for (auto it = modules.begin(); it != modules.end(); ++it) { + deps[it->name].insert(module.name); + setupReverseModuleDependencies(*it, deps, seenModules); + } +} + +ModuleLoader::ModuleDependencies ModuleLoader::setupReverseModuleDependencies(const Item *product) +{ + ModuleDependencies deps; + QualifiedIdSet seenModules; + const Item::Modules &modules = product->modules(); + for (auto it = modules.begin(); it != modules.end(); ++it) + setupReverseModuleDependencies(*it, deps, seenModules); + return deps; +} + +void ModuleLoader::handleProduct(ModuleLoader::ProductContext *productContext) +{ + if (productContext->info.hasError) + return; + + Item * const item = productContext->item; + + Item::Modules mergedModules; + foreach (const Item::Module &module, item->modules()) { + Item::Module mergedModule = module; + ModuleMerger(m_logger, item, mergedModule).start(); + mergedModules << mergedModule; + } + item->setModules(mergedModules); + + // Must happen after all modules have been merged, so needs to be a second loop. + QVector sortedModules; + foreach (const Item::Module &module, item->modules()) + createSortedModuleList(module, sortedModules); + QBS_CHECK(sortedModules.count() == item->modules().count()); + + foreach (const Item::Module &module, sortedModules) { + if (!module.item->isPresentModule()) + continue; + try { + resolveProbes(productContext, module.item); + if (module.versionRange.minimum.isValid() + || module.versionRange.maximum.isValid()) { + if (module.versionRange.maximum.isValid() + && module.versionRange.minimum >= module.versionRange.maximum) { + throw ErrorInfo(Tr::tr("Impossible version constraint [%1,%2) set for module " + "'%3'").arg(module.versionRange.minimum.toString(), + module.versionRange.maximum.toString(), + module.name.toString())); + } + const Version moduleVersion = Version::fromString( + m_evaluator->stringValue(module.item, QLatin1String("version"))); + if (moduleVersion < module.versionRange.minimum) { + throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be " + "at least %3.").arg(module.name.toString(), + moduleVersion.toString(), + module.versionRange.minimum.toString())); + } + if (module.versionRange.maximum.isValid() + && moduleVersion >= module.versionRange.maximum) { + throw ErrorInfo(Tr::tr("Module '%1' has version %2, but it needs to be " + "lower than %3.").arg(module.name.toString(), + moduleVersion.toString(), + module.versionRange.maximum.toString())); + } + } + } catch (const ErrorInfo &error) { + handleModuleSetupError(productContext, module, error); + } + } + + resolveProbes(productContext, item); + + // Module validation must happen in an extra pass, after all Probes have been resolved. + EvalCacheEnabler cacheEnabler(m_evaluator); + foreach (const Item::Module &module, sortedModules) { + if (!module.item->isPresentModule() || module.item->delayedError().hasError()) + continue; + try { + m_evaluator->boolValue(module.item, QLatin1String("validate")); + } catch (const ErrorInfo &error) { + handleModuleSetupError(productContext, module, error); + } + } + + if (!checkItemCondition(item)) { + Item * const productModule = m_productModuleCache.value(productContext->name); + if (productModule && productModule->isPresentModule()) + createNonPresentModule(productContext->name, QLatin1String("disabled"), productModule); + } + + copyGroupsFromModulesToProduct(*productContext); + + ModuleDependencies reverseModuleDeps; + foreach (Item *child, item->children()) { + if (child->type() == ItemType::Group) { + if (reverseModuleDeps.isEmpty()) + reverseModuleDeps = setupReverseModuleDependencies(item); + handleGroup(child, reverseModuleDeps); + } + } + productContext->project->result->productInfos.insert(item, productContext->info); +} + +void ModuleLoader::handleModuleSetupError(ModuleLoader::ProductContext *productContext, + const Item::Module &module, const ErrorInfo &error) +{ + if (module.required) { + try { + handleProductError(error, productContext); + } catch (const ErrorInfo &) { + // Error will be thrown for enabled products only + module.item->setDelayedError(error); + } + } else { + createNonPresentModule(module.name.toString(), QLatin1String("failed validation"), + module.item); + } +} + +void ModuleLoader::initProductProperties(const ProductContext &product) +{ + QString buildDir = ResolvedProduct::deriveBuildDirectoryName(product.name, product.profileName); + buildDir = FileInfo::resolvePath(product.project->topLevelProject->buildDirectory, buildDir); + product.item->setProperty(QLatin1String("buildDirectory"), VariantValue::create(buildDir)); + const QString sourceDir = QFileInfo(product.item->file()->filePath()).absolutePath(); + product.item->setProperty(QLatin1String("sourceDirectory"), VariantValue::create(sourceDir)); +} + +void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext, Item *projectItem, + const QSet &referencedFilePaths) +{ + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] handleSubProject " << projectItem->file()->filePath(); + + Item * const propertiesItem = projectItem->child(ItemType::PropertiesInSubProject); + bool subProjectEnabled = true; + if (propertiesItem) { + propertiesItem->setScope(projectItem); + subProjectEnabled = checkItemCondition(propertiesItem); + } + if (!subProjectEnabled) + return; + + Item *loadedItem; + QString subProjectFilePath; + try { + const QString projectFileDirPath = FileInfo::path(projectItem->file()->filePath()); + const QString relativeFilePath + = m_evaluator->stringValue(projectItem, QLatin1String("filePath")); + subProjectFilePath = FileInfo::resolvePath(projectFileDirPath, relativeFilePath); + if (referencedFilePaths.contains(subProjectFilePath)) + throw ErrorInfo(Tr::tr("Cycle detected while loading subproject file '%1'.") + .arg(relativeFilePath), projectItem->location()); + loadedItem = m_reader->readFile(subProjectFilePath); + } catch (const ErrorInfo &error) { + if (m_parameters.productErrorMode() == ErrorHandlingMode::Strict) + throw; + m_logger.printWarning(error); + return; + } + + loadedItem = wrapInProjectIfNecessary(loadedItem); + const bool inheritProperties + = m_evaluator->boolValue(projectItem, QLatin1String("inheritProperties"), true); + + if (inheritProperties) + copyProperties(projectItem->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())); + } + } + + Item::addChild(projectItem, loadedItem); + projectItem->setScope(projectContext->scope); + handleProject(projectContext->result, projectContext->topLevelProject, loadedItem, + QSet(referencedFilePaths) << subProjectFilePath); +} + +QList ModuleLoader::loadReferencedFile(const QString &relativePath, + const CodeLocation &referencingLocation, + const QSet &referencedFilePaths, + ModuleLoader::ProductContext &dummyContext) +{ + QString absReferencePath = FileInfo::resolvePath(FileInfo::path(referencingLocation.filePath()), + relativePath); + 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), referencingLocation); + } + qbsFilePath = dit.next(); + } + if (qbsFilePath.isEmpty()) { + throw ErrorInfo(Tr::tr("Referenced directory '%1' does not contain a qbs file.") + .arg(absReferencePath), referencingLocation); + } + absReferencePath = qbsFilePath; + } + if (referencedFilePaths.contains(absReferencePath)) + throw ErrorInfo(Tr::tr("Cycle detected while referencing file '%1'.").arg(relativePath), + referencingLocation); + Item * const subItem = m_reader->readFile(absReferencePath); + if (subItem->type() != ItemType::Project && subItem->type() != ItemType::Product) { + ErrorInfo error(Tr::tr("Item type should be 'Product' or 'Project', but is '%1'.") + .arg(subItem->typeName())); + error.append(Tr::tr("Item is defined here."), subItem->location()); + error.append(Tr::tr("File is referenced here."), referencingLocation); + throw error; + } + subItem->setScope(dummyContext.project->scope); + subItem->setParent(dummyContext.project->item); + QList loadedItems; + loadedItems << subItem; + if (subItem->type() == ItemType::Product) + loadedItems << multiplexProductItem(&dummyContext, subItem); + return loadedItems; +} + +void ModuleLoader::handleGroup(Item *groupItem, const ModuleDependencies &reverseDepencencies) +{ + checkCancelation(); + propagateModulesFromParent(groupItem, reverseDepencencies); + checkItemCondition(groupItem); + foreach (Item * const child, groupItem->children()) { + if (child->type() == ItemType::Group) + handleGroup(child, reverseDepencencies); + } +} + +void ModuleLoader::handleAllPropertyOptionsItems(Item *item) +{ + foreach (Item * const child, item->children()) { + if (child->type() == ItemType::PropertyOptions) + handlePropertyOptions(child); + } +} + +void ModuleLoader::handlePropertyOptions(Item *optionsItem) +{ + const QString name = m_evaluator->stringValue(optionsItem, QLatin1String("name")); + if (name.isEmpty()) { + throw ErrorInfo(Tr::tr("PropertyOptions item needs a name property"), + optionsItem->location()); + } + if (!optionsItem->parent()->hasProperty(name)) { + throw ErrorInfo(Tr::tr("PropertyOptions item refers to non-existing property '%1'") + .arg(name), optionsItem->location()); + } + const QString description = m_evaluator->stringValue(optionsItem, QLatin1String("description")); + const auto removalVersion = Version::fromString(m_evaluator->stringValue(optionsItem, + QLatin1String("removalVersion"))); + PropertyDeclaration decl = optionsItem->parent()->propertyDeclaration(name); + if (!decl.isValid()) { + decl.setName(name); + decl.setType(PropertyDeclaration::Variant); + } + decl.setDescription(description); + if (removalVersion.isValid()) { + DeprecationInfo di(removalVersion, description); + decl.setDeprecationInfo(di); + } + optionsItem->parent()->setPropertyDeclaration(name, decl); +} + +static void mergeProperty(Item *dst, const QString &name, const ValuePtr &value) +{ + if (value->type() == Value::ItemValueType) { + Item *valueItem = value.staticCast()->item(); + Item *subItem = dst->itemProperty(name, true)->item(); + for (QMap::const_iterator it = valueItem->properties().constBegin(); + it != valueItem->properties().constEnd(); ++it) + mergeProperty(subItem, it.key(), it.value()); + } else { + // If the property already exists set up the base value. + if (value->type() == Value::JSSourceValueType) { + const ValuePtr baseValue = dst->property(name); + if (baseValue) { + QBS_CHECK(baseValue->type() == Value::JSSourceValueType); + const JSSourceValuePtr jsBaseValue = baseValue->clone().staticCast(); + JSSourceValue *jsValue = static_cast(value.data()); + jsValue->setBaseValue(jsBaseValue); + } + } + dst->setProperty(name, value); + } +} + +bool ModuleLoader::checkExportItemCondition(Item *exportItem, const ProductContext &productContext) +{ + class ScopeHandler { + public: + ScopeHandler(Item *exportItem, const ProductContext &productContext, Item **cachedScopeItem) + : m_exportItem(exportItem) + { + if (!*cachedScopeItem) + *cachedScopeItem = Item::create(exportItem->pool()); + Item * const scope = *cachedScopeItem; + QBS_CHECK(productContext.item->file()); + scope->setFile(productContext.item->file()); + scope->setScope(productContext.item); + productContext.project->scope->copyProperty(QLatin1String("project"), scope); + productContext.scope->copyProperty(QLatin1String("product"), scope); + QBS_CHECK(!exportItem->scope()); + exportItem->setScope(scope); + } + ~ScopeHandler() { m_exportItem->setScope(nullptr); } + + private: + Item * const m_exportItem; + } scopeHandler(exportItem, productContext, &m_tempScopeItem); + return checkItemCondition(exportItem); +} + +ProbeConstPtr ModuleLoader::findOldProbe(const QString &product, + bool condition, + const QVariantMap &initialProperties, + const QString &sourceCode) const +{ + if (m_parameters.forceProbeExecution()) + return ProbeConstPtr(); + foreach (const ProbeConstPtr &oldProbe, m_oldProbes.value(product)) { + if (oldProbe->condition() != condition) + continue; + if (oldProbe->configureScript() != sourceCode) + continue; + if (oldProbe->initialProperties() != initialProperties) + continue; + return oldProbe; + } + + return ProbeConstPtr(); +} + +ProbeConstPtr ModuleLoader::findCurrentProbe(const CodeLocation &location, bool condition, + const QVariantMap &initialProperties) const +{ + const QList cachedProbes = m_currentProbes.value(location); + foreach (const ProbeConstPtr &p, cachedProbes) { + if (p->condition() == condition && p->initialProperties() == initialProperties) + return p; + } + return ProbeConstPtr(); +} + +void ModuleLoader::printProfilingInfo() +{ + if (!m_parameters.logElapsedTime()) + return; + m_logger.qbsLog(LoggerInfo, true) << "\t" + << Tr::tr("Project file loading and parsing took %1.") + .arg(elapsedTimeString(m_reader->elapsedTime())); + m_logger.qbsLog(LoggerInfo, true) << "\t" + << Tr::tr("Running Probes took %1.") + .arg(elapsedTimeString(m_elapsedTimeProbes)); +} + +void ModuleLoader::mergeExportItems(const ProductContext &productContext) +{ + QVector exportItems; + QList children = productContext.item->children(); + for (int i = 0; i < children.count();) { + Item * const child = children.at(i); + if (child->type() == ItemType::Export) { + exportItems << child; + children.removeAt(i); + } else { + ++i; + } + } + + // Note that we do not return if there are no Export items: The "merged" item becomes the + // "product module", which always needs to exist, regardless of whether the product sources + // actually contain an Export item or not. + if (!exportItems.isEmpty()) + productContext.item->setChildren(children); + + Item *merged = Item::create(productContext.item->pool(), ItemType::Export); + QSet filesWithExportItem; + foreach (Item *exportItem, exportItems) { + checkCancelation(); + if (Q_UNLIKELY(filesWithExportItem.contains(exportItem->file()))) + throw ErrorInfo(Tr::tr("Multiple Export items in one product are prohibited."), + exportItem->location()); + if (!checkExportItemCondition(exportItem, productContext)) + continue; + filesWithExportItem += exportItem->file(); + foreach (Item *child, exportItem->children()) + Item::addChild(merged, child); + const Item::PropertyDeclarationMap &decls = exportItem->propertyDeclarations(); + for (auto it = decls.constBegin(); it != decls.constEnd(); ++it) { + const PropertyDeclaration &newDecl = it.value(); + const PropertyDeclaration &existingDecl = merged->propertyDeclaration(it.key()); + if (existingDecl.isValid() && existingDecl.type() != newDecl.type()) { + ErrorInfo error(Tr::tr("Export item in inherited item redeclares property " + "'%1' with different type.").arg(it.key()), exportItem->location()); + handlePropertyError(error, m_parameters, m_logger); + } + merged->setPropertyDeclaration(newDecl.name(), newDecl); + } + for (QMap::const_iterator it = exportItem->properties().constBegin(); + it != exportItem->properties().constEnd(); ++it) { + mergeProperty(merged, it.key(), it.value()); + } + } + merged->setFile(exportItems.isEmpty() + ? productContext.item->file() : exportItems.last()->file()); + merged->setLocation(exportItems.isEmpty() + ? productContext.item->location() : exportItems.last()->location()); + Item::addChild(productContext.item, merged); + merged->setupForBuiltinType(m_logger); + ProductModuleInfo &pmi + = productContext.project->topLevelProject->productModules[productContext.name]; + pmi.exportItem = merged; +} + +bool ModuleLoader::isSomeModulePropertySet(const Item *item) +{ + for (auto it = item->properties().cbegin(); it != item->properties().cend(); ++it) { + switch (it.value()->type()) { + case Value::JSSourceValueType: + if (item->type() == ItemType::ModuleInstance) { + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << "[LDR] scope adaptation for group module items " + "necessary because of property " << it.key(); + } + return true; + } + break; + case Value::ItemValueType: + if (isSomeModulePropertySet(it.value().staticCast()->item())) + return true; + break; + default: + break; + } + } + return false; +} + +void ModuleLoader::propagateModulesFromParent(Item *groupItem, + const ModuleDependencies &reverseDepencencies) +{ + QBS_CHECK(groupItem->type() == ItemType::Group); + QHash moduleInstancesForGroup; + + // Step 1: Instantiate the product's modules for the group. + for (Item::Modules::const_iterator it = groupItem->parent()->modules().constBegin(); + it != groupItem->parent()->modules().constEnd(); ++it) + { + Item::Module m = *it; + Item *targetItem = moduleInstanceItem(groupItem, m.name); + targetItem->setPrototype(m.item); + targetItem->setType(ItemType::ModuleInstance); + + Item * const moduleScope = Item::create(targetItem->pool(), ItemType::Scope); + moduleScope->setFile(groupItem->file()); + moduleScope->setProperties(m.item->scope()->properties()); // "project", "product", ids + moduleScope->setScope(groupItem); + targetItem->setScope(moduleScope); + + targetItem->setFile(m.item->file()); + + // "parent" should point to the group/artifact parent + targetItem->setParent(groupItem->parent()); + + targetItem->setOuterItem(m.item); + + m.item = targetItem; + groupItem->addModule(m); + moduleInstancesForGroup.insert(m.name, targetItem); + } + + // Step 2: Make the inter-module references point to the instances created in step 1. + for (auto it = groupItem->modules().cbegin(); it != groupItem->modules().cend(); ++it) { + Item::Modules adaptedModules; + const Item::Modules &oldModules = it->item->prototype()->modules(); + for (auto depIt = oldModules.begin(); depIt != oldModules.end(); ++depIt) { + Item::Module depMod = *depIt; + depMod.item = moduleInstancesForGroup.value(depIt->name); + adaptedModules << depMod; + if (depMod.name.first() == it->name.first()) + continue; + const ItemValuePtr &modulePrefix = groupItem->itemProperty(depMod.name.first()); + QBS_CHECK(modulePrefix); + it->item->setProperty(depMod.name.first(), modulePrefix); + } + it->item->setModules(adaptedModules); + } + + if (!isSomeModulePropertySet(groupItem)) + return; + + // Step 3: Adapt defining items in values. This is potentially necessary if module properties + // get assigned on the group level. + const Item::Modules &groupModules = groupItem->modules(); + for (auto modIt = groupModules.begin(); modIt != groupModules.end(); ++modIt) { + const QualifiedIdSet &dependents = reverseDepencencies.value(modIt->name); + Item::Modules dependentModules; + dependentModules.reserve(int(dependents.size())); + for (auto depIt = dependents.begin(); depIt != dependents.end(); ++depIt) { + Item * const itemOfDependent = moduleInstancesForGroup.value(*depIt); + QBS_CHECK(itemOfDependent); + Item::Module depMod; + depMod.name = *depIt; + depMod.item = itemOfDependent; + dependentModules << depMod; + } + adjustDefiningItemsInGroupModuleInstances(*modIt, dependentModules); + } +} + +void ModuleLoader::adjustDefiningItemsInGroupModuleInstances(const Item::Module &module, + const Item::Modules &dependentModules) +{ + // There are three cases: + // a) The defining item is the "main" module instance, i.e. the one instantiated in the + // product directly (or a parent group). + // b) The defining item refers to the module prototype (or the replacement of it + // created in the module merger [for products] or in this function [for parent groups]). + // c) The defining item is a different instance of the module, i.e. it was instantiated + // in some other module. + + QHash definingItemReplacements; + + Item *modulePrototype = module.item->prototype(); + while (modulePrototype->prototype()) + modulePrototype = modulePrototype->prototype(); + + // TODO: Why are there module instances whose top-level prototype is of type ModuleInstance? + // QBS_CHECK(modulePrototype->type() == ItemType::Module); + + const Item::PropertyDeclarationMap &propDecls = modulePrototype->propertyDeclarations(); + for (const auto &decl : propDecls) { + const QString &propName = decl.name(); + + // Module properties assigned in the group are not relevant here, as nothing + // gets inherited in that case. In particular, setting a list property + // overwrites the value from the product's (or parent group's) instance completely, + // rather than appending to it (concatenation happens via outer.concat()). + ValueConstPtr propValue = module.item->properties().value(propName); + if (propValue) + continue; + + // Find the nearest prototype instance that has the value assigned. + // The result is either an instance of a parent group (or the parent group's + // parent group and so on) or the instance of the product or the module prototype. + // In the latter case, we don't have to do anything. + const Item *instanceWithProperty = module.item; + int prototypeChainLen = 0; + do { + instanceWithProperty = instanceWithProperty->prototype(); + QBS_CHECK(instanceWithProperty); + ++prototypeChainLen; + propValue = instanceWithProperty->properties().value(propName); + } while (!propValue); + QBS_CHECK(propValue); + + if (propValue->type() != Value::JSSourceValueType) + continue; + + bool hasDefiningItem = false; + for (ValueConstPtr v = propValue; v && !hasDefiningItem; v = v->next()) + hasDefiningItem = v->definingItem(); + if (!hasDefiningItem) { + QBS_CHECK(decl.isScalar() || instanceWithProperty == modulePrototype); + continue; + } + + const ValuePtr clonedValue = propValue->clone(); + for (ValuePtr v = clonedValue; v; v = v->next()) { + QBS_CHECK(v->definingItem()); + + Item *& replacement = definingItemReplacements[v->definingItem()]; + static const QString caseA = QLatin1String("__group_case_a"); + if (v->definingItem() == instanceWithProperty + || v->definingItem()->variantProperty(caseA)) { + // Case a) + // For values whose defining item is the product's (or parent group's) instance, + // we take its scope and replace references to module instances with those from the + // group's instance. This handles cases like the following: + // Product { + // name: "theProduct" + // aModule.listProp: [name, otherModule.stringProp] + // Group { name: "theGroup"; otherModule.stringProp: name } + // ... + // } + // In the above example, aModule.listProp is set to ["theProduct", "theGroup"] + // (plus potential values from the prototype and other module instances, + // which are different Value objects in the "next chain"). + if (!replacement) { + replacement = Item::create(v->definingItem()->pool()); + Item * const scope = Item::create(v->definingItem()->pool(), ItemType::Scope); + scope->setProperties(module.item->scope()->properties()); + Item * const scopeScope + = Item::create(v->definingItem()->pool(), ItemType::Scope); + scopeScope->setProperties(v->definingItem()->scope()->scope()->properties()); + scope->setScope(scopeScope); + replacement->setScope(scope); + const Item::PropertyMap &groupScopeProperties + = module.item->scope()->scope()->properties(); + for (auto propIt = groupScopeProperties.begin(); + propIt != groupScopeProperties.end(); ++propIt) { + if (propIt.value()->type() == Value::ItemValueType) + scopeScope->setProperty(propIt.key(), propIt.value()); + } + } + replacement->setPropertyDeclaration(propName, decl); + replacement->setProperty(propName, v); + replacement->setProperty(caseA, VariantValue::create(QVariant())); + } else if (v->definingItem()->type() == ItemType::Module) { + // Case b) + // For values whose defining item is the module prototype, we change the scope to + // the group's instance, analogous to what we do in + // ModuleMerger::appendPrototypeValueToNextChain(). + QBS_CHECK(!decl.isScalar()); + QBS_CHECK(!v->next()); + Item *& replacement = definingItemReplacements[v->definingItem()]; + if (!replacement) { + replacement = Item::create(v->definingItem()->pool(), + ItemType::Module); + replacement->setScope(module.item); + } + QBS_CHECK(!replacement->hasOwnProperty(caseA)); + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << "[LDR] replacing defining item for prototype; module is " + << module.name.toString() << module.item + << ", property is " << propName + << ", old defining item was " << v->definingItem() + << " with scope" << v->definingItem()->scope() + << ", new defining item is" << replacement + << " with scope" << replacement->scope() + << ", value source code is " + << v.staticCast()->sourceCode().toString(); + } + replacement->setPropertyDeclaration(propName, decl); + replacement->setProperty(propName, v); + } else { + // Look for instance scopes of other module instances in defining items and + // replace the affected values. + // This is case c) as introduced above. See ModuleMerger::replaceItemInScopes() + // for a detailed explanation. + + QBS_CHECK(v->definingItem()->scope() && v->definingItem()->scope()->scope()); + bool found = false; + for (auto depIt = dependentModules.cbegin(); depIt != dependentModules.cend(); + ++depIt) { + const Item::Module &depMod = *depIt; + const Item *depModPrototype = depMod.item->prototype(); + for (int i = 1; i < prototypeChainLen; ++i) + depModPrototype = depModPrototype->prototype(); + if (v->definingItem()->scope()->scope() != depModPrototype) + continue; + + found = true; + Item *& replacement = definingItemReplacements[v->definingItem()]; + if (!replacement) { + replacement = Item::create(v->definingItem()->pool()); + replacement->setProperties(v->definingItem()->properties()); + foreach (const auto &decl, v->definingItem()->propertyDeclarations()) + replacement->setPropertyDeclaration(decl.name(), decl); + replacement->setPrototype(v->definingItem()->prototype()); + replacement->setScope(Item::create(v->definingItem()->pool())); + replacement->scope()->setScope(depMod.item); + } + QBS_CHECK(!replacement->hasOwnProperty(caseA)); + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << "[LDR] reset instance scope of module " + << depMod.name.toString() << " in property " + << propName << " of module " << module.name; + } + } + QBS_CHECK(found); + } + QBS_CHECK(replacement); + v->setDefiningItem(replacement); + } + module.item->setProperty(propName, clonedValue); + } +} + +void ModuleLoader::resolveDependencies(DependsContext *dependsContext, Item *item) +{ + const Item::Module baseModule = loadBaseModule(dependsContext->product, item); + // Resolve all Depends items. + ItemModuleList loadedModules; + ProductDependencyResults productDependencies; + foreach (Item *child, item->children()) + if (child->type() == ItemType::Depends) + resolveDependsItem(dependsContext, item, child, &loadedModules, &productDependencies); + + item->addModule(baseModule); + foreach (const Item::Module &module, loadedModules) + item->addModule(module); + + dependsContext->productDependencies->append(productDependencies); +} + +class RequiredChainManager +{ +public: + RequiredChainManager(QStack &requiredChain, bool required) + : m_requiredChain(requiredChain) + { + m_requiredChain.push(required); + } + + ~RequiredChainManager() { m_requiredChain.pop(); } + +private: + QStack &m_requiredChain; +}; + +void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *parentItem, + Item *dependsItem, ItemModuleList *moduleResults, + ProductDependencyResults *productResults) +{ + checkCancelation(); + if (!checkItemCondition(dependsItem)) { + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "Depends item disabled, ignoring."; + return; + } + bool productTypesIsSet; + const FileTags productTypes = m_evaluator->fileTagsValue(dependsItem, + QLatin1String("productTypes"), &productTypesIsSet); + bool nameIsSet; + const QString name + = m_evaluator->stringValue(dependsItem, QLatin1String("name"), QString(), &nameIsSet); + bool submodulesPropertySet; + QStringList submodules = m_evaluator->stringListValue(dependsItem, QLatin1String("submodules"), + &submodulesPropertySet); + if (productTypesIsSet) { + if (nameIsSet) { + throw ErrorInfo(Tr::tr("The 'productTypes' and 'name' properties are mutually " + "exclusive."), dependsItem->location()); + } + if (submodulesPropertySet) { + throw ErrorInfo(Tr::tr("The 'productTypes' and 'subModules' properties are mutually " + "exclusive."), dependsItem->location()); + } + if (productTypes.isEmpty()) { + m_logger.qbsTrace() << "Ignoring Depends item with empty productTypes list."; + return; + } + + // TODO: We could also filter by the "profiles" property. This would required a refactoring + // (Dependency needs a list of profiles and the multiplexing must happen later). + ModuleLoaderResult::ProductInfo::Dependency dependency; + dependency.productTypes = productTypes; + dependency.limitToSubProject + = m_evaluator->boolValue(dependsItem, QLatin1String("limitToSubProject")); + productResults->append(dependency); + return; + } + if (submodules.isEmpty() && submodulesPropertySet) { + m_logger.qbsTrace() << "Ignoring Depends item with empty submodules list."; + return; + } + 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()); + } + + QList moduleNames; + const QualifiedId nameParts = QualifiedId::fromString(name); + if (submodules.isEmpty()) { + // Ignore explicit dependencies on the base module, which has already been loaded. + if (name == QStringLiteral("qbs")) + return; + + moduleNames << nameParts; + } else { + foreach (const QString &submodule, submodules) + moduleNames << nameParts + QualifiedId::fromString(submodule); + } + + Item::Module result; + foreach (const QualifiedId &moduleName, moduleNames) { + bool isRequired = m_evaluator->boolValue(dependsItem, QLatin1String("required")); + for (int i = m_requiredChain.count() - 1; i >= 0 && isRequired; --i) { + if (!m_requiredChain.at(i)) + isRequired = false; + } + const Version minVersion = Version::fromString( + m_evaluator->stringValue(dependsItem, QLatin1String("versionAtLeast"))); + const Version maxVersion = Version::fromString( + m_evaluator->stringValue(dependsItem, QLatin1String("versionBelow"))); + const VersionRange versionRange(minVersion, maxVersion); + + // Don't load the same module twice. Duplicate Depends statements can easily + // happen due to inheritance. + const auto it = std::find_if(moduleResults->begin(), moduleResults->end(), + [moduleName](const Item::Module &m) { return m.name == moduleName; }); + if (it != moduleResults->end()) { + if (isRequired) + it->required = true; + it->versionRange.narrowDown(versionRange); + continue; + } + + RequiredChainManager requiredChainManager(m_requiredChain, isRequired); + + Item *moduleItem = loadModule(dependsContext->product, parentItem, dependsItem->location(), + dependsItem->id(), moduleName, isRequired, &result.isProduct); + if (!moduleItem) { + throw ErrorInfo(Tr::tr("Dependency '%1' not found for product '%2'.") + .arg(moduleName.toString(), dependsContext->product->name), + dependsItem->location()); + } + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "module loaded: " << moduleName.toString(); + result.name = moduleName; + result.item = moduleItem; + result.required = isRequired; + result.versionRange = versionRange; + moduleResults->append(result); + if (result.isProduct) { + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "product dependency loaded: " << moduleName.toString(); + const QString profilesKey = QLatin1String("profiles"); + const QStringList profiles = m_evaluator->stringListValue(dependsItem, profilesKey); + if (profiles.isEmpty()) { + ModuleLoaderResult::ProductInfo::Dependency dependency; + dependency.name = moduleName.toString(); + dependency.profile = QLatin1String("*"); + dependency.isRequired = isRequired; + productResults->append(dependency); + continue; + } + foreach (const QString &profile, profiles) { + ModuleLoaderResult::ProductInfo::Dependency dependency; + dependency.name = moduleName.toString(); + dependency.profile = profile; + dependency.isRequired = isRequired; + productResults->append(dependency); + } + } + } +} + +Q_NORETURN static void throwModuleNamePrefixError(const QualifiedId &shortName, + const QualifiedId &longName, const CodeLocation &codeLocation) +{ + throw ErrorInfo(Tr::tr("The name of module '%1' is equal to the first component of the " + "name of module '%2', which is not allowed") + .arg(shortName.toString(), longName.toString()), codeLocation); +} + +Item *ModuleLoader::moduleInstanceItem(Item *containerItem, const QualifiedId &moduleName) +{ + QBS_CHECK(!moduleName.isEmpty()); + Item *instance = containerItem; + for (int i = 0; i < moduleName.count(); ++i) { + const QString &moduleNameSegment = moduleName.at(i); + const ValuePtr v = instance->properties().value(moduleName.at(i)); + if (v && v->type() == Value::ItemValueType) { + instance = v.staticCast()->item(); + } else { + Item *newItem = Item::create(m_pool); + instance->setProperty(moduleNameSegment, ItemValue::create(newItem)); + instance = newItem; + } + if (i < moduleName.count() - 1) { + if (instance->type() == ItemType::ModuleInstance) { + QualifiedId conflictingName = QStringList(moduleName.mid(0, i + 1)); + throwModuleNamePrefixError(conflictingName, moduleName, CodeLocation()); + } + if (instance->type() != ItemType::ModulePrefix) { + QBS_CHECK(instance->type() == ItemType::Unknown); + instance->setType(ItemType::ModulePrefix); + } + } + } + QBS_CHECK(instance != containerItem); + return instance; +} + +Item *ModuleLoader::loadProductModule(ModuleLoader::ProductContext *productContext, + const QString &moduleName) +{ + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] loadProductModule name: " << moduleName; + Item *module = m_productModuleCache.value(moduleName); + if (module) { + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] loadProductModule cache hit."; + return module; + } + ProductModuleInfo &pmi = productContext->project->topLevelProject->productModules[moduleName]; + module = pmi.exportItem; + if (module) { + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] loadProductModule cache miss."; + DependsContext dependsContext; + dependsContext.product = productContext; + dependsContext.productDependencies = &pmi.productDependencies; + resolveDependencies(&dependsContext, module); + m_productModuleCache.insert(moduleName, module); + } + return module; +} + +class ModuleLoader::DependsChainManager +{ +public: + DependsChainManager(QStack &dependsChain, const QualifiedId &module, + const CodeLocation &dependsLocation) + : m_dependsChain(dependsChain) + { + const bool alreadyInChain = std::any_of(dependsChain.cbegin(), dependsChain.cend(), + [&module](const DependsChainEntry &e) { + return e.first == module; + }); + if (alreadyInChain) { + ErrorInfo error; + error.append(Tr::tr("Cyclic dependencies detected:")); + for (auto e = m_dependsChain.cbegin(); e != m_dependsChain.cend(); ++e) + error.append(e->first.toString(), e->second); + error.append(module.toString(), dependsLocation); + throw error; + } + m_dependsChain.push(qMakePair(module, dependsLocation)); + } + + ~DependsChainManager() { m_dependsChain.pop(); } + +private: + QStack &m_dependsChain; +}; + +Item *ModuleLoader::loadModule(ProductContext *productContext, Item *item, + const CodeLocation &dependsItemLocation, + const QString &moduleId, const QualifiedId &moduleName, bool isRequired, + bool *isProductDependency) +{ + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] loadModule name: " << moduleName << ", id: " << moduleId; + + DependsChainManager dependsChainManager(m_dependsChain, moduleName, dependsItemLocation); + + Item *moduleInstance = moduleId.isEmpty() + ? moduleInstanceItem(item, moduleName) + : moduleInstanceItem(item, QStringList(moduleId)); + if (moduleInstance->type() == ItemType::ModuleInstance) { + // already handled + return moduleInstance; + } + if (Q_UNLIKELY(moduleInstance->type() == ItemType::ModulePrefix)) { + foreach (const Item::Module &m, item->modules()) { + if (m.name.first() == moduleName.first()) + throwModuleNamePrefixError(moduleName, m.name, dependsItemLocation); + } + } + QBS_CHECK(moduleInstance->type() == ItemType::Unknown); + + *isProductDependency = true; + Item *modulePrototype = loadProductModule(productContext, moduleName.toString()); + if (!modulePrototype) { + *isProductDependency = false; + QStringList moduleSearchPaths; + foreach (const QString &searchPath, m_reader->searchPaths()) + addExtraModuleSearchPath(moduleSearchPaths, searchPath); + bool cacheHit = false; + modulePrototype = searchAndLoadModuleFile(productContext, dependsItemLocation, + moduleName, moduleSearchPaths, isRequired, &cacheHit); + static const QualifiedId baseModuleId = QualifiedId(QLatin1String("qbs")); + if (modulePrototype && !cacheHit && moduleName == baseModuleId) + setupBaseModulePrototype(modulePrototype); + } + if (!modulePrototype) + return 0; + instantiateModule(productContext, nullptr, item, moduleInstance, modulePrototype, + moduleName, *isProductDependency); + return moduleInstance; +} + +Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext, + const CodeLocation &dependsItemLocation, const QualifiedId &moduleName, + const QStringList &extraSearchPaths, bool isRequired, bool *cacheHit) +{ + QStringList searchPaths = extraSearchPaths; + searchPaths.append(m_moduleSearchPaths); + + bool triedToLoadModule = false; + const QString fullName = moduleName.toString(); + 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, &triedToLoadModule); + if (module) + return module; + if (!triedToLoadModule) + m_moduleDirListCache[dirPath].removeOne(filePath); + } + } + + if (!isRequired) + return createNonPresentModule(fullName, QLatin1String("not found"), nullptr); + + 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; + } + 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(QLatin1Char('.')))); + } + return c; +} + +Item *ModuleLoader::loadModuleFile(ProductContext *productContext, const QString &fullModuleName, + bool isBaseModule, const QString &filePath, bool *cacheHit, bool *triedToLoad) +{ + checkCancelation(); + + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] trying to load " << fullModuleName << " from " << filePath; + + const ModuleItemCache::key_type cacheKey(filePath, productContext->profileName); + const ItemCacheValue cacheValue = m_modulePrototypeItemCache.value(cacheKey); + if (cacheValue.module) { + m_logger.qbsTrace() << "[LDR] loadModuleFile cache hit for " << filePath; + *cacheHit = true; + return cacheValue.enabled ? cacheValue.module : 0; + } + *cacheHit = false; + Item * const module = m_reader->readFile(filePath); + if (module->type() != ItemType::Module) { + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << "[MODLDR] Alleged module " << fullModuleName << " has type '" + << module->typeName() << "', so it's not a module after all."; + } + *triedToLoad = false; + return 0; + } + + handleAllPropertyOptionsItems(module); + + if (!isBaseModule) { + DependsContext dependsContext; + dependsContext.product = productContext; + dependsContext.productDependencies = &productContext->info.usedProducts; + resolveDependencies(&dependsContext, module); + } + + // Module properties that are defined in the profile are used as default values. + const QVariantMap profileModuleProperties + = productContext->moduleProperties.value(fullModuleName).toMap(); + QList unknownProfilePropertyErrors; + for (QVariantMap::const_iterator vmit = profileModuleProperties.begin(); + vmit != profileModuleProperties.end(); ++vmit) + { + if (Q_UNLIKELY(!module->hasProperty(vmit.key()))) { + const ErrorInfo error(Tr::tr("Unknown property: %1.%2").arg(fullModuleName, + vmit.key())); + unknownProfilePropertyErrors.append(error); + continue; + } + const PropertyDeclaration decl = module->propertyDeclaration(vmit.key()); + VariantValuePtr v = VariantValue::create(convertToPropertyType(vmit.value(), decl.type(), + QStringList(fullModuleName), vmit.key())); + module->setProperty(vmit.key(), v); + } + + // Check the condition last in case the condition needs to evaluate other properties that were + // set by the profile + if (!checkItemCondition(module)) { + m_logger.qbsTrace() << "[LDR] module condition is false"; + m_modulePrototypeItemCache.insert(cacheKey, ItemCacheValue(module, false)); + return 0; + } + + foreach (const ErrorInfo &error, unknownProfilePropertyErrors) + handlePropertyError(error, m_parameters, m_logger); + + module->setProperty(QLatin1String("name"), VariantValue::create(fullModuleName)); + m_modulePrototypeItemCache.insert(cacheKey, ItemCacheValue(module, true)); + return module; +} + +Item::Module ModuleLoader::loadBaseModule(ProductContext *productContext, Item *item) +{ + const QualifiedId baseModuleName(QLatin1String("qbs")); + Item::Module baseModuleDesc; + baseModuleDesc.name = baseModuleName; + baseModuleDesc.item = loadModule(productContext, item, CodeLocation(), QString(), + baseModuleName, true, &baseModuleDesc.isProduct); + QBS_CHECK(!baseModuleDesc.isProduct); + if (Q_UNLIKELY(!baseModuleDesc.item)) + throw ErrorInfo(Tr::tr("Cannot load base qbs module.")); + return baseModuleDesc; +} + +static QStringList hostOS() +{ + QStringList hostSystem; + +#if defined(Q_OS_AIX) + hostSystem << QLatin1String("aix"); +#endif +#if defined(Q_OS_ANDROID) + hostSystem << QLatin1String("android"); +#endif +#if defined(Q_OS_BLACKBERRY) + hostSystem << QLatin1String("blackberry"); +#endif +#if defined(Q_OS_BSD4) + hostSystem << QLatin1String("bsd") << QLatin1String("bsd4"); +#endif +#if defined(Q_OS_BSDI) + hostSystem << QLatin1String("bsdi"); +#endif +#if defined(Q_OS_CYGWIN) + hostSystem << QLatin1String("cygwin"); +#endif +#if defined(Q_OS_DARWIN) + hostSystem << QLatin1String("darwin"); +#endif +#if defined(Q_OS_DGUX) + hostSystem << QLatin1String("dgux"); +#endif +#if defined(Q_OS_DYNIX) + hostSystem << QLatin1String("dynix"); +#endif +#if defined(Q_OS_FREEBSD) + hostSystem << QLatin1String("freebsd"); +#endif +#if defined(Q_OS_HPUX) + hostSystem << QLatin1String("hpux"); +#endif +#if defined(Q_OS_HURD) + hostSystem << QLatin1String("hurd"); +#endif +#if defined(Q_OS_INTEGRITY) + hostSystem << QLatin1String("integrity"); +#endif +#if defined(Q_OS_IOS) + hostSystem << QLatin1String("ios"); +#endif +#if defined(Q_OS_IRIX) + hostSystem << QLatin1String("irix"); +#endif +#if defined(Q_OS_LINUX) + hostSystem << QLatin1String("linux"); +#endif +#if defined(Q_OS_LYNX) + hostSystem << QLatin1String("lynx"); +#endif +#if defined(Q_OS_MACOS) || defined(Q_OS_OSX) + hostSystem << QLatin1String("macos") << QLatin1String("osx"); +#endif +#if defined(Q_OS_MSDOS) + hostSystem << QLatin1String("msdos"); +#endif +#if defined(Q_OS_NACL) + hostSystem << QLatin1String("nacl"); +#endif +#if defined(Q_OS_NETBSD) + hostSystem << QLatin1String("netbsd"); +#endif +#if defined(Q_OS_OPENBSD) + hostSystem << QLatin1String("openbsd"); +#endif +#if defined(Q_OS_OS2) + hostSystem << QLatin1String("os2"); +#endif +#if defined(Q_OS_OS2EMX) + hostSystem << QLatin1String("os2emx"); +#endif +#if defined(Q_OS_OSF) + hostSystem << QLatin1String("osf"); +#endif +#if defined(Q_OS_QNX) + hostSystem << QLatin1String("qnx"); +#endif +#if defined(Q_OS_ONX6) + hostSystem << QLatin1String("qnx6"); +#endif +#if defined(Q_OS_RELIANT) + hostSystem << QLatin1String("reliant"); +#endif +#if defined(Q_OS_SCO) + hostSystem << QLatin1String("sco"); +#endif +#if defined(Q_OS_SOLARIS) + hostSystem << QLatin1String("solaris"); +#endif +#if defined(Q_OS_SYMBIAN) + hostSystem << QLatin1String("symbian"); +#endif +#if defined(Q_OS_ULTRIX) + hostSystem << QLatin1String("ultrix"); +#endif +#if defined(Q_OS_UNIX) + hostSystem << QLatin1String("unix"); +#endif +#if defined(Q_OS_UNIXWARE) + hostSystem << QLatin1String("unixware"); +#endif +#if defined(Q_OS_VXWORKS) + hostSystem << QLatin1String("vxworks"); +#endif +#if defined(Q_OS_WIN32) + hostSystem << QLatin1String("windows"); +#endif +#if defined(Q_OS_WINCE) + hostSystem << QLatin1String("windowsce"); +#endif +#if defined(Q_OS_WINPHONE) + hostSystem << QLatin1String("windowsphone"); +#endif +#if defined(Q_OS_WINRT) + hostSystem << QLatin1String("winrt"); +#endif + + return hostSystem; +} + +void ModuleLoader::setupBaseModulePrototype(Item *prototype) +{ + prototype->setProperty(QLatin1String("hostOS"), VariantValue::create(hostOS())); + prototype->setProperty(QLatin1String("libexecPath"), + VariantValue::create(m_parameters.libexecPath())); + + const Version qbsVersion = Version::qbsVersion(); + prototype->setProperty(QLatin1String("versionMajor"), + VariantValue::create(qbsVersion.majorVersion())); + prototype->setProperty(QLatin1String("versionMinor"), + VariantValue::create(qbsVersion.minorVersion())); + prototype->setProperty(QLatin1String("versionPatch"), + VariantValue::create(qbsVersion.patchLevel())); +} + +static void collectItemsWithId_impl(Item *item, QList *result) +{ + if (!item->id().isEmpty()) + result->append(item); + foreach (Item *child, item->children()) + collectItemsWithId_impl(child, result); +} + +static QList collectItemsWithId(Item *item) +{ + QList result; + collectItemsWithId_impl(item, &result); + return result; +} + +void ModuleLoader::instantiateModule(ProductContext *productContext, Item *exportingProduct, + Item *instanceScope, Item *moduleInstance, Item *modulePrototype, + const QualifiedId &moduleName, bool isProduct) +{ + const QString fullName = moduleName.toString(); + moduleInstance->setPrototype(modulePrototype); + moduleInstance->setFile(modulePrototype->file()); + moduleInstance->setLocation(modulePrototype->location()); + QBS_CHECK(moduleInstance->type() != ItemType::ModuleInstance); + moduleInstance->setType(ItemType::ModuleInstance); + + // create module scope + Item *moduleScope = Item::create(m_pool); + QBS_CHECK(instanceScope->file()); + moduleScope->setFile(instanceScope->file()); + moduleScope->setScope(instanceScope); + QBS_CHECK(productContext->project->scope); + productContext->project->scope->copyProperty(QLatin1String("project"), moduleScope); + if (productContext->scope) + productContext->scope->copyProperty(QLatin1String("product"), moduleScope); + else + QBS_CHECK(moduleName.toString() == QLatin1String("qbs")); // Dummy product. + + if (isProduct) { + exportingProduct = 0; + for (Item *exportItem = modulePrototype; exportItem && !exportingProduct; + exportItem = exportItem->prototype()) { + // exportItem is either of type ModuleInstance or Export. Only the latter has + // a parent item, which is always of type Product. + exportingProduct = exportItem->parent(); + } + QBS_CHECK(exportingProduct); + QBS_CHECK(exportingProduct->type() == ItemType::Product); + } + + if (exportingProduct) { + // TODO: For consistency with modules, it should be the other way around, i.e. + // "exportingProduct" and just "product". + moduleScope->setProperty(QLatin1String("product"), ItemValue::create(exportingProduct)); + moduleScope->setProperty(QLatin1String("importingProduct"), + ItemValue::create(productContext->item)); + + moduleScope->setProperty(QLatin1String("project"), + ItemValue::create(exportingProduct->parent())); + + PropertyDeclaration pd(QLatin1String("_qbs_sourceDir"), PropertyDeclaration::String, + PropertyDeclaration::PropertyNotAvailableInConfig); + moduleInstance->setPropertyDeclaration(pd.name(), pd); + ValuePtr v = exportingProduct->property(QLatin1String("sourceDirectory"))->clone(); + moduleInstance->setProperty(pd.name(), v); + } + + moduleInstance->setScope(moduleScope); + + QHash 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 == depinst); + } + QBS_ASSERT(depinst != m.item, continue); + instantiateModule(productContext, isProduct ? exportingProduct : nullptr, moduleInstance, + depinst, m.item, m.name, m.isProduct); + m.item = depinst; + moduleInstance->addModule(m); + } + + // override module properties given on the command line + const QVariantMap userModuleProperties + = m_parameters.overriddenValuesTree().value(fullName).toMap(); + for (QVariantMap::const_iterator vmit = userModuleProperties.begin(); + vmit != userModuleProperties.end(); ++vmit) { + if (Q_UNLIKELY(!moduleInstance->hasProperty(vmit.key()))) { + const ErrorInfo error(Tr::tr("Unknown property: %1.%2") + .arg(moduleName.toString(), vmit.key())); + handlePropertyError(error, m_parameters, m_logger); + continue; + } + const PropertyDeclaration decl = moduleInstance->propertyDeclaration(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 *prototypeInstanceMap) const +{ + foreach (Item *childPrototype, prototype->children()) { + Item *childInstance = Item::create(m_pool, childPrototype->type()); + 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(ProductContext *productContext, Item *item) +{ + AccumulatingTimer probesTimer(m_parameters.logElapsedTime() ? &m_elapsedTimeProbes : nullptr); + EvalContextSwitcher evalContextSwitcher(m_evaluator->engine(), EvalContext::ProbeExecution); + foreach (Item *child, item->children()) + if (child->type() == ItemType::Probe) + resolveProbe(productContext, item, child); +} + +void ModuleLoader::resolveProbe(ProductContext *productContext, Item *parent, Item *probe) +{ + m_logger.qbsTrace() << "Resolving Probe at " << probe->location().toString(); + const JSSourceValueConstPtr configureScript = probe->sourceProperty(QLatin1String("configure")); + if (Q_UNLIKELY(!configureScript)) + throw ErrorInfo(Tr::tr("Probe.configure must be set."), probe->location()); + typedef QPair ProbeProperty; + QList probeBindings; + QVariantMap initialProperties; + for (Item *obj = probe; obj; obj = obj->prototype()) { + foreach (const QString &name, obj->properties().keys()) { + if (name == QLatin1String("configure")) + continue; + const QScriptValue value = m_evaluator->value(probe, name); + probeBindings += ProbeProperty(name, value); + if (name != QLatin1String("condition")) + initialProperties.insert(name, value.toVariant()); + } + } + QScriptValue scope = m_engine->newObject(); + m_engine->currentContext()->pushScope(m_evaluator->scriptValue(parent)); + m_engine->currentContext()->pushScope(m_evaluator->fileScope(configureScript->file())); + m_engine->currentContext()->pushScope(m_evaluator->importScope(configureScript->file())); + m_engine->currentContext()->pushScope(scope); + foreach (const ProbeProperty &b, probeBindings) + scope.setProperty(b.first, b.second); + const bool condition = m_evaluator->boolValue(probe, QLatin1String("condition")); + const QString &uniqueProductName + = ResolvedProduct::uniqueName(productContext->name, productContext->profileName); + ProbeConstPtr resolvedProbe = findOldProbe(uniqueProductName, condition, initialProperties, + configureScript->sourceCode().toString()); + if (!resolvedProbe) + resolvedProbe = findCurrentProbe(probe->location(), condition, initialProperties); + ErrorInfo evalError; + if (!condition) { + m_logger.qbsDebug() << "Probe disabled; skipping"; + } else if (!resolvedProbe) { + QScriptValue sv = m_engine->evaluate(configureScript->sourceCodeForEvaluation()); + if (Q_UNLIKELY(m_engine->hasErrorOrException(sv))) + evalError = ErrorInfo(m_engine->lastErrorString(sv), configureScript->location()); + } + QVariantMap properties; + foreach (const ProbeProperty &b, probeBindings) { + const QVariant newValue = resolvedProbe + ? resolvedProbe->properties().value(b.first) : scope.property(b.first).toVariant(); + if (newValue != b.second.toVariant()) + probe->setProperty(b.first, VariantValue::create(newValue)); + if (!resolvedProbe) + properties.insert(b.first, newValue); + } + if (!resolvedProbe) { + resolvedProbe = Probe::create(probe->location(), condition, + configureScript->sourceCode().toString(), properties, initialProperties); + m_currentProbes[probe->location()] << resolvedProbe; + } + productContext->info.probes << resolvedProbe; + m_engine->currentContext()->popScope(); + m_engine->currentContext()->popScope(); + m_engine->currentContext()->popScope(); + m_engine->currentContext()->popScope(); + if (evalError.hasError()) + throw evalError; +} + +void ModuleLoader::checkCancelation() const +{ + if (m_progressObserver && m_progressObserver->canceled()) { + throw ErrorInfo(Tr::tr("Project resolving canceled for configuration %1.") + .arg(TopLevelProject::deriveId(m_parameters.finalBuildConfigurationTree()))); + } +} + +bool ModuleLoader::checkItemCondition(Item *item) +{ + if (m_evaluator->boolValue(item, QLatin1String("condition"), true)) + return true; + m_disabledItems += item; + return false; +} + +QStringList ModuleLoader::readExtraSearchPaths(Item *item, bool *wasSet) +{ + QStringList result; + const QString propertyName = QLatin1String("qbsSearchPaths"); + const QStringList paths = m_evaluator->stringListValue(item, propertyName, wasSet); + const JSSourceValueConstPtr prop = item->sourceProperty(propertyName); + + // Value can come from within a project file or as an overridden value from the user + // (e.g command line). + const QString basePath = FileInfo::path(prop ? prop->file()->filePath() + : m_parameters.projectFilePath()); + foreach (const QString &path, paths) + result += FileInfo::resolvePath(basePath, path); + return result; +} + +void ModuleLoader::copyProperties(const Item *sourceProject, Item *targetProject) +{ + if (!sourceProject) + return; + const QList &builtinProjectProperties = BuiltinDeclarations::instance() + .declarationsForType(ItemType::Project).properties(); + QSet 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 there are exceptions. + if (it.key() == QLatin1String("qbsSearchPaths") || it.key() == QLatin1String("profile") + || it.key() == QLatin1String("buildDirectory") + || it.key() == QLatin1String("sourceDirectory") + || it.key() == QLatin1String("minimumQbsVersion")) { + const JSSourceValueConstPtr &v + = targetProject->property(it.key()).dynamicCast(); + QBS_ASSERT(v, continue); + if (v->sourceCode() == QLatin1String("undefined")) + sourceProject->copyProperty(it.key(), 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()); + sourceProject->copyProperty(it.key(), targetProject); + } +} + +Item *ModuleLoader::wrapInProjectIfNecessary(Item *item) +{ + if (item->type() == ItemType::Project) + return item; + Item *prj = Item::create(item->pool(), ItemType::Project); + Item::addChild(prj, item); + prj->setTypeName(QLatin1String("Project")); + prj->setFile(item->file()); + prj->setLocation(item->location()); + prj->setupForBuiltinType(m_logger); + return prj; +} + +QString ModuleLoader::findExistingModulePath(const QString &searchPath, + const QualifiedId &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::setScopeForDescendants(Item *item, Item *scope) +{ + foreach (Item *child, item->children()) { + child->setScope(scope); + setScopeForDescendants(child, scope); + } +} + +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()) { + ErrorInfo error(Tr::tr("Unknown property: %1.%2").arg(buildConfigKey, it.key())); + handlePropertyError(error, m_parameters, m_logger); + continue; + } + item->setProperty(it.key(), + VariantValue::create(convertToPropertyType(it.value(), decl.type(), + QStringList(buildConfigKey), it.key()))); + } +} + +static void collectAllModules(Item *item, QVector *modules) +{ + foreach (const Item::Module &m, item->modules()) { + auto it = std::find_if(modules->begin(), modules->end(), + [m] (const Item::Module &m2) { return m.name == m2.name; }); + if (it != modules->end()) { + // If a module is required somewhere, it is required in the top-level item. + if (m.required) + it->required = true; + it->versionRange.narrowDown(m.versionRange); + continue; + } + modules->append(m); + collectAllModules(m.item, modules); + } +} + +static QVector allModules(Item *item) +{ + QVector lst; + collectAllModules(item, &lst); + return lst; +} + +void ModuleLoader::addTransitiveDependencies(ProductContext *ctx) +{ + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[MODLDR] addTransitiveDependencies"; + QVector transitiveDeps = allModules(ctx->item); + std::sort(transitiveDeps.begin(), transitiveDeps.end()); + foreach (const Item::Module &m, ctx->item->modules()) { + if (m.isProduct) { + ctx->info.usedProducts.append( + ctx->project->topLevelProject->productModules.value( + m.name.toString()).productDependencies); + } + + auto it = std::lower_bound(transitiveDeps.begin(), transitiveDeps.end(), m); + QBS_CHECK(it != transitiveDeps.end() && it->name == m.name); + transitiveDeps.erase(it); + } + foreach (const Item::Module &module, transitiveDeps) { + if (module.isProduct) { + ctx->item->addModule(module); + ctx->info.usedProducts.append( + ctx->project->topLevelProject->productModules.value( + module.name.toString()).productDependencies); + } else { + Item::Module dep; + dep.item = loadModule(ctx, ctx->item, ctx->item->location(), QString(), module.name, + module.required, &dep.isProduct); + if (!dep.item) { + throw ErrorInfo(Tr::tr("Module '%1' not found when setting up transitive " + "dependencies for product '%2'.").arg(module.name.toString(), + ctx->name), + ctx->item->location()); + } + dep.name = module.name; + dep.required = module.required; + dep.versionRange = module.versionRange; + ctx->item->addModule(dep); + } + } +} + +Item *ModuleLoader::createNonPresentModule(const QString &name, const QString &reason, Item *module) +{ + if (m_logger.traceEnabled()) { + m_logger.qbsTrace() << "Non-required module '" << name << "' not loaded (" << reason << ")." + << "Creating dummy module for presence check."; + } + if (!module) { + module = Item::create(m_pool, ItemType::ModuleInstance); + module->setFile(FileContext::create()); + } + module->setProperty(QLatin1String("present"), VariantValue::create(false)); + return module; +} + +void ModuleLoader::handleProductError(const ErrorInfo &error, + ModuleLoader::ProductContext *productContext) +{ + if (m_parameters.productErrorMode() == ErrorHandlingMode::Strict) + throw error; + m_logger.printWarning(error); + productContext->info.hasError = true; + productContext->project->result->productInfos.insert(productContext->item, + productContext->info); + m_disabledItems << productContext->item; +} + +void ModuleLoader::copyGroupsFromModuleToProduct(const ProductContext &productContext, + const Item *modulePrototype) +{ + for (int i = 0; i < modulePrototype->children().count(); ++i) { + Item * const child = modulePrototype->children().at(i); + if (child->type() == ItemType::Group) { + Item * const clonedGroup = child->clone(); + clonedGroup->setScope(productContext.scope); + setScopeForDescendants(clonedGroup, productContext.scope); + Item::addChild(productContext.item, clonedGroup); + } + } +} + +void ModuleLoader::copyGroupsFromModulesToProduct(const ProductContext &productContext) +{ + foreach (const Item::Module &module, productContext.item->modules()) { + Item *prototype = module.item; + bool modulePassedValidation; + while ((modulePassedValidation = prototype->isPresentModule() + && !prototype->delayedError().hasError()) + && prototype->prototype()) { + prototype = prototype->prototype(); + } + if (modulePassedValidation) + copyGroupsFromModuleToProduct(productContext, prototype); + } +} + +QString ModuleLoaderResult::ProductInfo::Dependency::uniqueName() const +{ + return ResolvedProduct::uniqueName(name, profile); +} + +} // 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 00000000..288693b8 --- /dev/null +++ b/src/lib/corelib/language/moduleloader.h @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_MODULELOADER_H +#define QBS_MODULELOADER_H + +#include "filetags.h" +#include "forward_decls.h" +#include "item.h" +#include "itempool.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QScriptContext; +class QScriptEngine; +QT_END_NAMESPACE + +namespace qbs { + +class CodeLocation; + +namespace Internal { + +class Evaluator; +class Item; +class ItemReader; +class ProgressObserver; +class QualifiedId; +class ScriptEngine; + +struct ModuleLoaderResult +{ + ModuleLoaderResult() + : itemPool(new ItemPool), root(0) + {} + + struct ProductInfo + { + struct Dependency + { + FileTags productTypes; + QString name; + QString profile; // "*" <=> Match all profiles. + bool limitToSubProject = false; + bool isRequired = true; + + QString uniqueName() const; + }; + + QList probes; + QList usedProducts; + bool hasError = false; + }; + + QSharedPointer itemPool; + Item *root; + QHash productInfos; + QSet qbsFiles; + QVariantMap profileConfigs; +}; + +/* + * Loader stage II. Responsible for + * - loading modules and module dependencies, + * - project references, + * - Probe items. + */ +class ModuleLoader +{ +public: + ModuleLoader(ScriptEngine *engine, Logger &logger); + ~ModuleLoader(); + + void setProgressObserver(ProgressObserver *progressObserver); + void setSearchPaths(const QStringList &searchPaths); + void setOldProbes(const QHash> &oldProbes); + Evaluator *evaluator() const { return m_evaluator; } + + ModuleLoaderResult load(const SetupProjectParameters ¶meters); + +private: + class ProductSortByDependencies; + struct ItemCacheValue { + explicit ItemCacheValue(Item *module = 0, bool enabled = false) + : module(module), enabled(enabled) {} + Item *module; + bool enabled; + }; + + typedef QMap, ItemCacheValue> ModuleItemCache; + + class ContextBase + { + public: + ContextBase() + : item(0), scope(0) + {} + + Item *item; + Item *scope; + }; + + class ProjectContext; + + class ProductContext : public ContextBase + { + public: + ProjectContext *project; + ModuleLoaderResult::ProductInfo info; + QString name; + QString profileName; + QVariantMap moduleProperties; + }; + + class TopLevelProjectContext; + + class ProjectContext : public ContextBase + { + public: + TopLevelProjectContext *topLevelProject; + ModuleLoaderResult *result; + QVector products; + QStack searchPathsStack; + }; + + struct ProductModuleInfo + { + ProductModuleInfo() : exportItem() {} + + Item *exportItem; + QList productDependencies; + }; + + class TopLevelProjectContext + { + Q_DISABLE_COPY(TopLevelProjectContext) + public: + TopLevelProjectContext() {} + ~TopLevelProjectContext() { qDeleteAll(projects); } + + QVector projects; + QHash productModules; + QString buildDirectory; + }; + + class DependsContext + { + public: + ProductContext *product; + QList *productDependencies; + }; + + typedef QList ProductDependencyResults; + + void handleTopLevelProject(ModuleLoaderResult *loadResult, Item *projectItem, + const QString &buildDirectory, const QSet &referencedFilePaths); + void handleProject(ModuleLoaderResult *loadResult, + TopLevelProjectContext *topLevelProjectContext, Item *projectItem, + const QSet &referencedFilePaths); + QList multiplexProductItem(ProductContext *dummyContext, Item *productItem); + void prepareProduct(ProjectContext *projectContext, Item *productItem); + void setupProductDependencies(ProductContext *productContext); + void handleProduct(ProductContext *productContext); + void handleModuleSetupError(ProductContext *productContext, const Item::Module &module, + const ErrorInfo &error); + void initProductProperties(const ProductContext &product); + void handleSubProject(ProjectContext *projectContext, Item *projectItem, + const QSet &referencedFilePaths); + QList loadReferencedFile(const QString &relativePath, + const CodeLocation &referencingLocation, + const QSet &referencedFilePaths, + ProductContext &dummyContext); + void handleAllPropertyOptionsItems(Item *item); + void handlePropertyOptions(Item *optionsItem); + + using ModuleDependencies = QHash; + void setupReverseModuleDependencies(const Item::Module &module, ModuleDependencies &deps, + QualifiedIdSet &seenModules); + ModuleDependencies setupReverseModuleDependencies(const Item *product); + void handleGroup(Item *groupItem, const ModuleDependencies &reverseDepencencies); + void propagateModulesFromParent(Item *groupItem, const ModuleDependencies &reverseDepencencies); + void adjustDefiningItemsInGroupModuleInstances(const Item::Module &module, + const Item::Modules &dependentModules); + + void mergeExportItems(const ProductContext &productContext); + void resolveDependencies(DependsContext *dependsContext, Item *item); + class ItemModuleList; + void resolveDependsItem(DependsContext *dependsContext, Item *parentItem, Item *dependsItem, + ItemModuleList *moduleResults, ProductDependencyResults *productResults); + Item *moduleInstanceItem(Item *containerItem, const QualifiedId &moduleName); + Item *loadProductModule(ProductContext *productContext, const QString &moduleName); + Item *loadModule(ProductContext *productContext, Item *item, + const CodeLocation &dependsItemLocation, const QString &moduleId, + const QualifiedId &moduleName, bool isRequired, bool *isModuleDependency); + Item *searchAndLoadModuleFile(ProductContext *productContext, + const CodeLocation &dependsItemLocation, const QualifiedId &moduleName, + const QStringList &extraSearchPaths, bool isRequired, bool *cacheHit); + Item *loadModuleFile(ProductContext *productContext, const QString &fullModuleName, + bool isBaseModule, const QString &filePath, bool *cacheHit, bool *triedToLoad); + Item::Module loadBaseModule(ProductContext *productContext, Item *item); + void setupBaseModulePrototype(Item *prototype); + void instantiateModule(ProductContext *productContext, Item *exportingProductItem, + Item *instanceScope, Item *moduleInstance, Item *modulePrototype, + const QualifiedId &moduleName, bool isProduct); + void createChildInstances(ProductContext *productContext, Item *instance, + Item *prototype, QHash *prototypeInstanceMap) const; + void resolveProbes(ProductContext *productContext, Item *item); + void resolveProbe(ProductContext *productContext, Item *parent, Item *probe); + void checkCancelation() const; + bool checkItemCondition(Item *item); + QStringList readExtraSearchPaths(Item *item, bool *wasSet = 0); + void copyProperties(const Item *sourceProject, Item *targetProject); + Item *wrapInProjectIfNecessary(Item *item); + static QString findExistingModulePath(const QString &searchPath, + const QualifiedId &moduleName); + static void setScopeForDescendants(Item *item, Item *scope); + void overrideItemProperties(Item *item, const QString &buildConfigKey, + const QVariantMap &buildConfig); + void addTransitiveDependencies(ProductContext *ctx); + Item *createNonPresentModule(const QString &name, const QString &reason, Item *module); + void copyGroupsFromModuleToProduct(const ProductContext &productContext, + const Item *modulePrototype); + void copyGroupsFromModulesToProduct(const ProductContext &productContext); + bool checkExportItemCondition(Item *exportItem, const ProductContext &productContext); + ProbeConstPtr findOldProbe(const QString &product, bool condition, + const QVariantMap &initialProperties, + const QString &sourceCode) const; + ProbeConstPtr findCurrentProbe(const CodeLocation &location, bool condition, + const QVariantMap &initialProperties) const; + void printProfilingInfo(); + + void handleProductError(const ErrorInfo &error, ProductContext *productContext); + + bool isSomeModulePropertySet(const Item *item); + + ScriptEngine *m_engine; + ItemPool *m_pool; + Logger &m_logger; + ProgressObserver *m_progressObserver; + ItemReader *m_reader; + Evaluator *m_evaluator; + QStringList m_moduleSearchPaths; + QMap m_moduleDirListCache; + QHash m_productModuleCache; + ModuleItemCache m_modulePrototypeItemCache; + QHash > m_validItemPropertyNamesPerItem; + QSet m_disabledItems; + QStack m_requiredChain; + + using DependsChainEntry = QPair; + class DependsChainManager; + QStack m_dependsChain; + + QHash> m_oldProbes; + QHash> m_currentProbes; + SetupProjectParameters m_parameters; + Version m_qbsVersion; + Item *m_tempScopeItem = nullptr; + qint64 m_elapsedTimeProbes; +}; + +} // 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/modulemerger.cpp b/src/lib/corelib/language/modulemerger.cpp new file mode 100644 index 00000000..d0dc3e35 --- /dev/null +++ b/src/lib/corelib/language/modulemerger.cpp @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "modulemerger.h" + +#include "value.h" + +#include +#include + +namespace qbs { +namespace Internal { + +ModuleMerger::ModuleMerger(Logger &logger, Item *root, Item::Module &moduleToMerge) + : m_logger(logger) + , m_rootItem(root) + , m_mergedModule(moduleToMerge) + , m_required(moduleToMerge.required) + , m_versionRange(moduleToMerge.versionRange) +{ + QBS_CHECK(moduleToMerge.item->type() == ItemType::ModuleInstance); +} + +void ModuleMerger::replaceItemInValues(QualifiedId moduleName, Item *containerItem, Item *toReplace) +{ + QBS_CHECK(!moduleName.isEmpty()); + QBS_CHECK(containerItem != m_mergedModule.item); + const QString moduleNamePrefix = moduleName.takeFirst(); + Item::PropertyMap properties = containerItem->properties(); + for (auto it = properties.begin(); it != properties.end(); ++it) { + if (it.key() != moduleNamePrefix) + continue; + Value * const val = it.value().data(); + QBS_CHECK(val); + QBS_CHECK(val->type() == Value::ItemValueType); + ItemValue * const itemVal = static_cast(val); + if (moduleName.isEmpty()) { + QBS_CHECK(itemVal->item() == toReplace); + itemVal->setItem(m_mergedModule.item); + } else { + replaceItemInValues(moduleName, itemVal->item(), toReplace); + } + } +} + +void ModuleMerger::replaceItemInScopes(Item *toReplace) +{ + // In insertProperties(), we potentially call setDefiningItem() with the "wrong" + // (to-be-replaced) module instance as an argument. If such module instances + // are dependencies of other modules, they have the depending module's instance + // as their "instance scope", which is the scope of their scope. This function takes + // care that the "wrong" definingItem of values in sub-modules still has the "right" + // instance scope, namely our merged module instead of some other instance. + foreach (const Item::Module &module, toReplace->modules()) { + foreach (const ValuePtr &property, module.item->properties()) { + ValuePtr v = property; + do { + if (v->definingItem() && v->definingItem()->scope() + && v->definingItem()->scope()->scope() == toReplace) { + v->definingItem()->scope()->setScope(m_mergedModule.item); + } + v = v->next(); + } while (v); + } + } +} + +void ModuleMerger::start() +{ + Item::Module m; + m.item = m_rootItem; + const Item::PropertyMap props = dfs(m, Item::PropertyMap()); + if (m_required) + m_mergedModule.required = true; + m_mergedModule.versionRange.narrowDown(m_versionRange); + Item::PropertyMap mergedProps = m_mergedModule.item->properties(); + + Item *moduleProto = m_mergedModule.item->prototype(); + while (moduleProto->prototype()) + moduleProto = moduleProto->prototype(); + + for (auto it = props.constBegin(); it != props.constEnd(); ++it) { + appendPrototypeValueToNextChain(moduleProto, it.key(), it.value()); + mergedProps[it.key()] = it.value(); + } + m_mergedModule.item->setProperties(mergedProps); + + foreach (Item *moduleInstanceContainer, m_moduleInstanceContainers) { + Item::Modules modules; + foreach (const Item::Module &dep, moduleInstanceContainer->modules()) { + const bool isTheModule = dep.name == m_mergedModule.name; + Item::Module m = dep; + if (isTheModule && m.item != m_mergedModule.item) { + QBS_CHECK(m.item->type() == ItemType::ModuleInstance); + replaceItemInValues(m.name, moduleInstanceContainer, m.item); + replaceItemInScopes(m.item); + m.item = m_mergedModule.item; + if (m_required) + m.required = true; + m.versionRange.narrowDown(m_versionRange); + } + modules << m; + } + moduleInstanceContainer->setModules(modules); + } +} + +Item::PropertyMap ModuleMerger::dfs(const Item::Module &m, Item::PropertyMap props) +{ + Item *moduleInstance = 0; + int numberOfOutprops = m.item->modules().count(); + foreach (const Item::Module &dep, m.item->modules()) { + if (dep.name == m_mergedModule.name) { + --numberOfOutprops; + moduleInstance = dep.item; + insertProperties(&props, moduleInstance, ScalarProperties); + m_moduleInstanceContainers << m.item; + if (dep.required) + m_required = true; + m_versionRange.narrowDown(dep.versionRange); + break; + } + } + + QVector outprops; + outprops.reserve(numberOfOutprops); + foreach (const Item::Module &dep, m.item->modules()) { + if (dep.item != moduleInstance) + outprops << dfs(dep, props); + } + + if (!outprops.isEmpty()) { + props = outprops.first(); + for (int i = 1; i < outprops.count(); ++i) + mergeOutProps(&props, outprops.at(i)); + } + + if (moduleInstance) + insertProperties(&props, moduleInstance, ListProperties); + + return props; +} + +void ModuleMerger::mergeOutProps(Item::PropertyMap *dst, const Item::PropertyMap &src) +{ + for (auto it = src.constBegin(); it != src.constEnd(); ++it) { + ValuePtr &v = (*dst)[it.key()]; + if (!v) { + v = it.value(); + QBS_ASSERT(it.value(), continue); + continue; + } + // possible conflict + JSSourceValuePtr dstVal = v.dynamicCast(); + if (!dstVal) + continue; + JSSourceValuePtr srcVal = it.value().dynamicCast(); + if (!srcVal) + continue; + + const PropertyDeclaration pd = m_decls.value(srcVal); + QBS_CHECK(pd.isValid()); + + if (pd.isScalar()) { + if (dstVal->sourceCode() != srcVal->sourceCode()) { + m_logger.qbsWarning() << Tr::tr("Conflicting scalar values at %1 and %2.").arg( + dstVal->location().toString(), + srcVal->location().toString()); + // TODO: yield error with a hint how to solve the conflict. + } + v = it.value(); + } else { + lastInNextChain(dstVal)->setNext(srcVal); + } + } +} + +void ModuleMerger::insertProperties(Item::PropertyMap *dst, Item *srcItem, PropertiesType type) +{ + QSet &seenInstances + = type == ScalarProperties ? m_seenInstancesTopDown : m_seenInstancesBottomUp; + Item *origSrcItem = srcItem; + do { + if (!seenInstances.contains(srcItem)) { + seenInstances.insert(srcItem); + for (Item::PropertyMap::const_iterator it = srcItem->properties().constBegin(); + it != srcItem->properties().constEnd(); ++it) { + const ValuePtr &srcVal = it.value(); + if (srcVal->type() != Value::JSSourceValueType) + continue; + const PropertyDeclaration srcDecl = srcItem->propertyDeclaration(it.key()); + if (!srcDecl.isValid() || srcDecl.isScalar() != (type == ScalarProperties)) + continue; + ValuePtr &v = (*dst)[it.key()]; + if (v && type == ScalarProperties) + continue; + ValuePtr clonedVal = srcVal->clone(); + m_decls[clonedVal] = srcDecl; + clonedVal->setDefiningItem(origSrcItem); + if (v) { + QBS_CHECK(!clonedVal->next()); + clonedVal->setNext(v); + } + v = clonedVal; + } + } + srcItem = srcItem->prototype(); + } while (srcItem && srcItem->type() == ItemType::ModuleInstance); +} + +void ModuleMerger::appendPrototypeValueToNextChain(Item *moduleProto, const QString &propertyName, + const ValuePtr &sv) +{ + const PropertyDeclaration pd = m_mergedModule.item->propertyDeclaration(propertyName); + if (pd.isScalar()) + return; + if (!m_clonedModulePrototype) { + m_clonedModulePrototype = moduleProto->clone(); + m_clonedModulePrototype->setScope(m_mergedModule.item); + } + const ValuePtr clonedValue = m_clonedModulePrototype->property(propertyName); + QBS_CHECK(clonedValue); + clonedValue->setDefiningItem(m_clonedModulePrototype); + lastInNextChain(sv)->setNext(clonedValue); +} + +ValuePtr ModuleMerger::lastInNextChain(const ValuePtr &v) +{ + ValuePtr n = v; + while (n->next()) + n = n->next(); + return n; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/modulemerger.h b/src/lib/corelib/language/modulemerger.h new file mode 100644 index 00000000..479ad300 --- /dev/null +++ b/src/lib/corelib/language/modulemerger.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_MODULEMERGER_H +#define QBS_MODULEMERGER_H + +#include "item.h" +#include "qualifiedid.h" + +#include +#include + +#include +#include + +namespace qbs { +namespace Internal { + +class ModuleMerger { +public: + ModuleMerger(Logger &logger, Item *root, Item::Module &moduleToMerge); + void start(); + +private: + Item::PropertyMap dfs(const Item::Module &m, Item::PropertyMap props); + void mergeOutProps(Item::PropertyMap *dst, const Item::PropertyMap &src); + void appendPrototypeValueToNextChain(Item *moduleProto, const QString &propertyName, + const ValuePtr &sv); + static ValuePtr lastInNextChain(const ValuePtr &v); + + enum PropertiesType { ScalarProperties, ListProperties }; + void insertProperties(Item::PropertyMap *dst, Item *srcItem, PropertiesType type); + void replaceItemInValues(QualifiedId moduleName, Item *containerItem, Item *toReplace); + void replaceItemInScopes(Item *toReplace); + + Logger &m_logger; + Item * const m_rootItem; + Item::Module &m_mergedModule; + Item *m_clonedModulePrototype = nullptr; + QHash m_decls; + QSet m_seenInstancesTopDown; + QSet m_seenInstancesBottomUp; + QSet m_moduleInstanceContainers; + bool m_required; + VersionRange m_versionRange; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_MODULEMERGER_H diff --git a/src/lib/corelib/language/preparescriptobserver.cpp b/src/lib/corelib/language/preparescriptobserver.cpp new file mode 100644 index 00000000..748c343f --- /dev/null +++ b/src/lib/corelib/language/preparescriptobserver.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "preparescriptobserver.h" + +#include "property.h" +#include "scriptengine.h" + +#include + +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 00000000..b10c2203 --- /dev/null +++ b/src/lib/corelib/language/preparescriptobserver.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#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 00000000..9d9d56a8 --- /dev/null +++ b/src/lib/corelib/language/projectresolver.cpp @@ -0,0 +1,1297 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "projectresolver.h" + +#include "artifactproperties.h" +#include "evaluator.h" +#include "filecontext.h" +#include "item.h" +#include "language.h" +#include "propertymapinternal.h" +#include "resolvedfilecontext.h" +#include "scriptengine.h" +#include "value.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace qbs { +namespace Internal { + +extern bool debugProperties; + +static const FileTag unknownFileTag() +{ + static const FileTag tag("unknown-file-tag"); + return tag; +} + +struct ProjectResolver::ProjectContext +{ + ResolvedProjectPtr project; + QList fileTaggers; + QList rules; + ResolvedModulePtr dummyModule; +}; + +struct ProjectResolver::ProductContext +{ + ResolvedProductPtr product; + QString buildDirectory; + FileTags additionalFileTags; + Item *item; + typedef QPair ArtifactPropertiesInfo; + QHash artifactPropertiesPerFilter; + QHash sourceArtifactLocations; + GroupConstPtr currentGroup; +}; + +struct ProjectResolver::ModuleContext +{ + ResolvedModulePtr module; +}; + + +ProjectResolver::ProjectResolver(Evaluator *evaluator, const ModuleLoaderResult &loadResult, + const SetupProjectParameters &setupParameters, Logger &logger) + : m_evaluator(evaluator) + , m_logger(logger) + , m_engine(m_evaluator->engine()) + , m_progressObserver(0) + , m_setupParams(setupParameters) + , m_loadResult(loadResult) +{ + QBS_CHECK(FileInfo::isAbsolute(m_setupParams.buildRoot())); +} + +ProjectResolver::~ProjectResolver() +{ +} + +void ProjectResolver::setProgressObserver(ProgressObserver *observer) +{ + m_progressObserver = observer; +} + +static void checkForDuplicateProductNames(const TopLevelProjectConstPtr &project) +{ + const QList allProducts = project->allProducts(); + for (int i = 0; i < allProducts.count(); ++i) { + const ResolvedProductConstPtr product1 = allProducts.at(i); + const QString productName = product1->uniqueName(); + for (int j = i + 1; j < allProducts.count(); ++j) { + const ResolvedProductConstPtr product2 = allProducts.at(j); + if (product2->uniqueName() == productName) { + ErrorInfo error; + error.append(Tr::tr("Duplicate product name '%1'.").arg(product1->name)); + 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() +{ + TimedActivityLogger projectResolverTimer(m_logger, Tr::tr("ProjectResolver"), + m_setupParams.logElapsedTime()); + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[PR] resolving " << m_loadResult.root->file()->filePath(); + + m_productContext = 0; + m_moduleContext = 0; + m_elapsedTimeModPropEval = m_elapsedTimeAllPropEval = m_elapsedTimeGroups = 0; + const TopLevelProjectPtr tlp = resolveTopLevelProject(); + printProfilingInfo(); + return tlp; +} + +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, bool *propertyWasSet) const +{ + QString result; + if (value && value->type() == Value::JSSourceValueType) { + const JSSourceValueConstPtr sourceValue = value.staticCast(); + result = sourceValue->sourceCodeForEvaluation(); + if (propertyWasSet) + *propertyWasSet = (result != QLatin1String("undefined")); + } else { + if (propertyWasSet) + *propertyWasSet = false; + } + return result; +} + +QString ProjectResolver::verbatimValue(Item *item, const QString &name, bool *propertyWasSet) const +{ + return verbatimValue(item->property(name), propertyWasSet); +} + +void ProjectResolver::ignoreItem(Item *item, ProjectContext *projectContext) +{ + Q_UNUSED(item); + Q_UNUSED(projectContext); +} + +static void makeSubProjectNamesUniqe(const ResolvedProjectPtr &parentProject) +{ + QSet subProjectNames; + QSet projectsInNeedOfNameChange; + foreach (const ResolvedProjectPtr &p, parentProject->subProjects) { + if (subProjectNames.contains(p->name)) + projectsInNeedOfNameChange << p; + else + subProjectNames << p->name; + makeSubProjectNamesUniqe(p); + } + while (!projectsInNeedOfNameChange.isEmpty()) { + QSet::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; + } + } + } +} + +TopLevelProjectPtr ProjectResolver::resolveTopLevelProject() +{ + if (m_progressObserver) + m_progressObserver->setMaximum(m_loadResult.productInfos.count()); + const TopLevelProjectPtr project = TopLevelProject::create(); + project->buildDirectory = TopLevelProject::deriveBuildDirectory(m_setupParams.buildRoot(), + TopLevelProject::deriveId(m_setupParams.finalBuildConfigurationTree())); + project->buildSystemFiles = m_loadResult.qbsFiles; + project->profileConfigs = m_loadResult.profileConfigs; + ProjectContext projectContext; + projectContext.project = project; + resolveProject(m_loadResult.root, &projectContext); + project->setBuildConfiguration(m_setupParams.finalBuildConfigurationTree()); + project->usedEnvironment = m_engine->usedEnvironment(); + project->canonicalFilePathResults = m_engine->canonicalFilePathResults(); + project->fileExistsResults = m_engine->fileExistsResults(); + project->directoryEntriesResults = m_engine->directoryEntriesResults(); + project->fileLastModifiedResults = m_engine->fileLastModifiedResults(); + project->environment = m_engine->environment(); + project->buildSystemFiles.unite(m_engine->imports()); + makeSubProjectNamesUniqe(project); + resolveProductDependencies(projectContext); + checkForDuplicateProductNames(project); + + foreach (const ResolvedProductPtr &product, project->allProducts()) { + if (!product->enabled) + continue; + + applyFileTaggers(product); + matchArtifactProperties(product, product->allEnabledFiles()); + + // Let a positive value of qbs.install imply the file tag "installable". + foreach (const SourceArtifactPtr &artifact, product->allFiles()) { + if (artifact->properties->qbsPropertyValue(QLatin1String("install")).toBool()) + artifact->fileTags += "installable"; + } + } + project->warningsEncountered = m_logger.warnings(); + return project; +} + +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().filePath()); // FIXME: Must also be changed in item? + projectContext->project->enabled + = m_evaluator->boolValue(item, QLatin1String("condition")); + QVariantMap projectProperties; + if (!projectContext->project->enabled) { + projectProperties.insert(QLatin1String("profile"), + m_evaluator->stringValue(item, QLatin1String("profile"))); + projectContext->project->setProjectProperties(projectProperties); + return; + } + + projectContext->dummyModule = ResolvedModule::create(); + + for (Item::PropertyDeclarationMap::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->value(item, it.key()).toVariant()); + } + projectContext->project->setProjectProperties(projectProperties); + + static const ItemFuncMap mapping = { + { ItemType::Project, &ProjectResolver::resolveProject }, + { ItemType::SubProject, &ProjectResolver::resolveSubProject }, + { ItemType::Product, &ProjectResolver::resolveProduct }, + { ItemType::FileTagger, &ProjectResolver::resolveFileTagger }, + { ItemType::Rule, &ProjectResolver::resolveRule }, + { ItemType::PropertyOptions, &ProjectResolver::ignoreItem } + }; + + 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(ItemType::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(ItemType::PropertiesInSubProject); + if (propertiesItem) { + subProjectContext.project->name + = m_evaluator->stringValue(propertiesItem, QLatin1String("name")); + } +} + +void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext) +{ + checkCancelation(); + m_evaluator->clearPropertyDependencies(); + ProductContext productContext; + m_productContext = &productContext; + productContext.item = item; + ResolvedProductPtr product = ResolvedProduct::create(); + product->moduleProperties = PropertyMapInternal::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")); + product->location = item->location(); + + // product->buildDirectory() isn't valid yet, because the productProperties map is not ready. + productContext.buildDirectory = m_evaluator->stringValue(item, QLatin1String("buildDirectory")); + product->profile = m_evaluator->stringValue(item, QLatin1String("profile")); + QBS_CHECK(!product->profile.isEmpty()); + m_logger.qbsTrace() << "[PR] resolveProduct " << product->uniqueName(); + m_productsByName.insert(product->uniqueName(), product); + const ModuleLoaderResult::ProductInfo &pi = m_loadResult.productInfos.value(item); + if (pi.hasError) { + m_logger.printWarning(ErrorInfo(Tr::tr("Product '%1' had errors and was disabled.") + .arg(product->name), item->location())); + product->enabled = false; + return; + } + + product->enabled = m_evaluator->boolValue(item, QLatin1String("condition")); + product->fileTags = m_evaluator->fileTagsValue(item, QLatin1String("type")); + product->targetName = m_evaluator->stringValue(item, QLatin1String("targetName")); + product->sourceDirectory = m_evaluator->stringValue(item, QLatin1String("sourceDirectory")); + const QString destDirKey = QLatin1String("destinationDirectory"); + product->destinationDirectory = m_evaluator->stringValue(item, destDirKey); + + if (product->destinationDirectory.isEmpty()) { + product->destinationDirectory = productContext.buildDirectory; + } else { + product->destinationDirectory = FileInfo::resolvePath( + product->topLevelProject()->buildDirectory, + product->destinationDirectory); + } + product->probes = pi.probes; + product->productProperties = createProductConfig(); + product->productProperties.insert(destDirKey, product->destinationDirectory); + QVariantMap moduleProperties; + moduleProperties.insert(QLatin1String("modules"), + product->productProperties.take(QLatin1String("modules"))); + product->moduleProperties->setValue(moduleProperties); + ModuleProperties::init(m_evaluator->scriptValue(item), product); + + QList 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->setType(ItemType::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)); + fakeGroup->setupForBuiltinType(m_logger); + subItems.prepend(fakeGroup); + } + + static const ItemFuncMap mapping = { + { ItemType::Depends, &ProjectResolver::ignoreItem }, + { ItemType::Rule, &ProjectResolver::resolveRule }, + { ItemType::FileTagger, &ProjectResolver::resolveFileTagger }, + { ItemType::Group, &ProjectResolver::resolveGroup }, + { ItemType::Export, &ProjectResolver::ignoreItem }, + { ItemType::Probe, &ProjectResolver::ignoreItem }, + { ItemType::PropertyOptions, &ProjectResolver::ignoreItem } + }; + + foreach (Item *child, subItems) + callItemFunction(mapping, child, projectContext); + + resolveModules(item, projectContext); + product->fileTags += productContext.additionalFileTags; + + foreach (const FileTag &t, product->fileTags) + m_productsByType[t] << product; + + m_productContext = 0; + if (m_progressObserver) + m_progressObserver->incrementProgressValue(); +} + +void ProjectResolver::resolveModules(const Item *item, ProjectContext *projectContext) +{ + // Breadth first search needed here, because the product might set properties on the cpp module, + // whose children must be evaluated in that context then. + QQueue modules; + foreach (const Item::Module &m, item->modules()) + modules.enqueue(m); + QSet seen; + while (!modules.isEmpty()) { + const Item::Module m = modules.takeFirst(); + if (seen.contains(m.name)) + continue; + seen.insert(m.name); + resolveModule(m.name, m.item, m.isProduct, projectContext); + foreach (const Item::Module &childModule, m.item->modules()) + modules.enqueue(childModule); + } + std::sort(m_productContext->product->modules.begin(), m_productContext->product->modules.end(), + [](const ResolvedModuleConstPtr &m1, const ResolvedModuleConstPtr &m2) { + return m1->name < m2->name; + }); +} + +void ProjectResolver::resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct, + ProjectContext *projectContext) +{ + checkCancelation(); + if (!m_evaluator->boolValue(item, QLatin1String("present"))) + return; + + if (m_productContext->product->enabled && item->delayedError().hasError()) + throw item->delayedError(); + + ModuleContext * const oldModuleContext = m_moduleContext; + ModuleContext moduleContext; + moduleContext.module = ResolvedModule::create(); + m_moduleContext = &moduleContext; + + const ResolvedModulePtr &module = moduleContext.module; + module->name = moduleName.toString(); + module->setupBuildEnvironmentScript = scriptFunctionValue(item, + QLatin1String("setupBuildEnvironment")); + module->setupRunEnvironmentScript = scriptFunctionValue(item, + QLatin1String("setupRunEnvironment")); + + m_productContext->additionalFileTags += + m_evaluator->fileTagsValue(item, QLatin1String("additionalProductTypes")); + + foreach (const Item::Module &m, item->modules()) { + if (m_evaluator->boolValue(m.item, QLatin1String("present"))) + module->moduleDependencies += m.name.toString(); + } + + if (!isProduct) + m_productContext->product->modules += module; + + static const ItemFuncMap mapping { + { ItemType::Group, &ProjectResolver::ignoreItem }, + { ItemType::Rule, &ProjectResolver::resolveRule }, + { ItemType::FileTagger, &ProjectResolver::resolveFileTagger }, + { ItemType::Scanner, &ProjectResolver::resolveScanner }, + { ItemType::PropertyOptions, &ProjectResolver::ignoreItem }, + { ItemType::Depends, &ProjectResolver::ignoreItem }, + { ItemType::Probe, &ProjectResolver::ignoreItem } + }; + foreach (Item *child, item->children()) + callItemFunction(mapping, child, projectContext); + + m_moduleContext = oldModuleContext; +} + +SourceArtifactPtr ProjectResolver::createSourceArtifact(const ResolvedProductConstPtr &rproduct, + const QString &fileName, const GroupPtr &group, bool wildcard, + const CodeLocation &filesLocation, QHash *fileLocations, + ErrorInfo *errorInfo) +{ + const QString absFilePath + = QDir::cleanPath(FileInfo::resolvePath(rproduct->sourceDirectory, fileName)); + if (!wildcard && !FileInfo(absFilePath).exists()) { + if (errorInfo) + errorInfo->append(Tr::tr("File '%1' does not exist.").arg(absFilePath), filesLocation); + return SourceArtifactPtr(); + } + if (group->enabled && fileLocations) { + CodeLocation &loc = (*fileLocations)[absFilePath]; + if (loc.isValid()) { + if (errorInfo) { + errorInfo->append(Tr::tr("Duplicate source file '%1'.").arg(absFilePath)); + errorInfo->append(Tr::tr("First occurrence is here."), loc); + errorInfo->append(Tr::tr("Next occurrence is here."), filesLocation); + } + return SourceArtifactPtr(); + } + loc = filesLocation; + } + SourceArtifactPtr artifact = SourceArtifactInternal::create(); + artifact->absoluteFilePath = absFilePath; + artifact->fileTags = group->fileTags; + artifact->overrideFileTags = group->overrideTags; + artifact->properties = group->properties; + (wildcard ? group->wildcards->files : group->files) += artifact; + return artifact; +} + +static QualifiedIdSet propertiesToEvaluate(const QList &initialProps, + const PropertyDependencies &deps) +{ + QList remainingProps = initialProps; + QualifiedIdSet allProperties; + while (!remainingProps.isEmpty()) { + const QualifiedId prop = remainingProps.takeFirst(); + const QualifiedIdSet::InsertResult insertResult = allProperties.insert(prop); + if (!insertResult.second) + continue; + const QualifiedIdSet &directDeps = deps.value(prop); + for (auto depIt = directDeps.cbegin(); depIt != directDeps.cend(); ++depIt) + remainingProps << *depIt; + } + return allProperties; +} + +static void gatherAssignedProperties(ItemValue *iv, const QualifiedId &prefix, + QList &properties) +{ + const Item::PropertyMap &props = iv->item()->properties(); + for (auto it = props.cbegin(); it != props.cend(); ++it) { + switch (it.value()->type()) { + case Value::JSSourceValueType: + properties << (QualifiedId(prefix) << it.key()); + break; + case Value::ItemValueType: + gatherAssignedProperties(it.value().staticCast().data(), + QualifiedId(prefix) << it.key(), properties); + break; + default: + break; + } + } +} + +QVariantMap ProjectResolver::resolveAdditionalModuleProperties(const Item *group, + const QVariantMap ¤tValues) +{ + // Step 1: Gather the properties directly set in the group + QList propsSetInGroup; + for (auto it = group->properties().cbegin(); it != group->properties().cend(); ++it) { + if (it.value()->type() == Value::ItemValueType) { + gatherAssignedProperties(it.value().staticCast().data(), + QualifiedId(it.key()), propsSetInGroup); + } + } + if (propsSetInGroup.isEmpty()) + return QVariantMap(); + + // Step 2: Gather all properties that depend on these properties. + const QualifiedIdSet &propsToEval + = propertiesToEvaluate(propsSetInGroup, m_evaluator->propertyDependencies()); + + // Step 3: Evaluate all these properties and replace their values in the map + QVariantMap modulesMap = currentValues.value(QLatin1String("modules")).toMap(); + QHash propsPerModule; + for (auto it = propsToEval.cbegin(); it != propsToEval.cend(); ++it) { + const QualifiedId fullPropName = *it; + const QString moduleName + = QualifiedId(fullPropName.mid(0, fullPropName.count() - 1)).toString(); + propsPerModule[moduleName] << fullPropName.last(); + } + EvalCacheEnabler cachingEnabler(m_evaluator); + for (auto it = group->modules().cbegin(); it != group->modules().cend(); ++it) { + const QString &fullModName = it->name.toString(); + const QStringList propsForModule = propsPerModule.take(fullModName); + if (propsForModule.isEmpty()) + continue; + QVariantMap reusableValues = modulesMap.value(fullModName).toMap(); + for (auto propIt = propsForModule.cbegin(); propIt != propsForModule.cend(); ++propIt) + reusableValues.remove(*propIt); + modulesMap.insert(fullModName, + evaluateProperties(it->item, it->item, reusableValues, true)); + } + QVariantMap newValues = currentValues; + newValues.insert(QLatin1String("modules"), modulesMap); + return newValues; +} + +void ProjectResolver::resolveGroup(Item *item, ProjectContext *projectContext) +{ + Q_UNUSED(projectContext); + checkCancelation(); + PropertyMapPtr moduleProperties = m_productContext->currentGroup + ? m_productContext->currentGroup->properties + : m_productContext->product->moduleProperties; + const QVariantMap newModuleProperties + = resolveAdditionalModuleProperties(item, moduleProperties->value()); + if (!newModuleProperties.isEmpty()) { + moduleProperties = PropertyMapInternal::create(); + moduleProperties->setValue(newModuleProperties); + } + + AccumulatingTimer groupTimer(m_setupParams.logElapsedTime() + ? &m_elapsedTimeGroups : nullptr); + + bool isEnabled = m_evaluator->boolValue(item, QLatin1String("condition")); + if (m_productContext->currentGroup) + isEnabled = isEnabled && m_productContext->currentGroup->enabled; + 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()); + + ProductContext::ArtifactPropertiesInfo apinfo + = m_productContext->artifactPropertiesPerFilter.value(fileTagsFilter); + if (apinfo.first) { + if (apinfo.second.filePath() == item->location().filePath()) { + ErrorInfo error(Tr::tr("Conflicting fileTagsFilter in Group items.")); + error.append(Tr::tr("First item"), apinfo.second); + error.append(Tr::tr("Second item"), item->location()); + throw error; + } + + // Discard any Group with the same fileTagsFilter that was defined in a base file. + m_productContext->product->artifactProperties.removeAll(apinfo.first); + } + if (!isEnabled) + return; + ArtifactPropertiesPtr aprops = ArtifactProperties::create(); + aprops->setFileTagsFilter(FileTags::fromStringList(fileTagsFilter)); + aprops->setPropertyMapInternal(moduleProperties); + m_productContext->product->artifactProperties += aprops; + m_productContext->artifactPropertiesPerFilter.insert(fileTagsFilter, + ProductContext::ArtifactPropertiesInfo(aprops, item->location())); + return; + } + 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->properties = moduleProperties; + bool fileTagsSet; + group->fileTags = m_evaluator->fileTagsValue(item, QLatin1String("fileTags"), &fileTagsSet); + group->overrideTags = m_evaluator->boolValue(item, QLatin1String("overrideTags")); + if (group->overrideTags && fileTagsSet) { + if (group->fileTags.isEmpty() ) + group->fileTags.insert(unknownFileTag()); + } else if (m_productContext->currentGroup) { + group->fileTags.unite(m_productContext->currentGroup->fileTags); + } + + const CodeLocation filesLocation = item->property(QLatin1String("files"))->location(); + + // TODO: Remove in 1.8 + bool emittedRelPathWarning = false; + const auto relPathChecker = [this, group, &filesLocation, &emittedRelPathWarning] + (const QString &fileName) { + if (emittedRelPathWarning) + return; + if (FileInfo::isAbsolute(fileName)) + return; + if (FileInfo::isAbsolute(group->prefix)) + return; + if (FileInfo::path(filesLocation.filePath()) + == FileInfo::path(m_productContext->product->location.filePath())) { + return; + } + if (m_groupLocationWarnings.contains(filesLocation)) + return; + const QString warningMessage = Tr::tr("Deprecation warning: Group is not located in the " + "same directory as the associated product and references files using a " + "relative path. The base directory for resolving such paths will change " + "in Qbs 1.8 from the directory of the product to the directory of the group. " + "You should probably use an absolute path as the group prefix here."); + m_logger.printWarning(ErrorInfo(warningMessage, filesLocation)); + m_groupLocationWarnings << filesLocation; + emittedRelPathWarning = true; + }; + + ErrorInfo fileError; + if (!patterns.isEmpty()) { + std::for_each(patterns.cbegin(), patterns.cend(), relPathChecker); + SourceWildCards::Ptr wildcards = SourceWildCards::create(); + wildcards->excludePatterns = m_evaluator->stringListValue(item, + QLatin1String("excludeFiles")); + wildcards->prefix = group->prefix; + wildcards->patterns = patterns; + group->wildcards = wildcards; + QSet files = wildcards->expandPatterns(group, m_productContext->product->sourceDirectory); + foreach (const QString &fileName, files) + createSourceArtifact(m_productContext->product, fileName, group, true, filesLocation, + &m_productContext->sourceArtifactLocations, &fileError); + } + + foreach (const QString &fileName, files) { + relPathChecker(fileName); + createSourceArtifact(m_productContext->product, fileName, group, false, filesLocation, + &m_productContext->sourceArtifactLocations, &fileError); + } + if (fileError.hasError()) { + if (group->enabled) { + if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict) + throw ErrorInfo(fileError); + m_logger.printWarning(fileError); + } else { + m_logger.qbsDebug() << fileError.toString(); + } + } + group->name = m_evaluator->stringValue(item, QLatin1String("name")); + if (group->name.isEmpty()) + group->name = Tr::tr("Group %1").arg(m_productContext->product->groups.count()); + m_productContext->product->groups += group; + + class GroupContextSwitcher { + public: + GroupContextSwitcher(ProductContext &context, const GroupConstPtr &newGroup) + : m_context(context), m_oldGroup(context.currentGroup) { + m_context.currentGroup = newGroup; + } + ~GroupContextSwitcher() { m_context.currentGroup = m_oldGroup; } + private: + ProductContext &m_context; + const GroupConstPtr m_oldGroup; + }; + GroupContextSwitcher groupSwitcher(*m_productContext, group); + foreach (Item * const childItem, item->children()) + resolveGroup(childItem, projectContext); +} + +static QString sourceCodeAsFunction(const JSSourceValueConstPtr &value, + const PropertyDeclaration &decl) +{ + const QString args = decl.functionArgumentNames().join(QLatin1Char(',')); + if (value->hasFunctionForm()) { + // Insert the argument list. + QString code = value->sourceCodeForEvaluation(); + 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().toString() + 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(*ctx); + 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 hasArtifactChildren = false; + foreach (Item *child, item->children()) { + if (Q_UNLIKELY(child->type() != ItemType::Artifact)) { + throw ErrorInfo(Tr::tr("'Rule' can only have children of type 'Artifact'."), + child->location()); + } + hasArtifactChildren = true; + resolveRuleArtifact(rule, child); + } + + rule->name = m_evaluator->stringValue(item, QLatin1String("name")); + rule->prepareScript = scriptFunctionValue(item, QLatin1String("prepare")); + rule->outputArtifactsScript = scriptFunctionValue(item, QLatin1String("outputArtifacts")); + if (rule->outputArtifactsScript->isValid()) { + if (hasArtifactChildren) + throw ErrorInfo(Tr::tr("The Rule.outputArtifacts script is not allowed in rules " + "that contain Artifact items."), + item->location()); + rule->outputFileTags = m_evaluator->fileTagsValue(item, QStringLiteral("outputFileTags")); + if (rule->outputFileTags.isEmpty()) + throw ErrorInfo(Tr::tr("Rule.outputFileTags must be specified if " + "Rule.outputArtifacts is specified."), + item->location()); + } + + rule->multiplex = m_evaluator->boolValue(item, QLatin1String("multiplex")); + rule->alwaysRun = m_evaluator->boolValue(item, QLatin1String("alwaysRun")); + rule->inputs = m_evaluator->fileTagsValue(item, QLatin1String("inputs")); + rule->inputsFromDependencies + = m_evaluator->fileTagsValue(item, QLatin1String("inputsFromDependencies")); + rule->auxiliaryInputs + = m_evaluator->fileTagsValue(item, QLatin1String("auxiliaryInputs")); + rule->excludedAuxiliaryInputs + = m_evaluator->fileTagsValue(item, QLatin1String("excludedAuxiliaryInputs")); + rule->explicitlyDependsOn + = m_evaluator->fileTagsValue(item, QLatin1String("explicitlyDependsOn")); + rule->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule; + if (!rule->multiplex && !rule->requiresInputs()) { + const QString message = Tr::tr("Rule has no inputs, but is not a multiplex rule."); + ErrorInfo error(message, item->location()); + if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict) + throw error; + m_logger.printWarning(error); + return; + } + if (m_productContext) + m_productContext->product->rules += rule; + else + projectContext->rules += rule; +} + +void ProjectResolver::resolveRuleArtifact(const RulePtr &rule, Item *item) +{ + RuleArtifactPtr artifact = RuleArtifact::create(); + rule->artifacts += artifact; + artifact->location = item->location(); + + if (const auto sourceProperty = item->sourceProperty(QStringLiteral("filePath"))) + artifact->filePathLocation = sourceProperty->location(); + + artifact->filePath = verbatimValue(item, QLatin1String("filePath")); + artifact->fileTags = m_evaluator->fileTagsValue(item, QLatin1String("fileTags")); + artifact->alwaysUpdated = m_evaluator->boolValue(item, QLatin1String("alwaysUpdated")); + + QualifiedIdSet seenBindings; + for (Item *obj = item; obj; obj = obj->prototype()) { + for (QMap::const_iterator it = obj->properties().constBegin(); + it != obj->properties().constEnd(); ++it) + { + if (it.value()->type() != Value::ItemValueType) + continue; + resolveRuleArtifactBinding(artifact, it.value().staticCast()->item(), + QStringList(it.key()), &seenBindings); + } + } +} + +void ProjectResolver::resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArtifact, + Item *item, + const QStringList &namePrefix, + QualifiedIdSet *seenBindings) +{ + for (QMap::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()->item(), name, + seenBindings); + } else if (it.value()->type() == Value::JSSourceValueType) { + const QualifiedIdSet::InsertResult insertResult = seenBindings->insert(name); + if (!insertResult.second) + continue; + JSSourceValuePtr sourceValue = it.value().staticCast(); + RuleArtifact::Binding rab; + rab.name = name; + rab.code = sourceValue->sourceCodeForEvaluation(); + rab.location = sourceValue->location(); + ruleArtifact->bindings += rab; + } else { + QBS_ASSERT(!"unexpected value type", continue); + } + } +} + +void ProjectResolver::resolveFileTagger(Item *item, ProjectContext *projectContext) +{ + checkCancelation(); + QList &fileTaggers = m_productContext + ? m_productContext->product->fileTaggers + : projectContext->fileTaggers; + const QStringList patterns = m_evaluator->stringListValue(item, QLatin1String("patterns")); + if (patterns.isEmpty()) + throw ErrorInfo(Tr::tr("FileTagger.patterns must be a non-empty list."), item->location()); + + const FileTags fileTags = m_evaluator->fileTagsValue(item, QLatin1String("fileTags")); + if (fileTags.isEmpty()) + throw ErrorInfo(Tr::tr("FileTagger.fileTags must not be empty."), item->location()); + + 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::resolveScanner(Item *item, ProjectResolver::ProjectContext *projectContext) +{ + checkCancelation(); + if (!m_evaluator->boolValue(item, QLatin1String("condition"))) { + m_logger.qbsTrace() << "[PR] scanner condition is false"; + return; + } + + ResolvedScannerPtr scanner = ResolvedScanner::create(); + scanner->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule; + scanner->inputs = m_evaluator->fileTagsValue(item, QLatin1String("inputs")); + scanner->recursive = m_evaluator->boolValue(item, QLatin1String("recursive")); + scanner->searchPathsScript = scriptFunctionValue(item, QLatin1String("searchPaths")); + scanner->scanScript = scriptFunctionValue(item, QLatin1String("scan")); + m_productContext->product->scanners += scanner; +} + +QList ProjectResolver::getProductDependencies(const ResolvedProductConstPtr &product, + const ModuleLoaderResult::ProductInfo &productInfo, bool &disabledDependency) +{ + QList dependencies = productInfo.usedProducts; + QList usedProducts; + for (int i = dependencies.count() - 1; i >= 0; --i) { + const ModuleLoaderResult::ProductInfo::Dependency &dependency = dependencies.at(i); + QBS_CHECK(dependency.name.isEmpty() != dependency.productTypes.isEmpty()); + if (!dependency.productTypes.isEmpty()) { + foreach (const FileTag &tag, dependency.productTypes) { + const QList productsForTag = m_productsByType.value(tag); + foreach (const ResolvedProductPtr &p, productsForTag) { + if (p == product || !p->enabled + || (dependency.limitToSubProject && !product->isInParentProject(p))) { + continue; + } + usedProducts << p; + ModuleLoaderResult::ProductInfo::Dependency newDependency; + newDependency.name = p->name; + newDependency.profile = p->profile; + dependencies << newDependency; + } + } + dependencies.removeAt(i); + } else if (dependency.profile == QLatin1String("*")) { + foreach (const ResolvedProductPtr &p, m_productsByName) { + if (p->name != dependency.name || p == product || !p->enabled + || (dependency.limitToSubProject && !product->isInParentProject(p))) { + continue; + } + usedProducts << p; + ModuleLoaderResult::ProductInfo::Dependency newDependency; + newDependency.name = p->name; + newDependency.profile = p->profile; + dependencies << newDependency; + } + dependencies.removeAt(i); + } else { + const ResolvedProductPtr &usedProduct + = m_productsByName.value(dependency.uniqueName()); + if (!usedProduct) { + throw ErrorInfo(Tr::tr("Product '%1' depends on '%2', which does not exist.") + .arg(product->name, dependency.uniqueName()), product->location); + } + if (!usedProduct->enabled) { + if (!dependency.isRequired) + continue; + ErrorInfo e; + e.append(Tr::tr("Product '%1' depends on '%2',") + .arg(product->name, usedProduct->name), product->location); + e.append(Tr::tr("but product '%1' is disabled.").arg(usedProduct->name), + usedProduct->location); + if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict) + throw e; + disabledDependency = true; + } + usedProducts << usedProduct; + } + } + return usedProducts; +} + +void ProjectResolver::matchArtifactProperties(const ResolvedProductPtr &product, + const QList &artifacts) +{ + foreach (const SourceArtifactPtr &artifact, artifacts) { + foreach (const ArtifactPropertiesConstPtr &artifactProperties, + product->artifactProperties) { + if (artifact->fileTags.matches(artifactProperties->fileTagsFilter())) + artifact->properties = artifactProperties->propertyMap(); + } + } +} + +void ProjectResolver::printProfilingInfo() +{ + if (!m_setupParams.logElapsedTime()) + return; + m_logger.qbsLog(LoggerInfo, true) << "\t" << Tr::tr("All property evaluation took %1.") + .arg(elapsedTimeString(m_elapsedTimeAllPropEval)); + m_logger.qbsLog(LoggerInfo, true) << "\t" << Tr::tr("Module property evaluation took %1.") + .arg(elapsedTimeString(m_elapsedTimeModPropEval)); + m_logger.qbsLog(LoggerInfo, true) << "\t" + << Tr::tr("Resolving groups (without module property " + "evaluation) took %1.") + .arg(elapsedTimeString(m_elapsedTimeGroups)); +} + +static bool hasDependencyCycle(QSet *checked, + QSet *branch, + const ResolvedProductPtr &product, + ErrorInfo *error) +{ + if (branch->contains(product.data())) + return true; + if (checked->contains(product.data())) + return false; + checked->insert(product.data()); + branch->insert(product.data()); + foreach (const ResolvedProductPtr &dep, product->dependencies) { + if (hasDependencyCycle(checked, branch, dep, error)) { + error->prepend(dep->name, dep->location); + return true; + } + } + branch->remove(product.data()); + return false; +} + +using DependencyMap = QHash>; +void gatherDependencies(ResolvedProduct *product, DependencyMap &dependencies) +{ + if (dependencies.contains(product)) + return; + QSet &productDeps = dependencies[product]; + foreach (const ResolvedProductPtr &dep, product->dependencies) { + productDeps << dep.data(); + gatherDependencies(dep.data(), dependencies); + productDeps += dependencies.value(dep.data()); + } +} + + + +static DependencyMap allDependencies(const QList &products) +{ + DependencyMap dependencies; + foreach (const ResolvedProductPtr &product, products) + gatherDependencies(product.data(), dependencies); + return dependencies; +} + +void ProjectResolver::resolveProductDependencies(const ProjectContext &projectContext) +{ + // Resolve all inter-product dependencies. + QList allProducts = projectContext.project->allProducts(); + bool disabledDependency = false; + foreach (const ResolvedProductPtr &rproduct, allProducts) { + if (!rproduct->enabled) + continue; + Item *productItem = m_productItemMap.value(rproduct); + const ModuleLoaderResult::ProductInfo &productInfo + = m_loadResult.productInfos.value(productItem); + foreach (const ResolvedProductPtr &usedProduct, + getProductDependencies(rproduct, productInfo, disabledDependency)) { + rproduct->dependencies.insert(usedProduct); + } + } + + // Check for cyclic dependencies. + QSet checked; + foreach (const ResolvedProductPtr &rproduct, allProducts) { + QSet branch; + ErrorInfo error; + if (hasDependencyCycle(&checked, &branch, rproduct, &error)) { + error.prepend(rproduct->name, rproduct->location); + error.prepend(Tr::tr("Cyclic dependencies detected.")); + throw error; + } + } + + // Mark all products as disabled that have a disabled dependency. + if (disabledDependency && m_setupParams.productErrorMode() == ErrorHandlingMode::Relaxed) { + const DependencyMap allDeps = allDependencies(allProducts); + DependencyMap allDepsReversed; + for (auto it = allDeps.constBegin(); it != allDeps.constEnd(); ++it) { + foreach (ResolvedProduct *dep, it.value()) + allDepsReversed[dep] << it.key(); + } + for (auto it = allDepsReversed.constBegin(); it != allDepsReversed.constEnd(); ++it) { + if (it.key()->enabled) + continue; + foreach (ResolvedProduct * const dependingProduct, it.value()) { + if (dependingProduct->enabled) { + m_logger.qbsWarning() << Tr::tr("Disabling product '%1', because it depends on " + "disabled product '%2'.") + .arg(dependingProduct->name, it.key()->name); + dependingProduct->enabled = false; + } + } + } + } +} + +void ProjectResolver::postProcess(const ResolvedProductPtr &product, + ProjectContext *projectContext) const +{ + product->fileTaggers += projectContext->fileTaggers; + foreach (const RulePtr &rule, projectContext->rules) + product->rules += rule; +} + +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, 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, bool lookupPrototype) +{ + AccumulatingTimer modPropEvalTimer(m_setupParams.logElapsedTime() + ? &m_elapsedTimeModPropEval : nullptr); + QVariantMap moduleValues; + foreach (const Item::Module &module, item->modules()) { + const QString fullName = module.name.toString(); + moduleValues[fullName] = evaluateProperties(module.item, lookupPrototype); + } + + QVariantMap result; + result[QLatin1String("modules")] = moduleValues; + return result; +} + +QVariantMap ProjectResolver::evaluateProperties(Item *item, bool lookupPrototype) +{ + const QVariantMap tmplt; + return evaluateProperties(item, item, tmplt, lookupPrototype); +} + +QVariantMap ProjectResolver::evaluateProperties(const Item *item, const Item *propertiesContainer, + const QVariantMap &tmplt, bool lookupPrototype) +{ + AccumulatingTimer propEvalTimer(m_setupParams.logElapsedTime() + ? &m_elapsedTimeAllPropEval : nullptr); + QVariantMap result = tmplt; + for (QMap::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; + const PropertyDeclaration pd = item->propertyDeclaration(it.key()); + if (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(m_evaluator->engine()->lastErrorString(scriptValue), + 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(); + QVariant v = vvp->value(); + + if (v.isNull() && !item->propertyDeclaration(it.key()).isScalar()) // QTBUG-51237 + v = QStringList(); + + result[it.key()] = v; + break; + } + } + } + return lookupPrototype && propertiesContainer->prototype() + ? evaluateProperties(item, propertiesContainer->prototype(), result, true) + : result; +} + +QVariantMap ProjectResolver::createProductConfig() +{ + EvalCacheEnabler cachingEnabler(m_evaluator); + 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 ItemFuncPtr f = mappings.value(item->type()); + QBS_CHECK(f); + if (item->type() == ItemType::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; + 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 00000000..45e63eee --- /dev/null +++ b/src/lib/corelib/language/projectresolver.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROJECTRESOLVER_H +#define PROJECTRESOLVER_H + +#include "filetags.h" +#include "itemtype.h" +#include "moduleloader.h" + +#include + +#include +#include + +namespace qbs { +namespace Internal { + +class Evaluator; +class Item; +class ProgressObserver; +class ScriptEngine; +class QualifiedIdSet; + +class ProjectResolver +{ +public: + ProjectResolver(Evaluator *evaluator, const ModuleLoaderResult &loadResult, + const SetupProjectParameters &setupParameters, Logger &logger); + ~ProjectResolver(); + + void setProgressObserver(ProgressObserver *observer); + TopLevelProjectPtr resolve(); + + static void applyFileTaggers(const SourceArtifactPtr &artifact, + const ResolvedProductConstPtr &product, Logger &logger); + + static SourceArtifactPtr createSourceArtifact(const ResolvedProductConstPtr &rproduct, + const QString &fileName, const GroupPtr &group, bool wildcard, + const CodeLocation &filesLocation = CodeLocation(), + QHash *fileLocations = nullptr, ErrorInfo *errorInfo = nullptr); + +private: + struct ProjectContext; + struct ProductContext; + struct ModuleContext; + + void checkCancelation() const; + QString verbatimValue(const ValueConstPtr &value, bool *propertyWasSet = 0) const; + QString verbatimValue(Item *item, const QString &name, bool *propertyWasSet = 0) const; + ScriptFunctionPtr scriptFunctionValue(Item *item, const QString &name) const; + ResolvedFileContextPtr resolvedFileContext(const FileContextConstPtr &ctx) const; + void ignoreItem(Item *item, ProjectContext *projectContext); + TopLevelProjectPtr resolveTopLevelProject(); + void resolveProject(Item *item, ProjectContext *projectContext); + void resolveSubProject(Item *item, ProjectContext *projectContext); + void resolveProduct(Item *item, ProjectContext *projectContext); + void resolveModules(const Item *item, ProjectContext *projectContext); + void resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct, + ProjectContext *projectContext); + QVariantMap resolveAdditionalModuleProperties(const Item *group, + const QVariantMap ¤tValues); + void resolveGroup(Item *item, ProjectContext *projectContext); + void resolveRule(Item *item, ProjectContext *projectContext); + void resolveRuleArtifact(const RulePtr &rule, Item *item); + static void resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArtifact, Item *item, + const QStringList &namePrefix, + QualifiedIdSet *seenBindings); + void resolveFileTagger(Item *item, ProjectContext *projectContext); + void resolveScanner(Item *item, ProjectContext *projectContext); + void resolveProductDependencies(const ProjectContext &projectContext); + void postProcess(const ResolvedProductPtr &product, ProjectContext *projectContext) const; + void applyFileTaggers(const ResolvedProductPtr &product) const; + QVariantMap evaluateModuleValues(Item *item, bool lookupPrototype = true); + QVariantMap evaluateProperties(Item *item, bool lookupPrototype = true); + QVariantMap evaluateProperties(const Item *item, const Item *propertiesContainer, + const QVariantMap &tmplt, bool lookupPrototype = true); + QVariantMap createProductConfig(); + QString convertPathProperty(const QString &path, const QString &dirPath) const; + QStringList convertPathListProperty(const QStringList &paths, const QString &dirPath) const; + ProjectContext createProjectContext(ProjectContext *parentProjectContext) const; + QList getProductDependencies(const ResolvedProductConstPtr &product, + const ModuleLoaderResult::ProductInfo &productInfo, bool &disabledDependency); + static void matchArtifactProperties(const ResolvedProductPtr &product, + const QList &artifacts); + void printProfilingInfo(); + + Evaluator *m_evaluator; + Logger &m_logger; + ScriptEngine *m_engine; + ProgressObserver *m_progressObserver; + ProductContext *m_productContext; + ModuleContext *m_moduleContext; + QMap m_productsByName; + QHash > m_productsByType; + QHash m_productItemMap; + mutable QHash m_fileContextMap; + const SetupProjectParameters &m_setupParams; + const ModuleLoaderResult &m_loadResult; + QSet m_groupLocationWarnings; + qint64 m_elapsedTimeModPropEval; + qint64 m_elapsedTimeAllPropEval; + qint64 m_elapsedTimeGroups; + + typedef void (ProjectResolver::*ItemFuncPtr)(Item *item, ProjectContext *projectContext); + typedef QMap 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.cpp b/src/lib/corelib/language/property.cpp new file mode 100644 index 00000000..2556a59a --- /dev/null +++ b/src/lib/corelib/language/property.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "property.h" + +#include + +namespace qbs { +namespace Internal { + +void storePropertySet(PersistentPool &pool, const PropertySet &propertySet) +{ + pool.stream() << propertySet.count(); + foreach (const Property &p, propertySet) { + pool.storeString(p.moduleName); + pool.storeString(p.propertyName); + pool.stream() << p.value << static_cast(p.kind); + } +} + +PropertySet restorePropertySet(PersistentPool &pool) +{ + int count; + pool.stream() >> count; + PropertySet propertySet; + propertySet.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(k); + propertySet += p; + } + return propertySet; +} + +void storePropertyHash(PersistentPool &pool, const PropertyHash &propertyHash) +{ + pool.stream() << propertyHash.count(); + for (auto it = propertyHash.constBegin(); it != propertyHash.constEnd(); ++it) { + pool.storeString(it.key()); + const PropertySet &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 + } + } +} + +PropertyHash restorePropertyHash(PersistentPool &pool) +{ + int count; + pool.stream() >> count; + PropertyHash propertyHash; + propertyHash.reserve(count); + while (--count >= 0) { + const QString artifactName = pool.idLoadString(); + int listCount; + pool.stream() >> listCount; + PropertySet 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; + } + propertyHash.insert(artifactName, list); + } + return propertyHash; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/property.h b/src/lib/corelib/language/property.h new file mode 100644 index 00000000..b98b4673 --- /dev/null +++ b/src/lib/corelib/language/property.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROPERTY_H +#define QBS_PROPERTY_H + +#include +#include +#include + +namespace qbs { +namespace Internal { +class PersistentPool; + +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) + { + } + + 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 PropertySet; +typedef QHash PropertyHash; + +void storePropertySet(PersistentPool &pool, const PropertySet &list); +PropertySet restorePropertySet(PersistentPool &pool); +void storePropertyHash(PersistentPool &pool, const PropertyHash &propertyHash); +PropertyHash restorePropertyHash(PersistentPool &pool); + +} // 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 00000000..14b43e73 --- /dev/null +++ b/src/lib/corelib/language/propertydeclaration.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "propertydeclaration.h" + +#include "deprecationinfo.h" + +#include +#include +#include + +namespace qbs { +namespace Internal { + +class PropertyDeclarationData : public QSharedData +{ +public: + PropertyDeclarationData() + : type(PropertyDeclaration::UnknownType) + , flags(PropertyDeclaration::DefaultFlags) + { + } + + QString name; + PropertyDeclaration::Type type; + PropertyDeclaration::Flags flags; + QScriptValue allowedValues; + QString description; + QString initialValueSource; + QStringList functionArgumentNames; + DeprecationInfo deprecationInfo; +}; + + +PropertyDeclaration::PropertyDeclaration() + : d(new PropertyDeclarationData) +{ +} + +PropertyDeclaration::PropertyDeclaration(const QString &name, Type type, Flags flags) + : d(new PropertyDeclarationData) +{ + d->name = name; + d->type = type; + d->flags = flags; +} + +PropertyDeclaration::PropertyDeclaration(const PropertyDeclaration &other) + : d(other.d) +{ +} + +PropertyDeclaration::~PropertyDeclaration() +{ +} + +PropertyDeclaration &PropertyDeclaration::operator=(const PropertyDeclaration &other) +{ + d = other.d; + return *this; +} + +bool PropertyDeclaration::isValid() const +{ + return d && d->type != UnknownType; +} + +bool PropertyDeclaration::isScalar() const +{ + // ### Should be determined by a PropertyOption in the future. + return d->type != PathList && d->type != StringList; +} + +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; +} + +QString PropertyDeclaration::typeString() const +{ + switch (type()) { + case Boolean: return QLatin1String("bool"); + case Integer: return QLatin1String("int"); + case Path: return QLatin1String("path"); + case PathList: return QLatin1String("pathList"); + case String: return QLatin1String("string"); + case StringList: return QLatin1String("stringList"); + case Variant: return QLatin1String("variant"); + case UnknownType: return QLatin1String("unknown"); + } + Q_UNREACHABLE(); // For stupid compilers. +} + +const QString &PropertyDeclaration::name() const +{ + return d->name; +} + +void PropertyDeclaration::setName(const QString &name) +{ + d->name = name; +} + +PropertyDeclaration::Type PropertyDeclaration::type() const +{ + return d->type; +} + +void PropertyDeclaration::setType(PropertyDeclaration::Type t) +{ + d->type = t; +} + +PropertyDeclaration::Flags PropertyDeclaration::flags() const +{ + return d->flags; +} + +void PropertyDeclaration::setFlags(Flags f) +{ + d->flags = f; +} + +const QScriptValue &PropertyDeclaration::allowedValues() const +{ + return d->allowedValues; +} + +void PropertyDeclaration::setAllowedValues(const QScriptValue &v) +{ + d->allowedValues = v; +} + +const QString &PropertyDeclaration::description() const +{ + return d->description; +} + +void PropertyDeclaration::setDescription(const QString &str) +{ + d->description = str; +} + +const QString &PropertyDeclaration::initialValueSource() const +{ + return d->initialValueSource; +} + +void PropertyDeclaration::setInitialValueSource(const QString &str) +{ + d->initialValueSource = str; +} + +const QStringList &PropertyDeclaration::functionArgumentNames() const +{ + return d->functionArgumentNames; +} + +void PropertyDeclaration::setFunctionArgumentNames(const QStringList &lst) +{ + d->functionArgumentNames = lst; +} + +bool PropertyDeclaration::isDeprecated() const +{ + return d->deprecationInfo.isValid(); +} + +const DeprecationInfo &PropertyDeclaration::deprecationInfo() const +{ + return d->deprecationInfo; +} + +void PropertyDeclaration::setDeprecationInfo(const DeprecationInfo &deprecationInfo) +{ + d->deprecationInfo = deprecationInfo; +} + +} // 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 00000000..5c72dd0c --- /dev/null +++ b/src/lib/corelib/language/propertydeclaration.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PROPERTYDECLARATION_H +#define QBS_PROPERTYDECLARATION_H + +#include + +QT_BEGIN_NAMESPACE +class QScriptValue; +class QString; +class QStringList; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { +class DeprecationInfo; +class PropertyDeclarationData; + +class PropertyDeclaration +{ +public: + enum Type + { + UnknownType, + Boolean, + Integer, + Path, + PathList, + String, + StringList, + Variant + }; + + 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(const PropertyDeclaration &other); + ~PropertyDeclaration(); + + PropertyDeclaration &operator=(const PropertyDeclaration &other); + + bool isValid() const; + bool isScalar() const; + + static Type propertyTypeFromString(const QString &typeName); + QString typeString() const; + + const QString &name() const; + void setName(const QString &name); + + Type type() const; + void setType(Type t); + + Flags flags() const; + void setFlags(Flags f); + + const QScriptValue &allowedValues() const; + void setAllowedValues(const QScriptValue &v); + + const QString &description() const; + void setDescription(const QString &str); + + const QString &initialValueSource() const; + void setInitialValueSource(const QString &str); + + const QStringList &functionArgumentNames() const; + void setFunctionArgumentNames(const QStringList &lst); + + bool isDeprecated() const; + const DeprecationInfo &deprecationInfo() const; + void setDeprecationInfo(const DeprecationInfo &deprecationInfo); + +private: + QSharedDataPointer d; +}; + +} // 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 00000000..1105f873 --- /dev/null +++ b/src/lib/corelib/language/propertymapinternal.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "propertymapinternal.h" + +#include +#include +#include + +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_impl(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_impl(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 toJSLiteral_impl(m_value); +} + +void PropertyMapInternal::load(PersistentPool &pool) +{ + m_value = pool.loadVariantMap(); +} + +void PropertyMapInternal::store(PersistentPool &pool) const +{ + pool.store(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 00000000..b4948d96 --- /dev/null +++ b/src/lib/corelib/language/propertymapinternal.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PROPERTYMAPINTERNAL_H +#define QBS_PROPERTYMAPINTERNAL_H + +#include "forward_decls.h" +#include +#include + +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/qualifiedid.cpp b/src/lib/corelib/language/qualifiedid.cpp new file mode 100644 index 00000000..49d88745 --- /dev/null +++ b/src/lib/corelib/language/qualifiedid.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qualifiedid.h" + +namespace qbs { +namespace Internal { + +QualifiedId::QualifiedId() +{ +} + +QualifiedId::QualifiedId(const QString &singlePartName) + : QStringList(singlePartName) +{ +} + +QualifiedId::QualifiedId(const QStringList &nameParts) + : QStringList(nameParts) +{ +} + +QualifiedId QualifiedId::fromString(const QString &str) +{ + return QualifiedId(str.split(QLatin1Char('.'))); +} + +QString QualifiedId::toString() const +{ + return join(QLatin1Char('.')); +} + +bool operator<(const QualifiedId &a, const QualifiedId &b) +{ + const int c = qMin(a.count(), b.count()); + for (int i = 0; i < c; ++i) { + int n = a.at(i).compare(b.at(i)); + if (n < 0) + return true; + if (n > 0) + return false; + } + return a.count() < b.count(); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/qualifiedid.h b/src/lib/corelib/language/qualifiedid.h new file mode 100644 index 00000000..5414a2ab --- /dev/null +++ b/src/lib/corelib/language/qualifiedid.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_QUALIFIEDID_H +#define QBS_QUALIFIEDID_H + +#include +#include + +#include + +namespace qbs { +namespace Internal { + +class QualifiedId : public QStringList +{ +public: + QualifiedId(); + QualifiedId(const QString &singlePartName); + QualifiedId(const QStringList &nameParts); + + static QualifiedId fromString(const QString &str); + QString toString() const; +}; + +bool operator<(const QualifiedId &a, const QualifiedId &b); +inline uint qHash(const QualifiedId &qid) { return qHash(qid.toString()); } + +class QualifiedIdSet : public std::set +{ +public: + typedef std::pair InsertResult; +}; + +// Values are the properties with a dependency on the key property +using PropertyDependencies = QHash; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_QUALIFIEDID_H diff --git a/src/lib/corelib/language/resolvedfilecontext.cpp b/src/lib/corelib/language/resolvedfilecontext.cpp new file mode 100644 index 00000000..9dc7ede8 --- /dev/null +++ b/src/lib/corelib/language/resolvedfilecontext.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "resolvedfilecontext.h" + +#include "jsimports.h" +#include + +namespace qbs { +namespace Internal { + +ResolvedFileContext::ResolvedFileContext(const FileContextBase &ctx) + : FileContextBase(ctx) +{ +} + +void ResolvedFileContext::load(PersistentPool &pool) +{ + m_filePath = pool.idLoadString(); + m_jsExtensions = pool.idLoadStringList(); + m_searchPaths = pool.idLoadStringList(); + int count; + pool.stream() >> count; + for (int i = 0; i < count; ++i) { + JsImport jsi; + jsi.scopeName = pool.idLoadString(); + jsi.filePaths = pool.idLoadStringList(); + jsi.location.load(pool); + m_jsImports << jsi; + } +} + +void ResolvedFileContext::store(PersistentPool &pool) const +{ + pool.storeString(m_filePath); + pool.storeStringList(m_jsExtensions); + pool.storeStringList(m_searchPaths); + pool.stream() << m_jsImports.count(); + foreach (const JsImport &jsi, m_jsImports) { + pool.storeString(jsi.scopeName); + pool.storeStringList(jsi.filePaths); + jsi.location.store(pool); + } +} + +bool operator==(const ResolvedFileContext &a, const ResolvedFileContext &b) +{ + return a.filePath() == b.filePath() + && a.jsExtensions().toSet() == b.jsExtensions().toSet() + && a.jsImports().toSet() == b.jsImports().toSet(); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/resolvedfilecontext.h b/src/lib/corelib/language/resolvedfilecontext.h new file mode 100644 index 00000000..e090eb42 --- /dev/null +++ b/src/lib/corelib/language/resolvedfilecontext.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_RESOLVEDFILECONTEXT_H +#define QBS_RESOLVEDFILECONTEXT_H + +#include "forward_decls.h" +#include "filecontextbase.h" + +#include + +namespace qbs { +namespace Internal { + +class ResolvedFileContext : public FileContextBase, public PersistentObject +{ +public: + static ResolvedFileContextPtr create() + { + return ResolvedFileContextPtr(new ResolvedFileContext); + } + + static ResolvedFileContextPtr create(const FileContextBase &baseContext) + { + return ResolvedFileContextPtr(new ResolvedFileContext(baseContext)); + } + +private: + ResolvedFileContext() {} + ResolvedFileContext(const FileContextBase &ctx); + + 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); } + +} // namespace Internal +} // namespace qbs + +#endif // QBS_RESOLVEDFILECONTEXT_H diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp new file mode 100644 index 00000000..3e83fe6c --- /dev/null +++ b/src/lib/corelib/language/scriptengine.cpp @@ -0,0 +1,633 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scriptengine.h" + +#include "filecontextbase.h" +#include "jsimports.h" +#include "propertymapinternal.h" +#include "scriptimporter.h" +#include "scriptpropertyobserver.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +const bool debugJSImports = false; + +bool operator==(const ScriptEngine::PropertyCacheKey &lhs, + const ScriptEngine::PropertyCacheKey &rhs) +{ + return lhs.m_propertyMap == rhs.m_propertyMap + && lhs.m_moduleName == rhs.m_moduleName + && lhs.m_propertyName == rhs.m_propertyName; +} + +static inline uint combineHash(uint h1, uint h2, uint seed) +{ + // stolen from qHash(QPair) + return ((h1 << 16) | (h1 >> 16)) ^ h2 ^ seed; +} + +uint qHash(const ScriptEngine::PropertyCacheKey &k, uint seed = 0) +{ + return combineHash(qHash(k.m_moduleName), + combineHash(qHash(k.m_propertyName), qHash(k.m_propertyMap), seed), seed); +} + +ScriptEngine::ScriptEngine(Logger &logger, EvalContext evalContext, QObject *parent) + : QScriptEngine(parent), m_scriptImporter(new ScriptImporter(this)), + m_propertyCacheEnabled(true), m_logger(logger), m_evalContext(evalContext) +{ + setProcessEventsInterval(1000); // For the cancelation mechanism to work. + m_cancelationError = currentContext()->throwValue(tr("Execution canceled")); + QScriptValue objectProto = globalObject().property(QLatin1String("Object")); + m_definePropertyFunction = objectProto.property(QLatin1String("defineProperty")); + QBS_ASSERT(m_definePropertyFunction.isFunction(), /* ignore */); + m_emptyFunction = evaluate(QLatin1String("(function(){})")); + QBS_ASSERT(m_emptyFunction.isFunction(), /* ignore */); + // Initially push a new context to turn off scope chain insanity mode. + QScriptEngine::pushContext(); + installQbsBuiltins(); + extendJavaScriptBuiltins(); +} + +ScriptEngine::~ScriptEngine() +{ + qDeleteAll(m_ownedVariantMaps); + delete (m_scriptImporter); + if (m_elapsedTimeImporting != -1) { + m_logger.qbsLog(LoggerInfo, true) << Tr::tr("Setting up imports took %1.") + .arg(elapsedTimeString(m_elapsedTimeImporting)); + } +} + +void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue &targetObject) +{ + installImportFunctions(); + m_currentDirPathStack.push(FileInfo::path(fileCtx->filePath())); + m_extensionSearchPathsStack.push(fileCtx->searchPaths()); + + const JsImports jsImports = fileCtx->jsImports(); + for (JsImports::const_iterator it = jsImports.begin(); it != jsImports.end(); ++it) { + import(*it, targetObject); + } + + m_currentDirPathStack.pop(); + m_extensionSearchPathsStack.pop(); + uninstallImportFunctions(); +} + +void ScriptEngine::import(const JsImport &jsImport, QScriptValue &targetObject) +{ + QBS_ASSERT(targetObject.isObject(), return); + QBS_ASSERT(targetObject.engine() == this, return); + + if (debugJSImports) + qDebug() << "[ENGINE] import into " << jsImport.scopeName; + + QScriptValue jsImportValue = m_jsImportCache.value(jsImport); + if (jsImportValue.isValid()) { + if (debugJSImports) + qDebug() << "[ENGINE] " << jsImport.filePaths << " (cache hit)"; + } else { + if (debugJSImports) + qDebug() << "[ENGINE] " << jsImport.filePaths << " (cache miss)"; + jsImportValue = newObject(); + foreach (const QString &filePath, jsImport.filePaths) + importFile(filePath, jsImportValue); + m_jsImportCache.insert(jsImport, jsImportValue); + } + targetObject.setProperty(jsImport.scopeName, jsImportValue); +} + +void ScriptEngine::clearImportsCache() +{ + m_jsImportCache.clear(); +} + +void ScriptEngine::checkContext(const QString &operation, + const DubiousContextList &dubiousContexts) +{ + for (auto it = dubiousContexts.cbegin(); it != dubiousContexts.cend(); ++it) { + const DubiousContext &info = *it; + if (info.context != evalContext()) + continue; + QString warning; + switch (info.context) { + case EvalContext::PropertyEvaluation: + warning = Tr::tr("Suspicious use of %1 during property evaluation.").arg(operation); + if (info.suggestion == DubiousContext::SuggestMoving) + warning += QLatin1Char(' ') + Tr::tr("Should this call be in a Probe instead?"); + break; + case EvalContext::RuleExecution: + warning = Tr::tr("Suspicious use of %1 during rule execution.").arg(operation); + if (info.suggestion == DubiousContext::SuggestMoving) { + warning += QLatin1Char(' ') + + Tr::tr("Should this call be in a JavaScriptCommand instead?"); + } + break; + case EvalContext::ProbeExecution: + case EvalContext::JsCommand: + QBS_ASSERT(false, continue); + break; + } + m_logger.printWarning(ErrorInfo(warning, currentContext()->backtrace())); + return; + } +} + +void ScriptEngine::addPropertyRequestedFromArtifact(const Artifact *artifact, + const Property &property) +{ + m_propertiesRequestedFromArtifact[artifact->filePath()] << property; +} + +void ScriptEngine::enableProfiling(bool enable) +{ + m_elapsedTimeImporting = enable ? 0 : -1; +} + +void ScriptEngine::addToPropertyCache(const QString &moduleName, const QString &propertyName, + const PropertyMapConstPtr &propertyMap, const QVariant &value) +{ + m_propertyCache.insert(PropertyCacheKey(moduleName, propertyName, propertyMap), value); +} + +QVariant ScriptEngine::retrieveFromPropertyCache(const QString &moduleName, + const QString &propertyName, const PropertyMapConstPtr &propertyMap) +{ + return m_propertyCache.value(PropertyCacheKey(moduleName, 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(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); + object.setProperty(name, getterFunc, QScriptValue::PropertyGetter); +} + +static QScriptValue js_deprecatedGet(QScriptContext *context, QScriptEngine *qtengine) +{ + ScriptEngine *engine = static_cast(qtengine); + const QScriptValue data = context->callee().property(QLatin1String("qbsdata")); + engine->logger().qbsWarning() + << ScriptEngine::tr("Property %1 is deprecated. Please use %2 instead.").arg( + data.property(0).toString(), data.property(1).toString()); + return data.property(2); +} + +void ScriptEngine::setDeprecatedProperty(QScriptValue &object, const QString &oldName, + const QString &newName, const QScriptValue &value) +{ + QScriptValue data = newArray(); + data.setProperty(0, oldName); + data.setProperty(1, newName); + data.setProperty(2, value); + QScriptValue getterFunc = newFunction(js_deprecatedGet); + getterFunc.setProperty(QLatin1String("qbsdata"), data); + object.setProperty(oldName, getterFunc, QScriptValue::PropertyGetter + | QScriptValue::SkipInEnumeration); +} + +QProcessEnvironment ScriptEngine::environment() const +{ + return m_environment; +} + +void ScriptEngine::setEnvironment(const QProcessEnvironment &env) +{ + m_environment = env; +} + +void ScriptEngine::importFile(const QString &filePath, QScriptValue &targetObject) +{ + AccumulatingTimer importTimer(m_elapsedTimeImporting != -1 ? &m_elapsedTimeImporting : nullptr); + QFile file(filePath); + if (Q_UNLIKELY(!file.open(QFile::ReadOnly))) + throw ErrorInfo(tr("Cannot open '%1'.").arg(filePath)); + QTextStream stream(&file); + stream.setCodec("UTF-8"); + const QString sourceCode = stream.readAll(); + file.close(); + m_currentDirPathStack.push(FileInfo::path(filePath)); + m_scriptImporter->importSourceCode(sourceCode, filePath, targetObject); + m_currentDirPathStack.pop(); +} + +static QString findExtensionDir(const QStringList &searchPaths, const QString &extensionPath) +{ + foreach (const QString &searchPath, searchPaths) { + const QString dirPath = searchPath + QStringLiteral("/imports/") + extensionPath; + QFileInfo fi(dirPath); + if (fi.exists() && fi.isDir()) + return dirPath; + } + return QString(); +} + +static QScriptValue mergeExtensionObjects(const QScriptValueList &lst) +{ + QScriptValue result; + foreach (const QScriptValue &v, lst) { + if (!result.isValid()) { + result = v; + continue; + } + QScriptValueIterator svit(v); + while (svit.hasNext()) { + svit.next(); + result.setProperty(svit.name(), svit.value()); + } + } + return result; +} + +static QScriptValue loadInternalExtension(QScriptContext *context, ScriptEngine *engine, + const QString &uri) +{ + const QString name = uri.mid(4); // remove the "qbs." part + QScriptValue extensionObj = JsExtensions::loadExtension(engine, name); + if (!extensionObj.isValid()) { + return context->throwError(ScriptEngine::tr("loadExtension: " + "cannot load extension '%1'.").arg(uri)); + } + return extensionObj; +} + +QScriptValue ScriptEngine::js_loadExtension(QScriptContext *context, QScriptEngine *qtengine) +{ + ScriptEngine *engine = static_cast(qtengine); + if (context->argumentCount() < 1) { + return context->throwError( + ScriptEngine::tr("The loadExtension function requires " + "an extension name.")); + } + + if (engine->m_extensionSearchPathsStack.isEmpty()) + return context->throwError( + ScriptEngine::tr("loadExtension: internal error. No search paths.")); + + const QString uri = context->argument(0).toVariant().toString(); + if (engine->m_logger.debugEnabled()) { + engine->m_logger.qbsDebug() + << "[loadExtension] loading extension " << uri; + } + + QString uriAsPath = uri; + uriAsPath.replace(QLatin1Char('.'), QLatin1Char('/')); + const QStringList searchPaths = engine->m_extensionSearchPathsStack.top(); + const QString dirPath = findExtensionDir(searchPaths, uriAsPath); + if (dirPath.isEmpty()) { + if (uri.startsWith(QLatin1String("qbs."))) + return loadInternalExtension(context, engine, uri); + + return context->throwError( + ScriptEngine::tr("loadExtension: Cannot find extension '%1'. " + "Search paths: %2.").arg(uri, searchPaths.join( + QLatin1String(", ")))); + } + + QDirIterator dit(dirPath, QDir::Files | QDir::Readable); + QScriptValueList values; + try { + while (dit.hasNext()) { + const QString filePath = dit.next(); + if (engine->m_logger.debugEnabled()) { + engine->m_logger.qbsDebug() + << "[loadExtension] importing file " << filePath; + } + QScriptValue obj = engine->newObject(); + engine->importFile(filePath, obj); + values << obj; + } + } catch (const ErrorInfo &e) { + return context->throwError(e.toString()); + } + + return mergeExtensionObjects(values); +} + +QScriptValue ScriptEngine::js_loadFile(QScriptContext *context, QScriptEngine *qtengine) +{ + ScriptEngine *engine = static_cast(qtengine); + if (context->argumentCount() < 1) { + return context->throwError( + ScriptEngine::tr("The loadFile function requires a file path.")); + } + + if (engine->m_currentDirPathStack.isEmpty()) { + return context->throwError( + ScriptEngine::tr("loadFile: internal error. No current directory.")); + } + + QScriptValue result; + const QString relativeFilePath = context->argument(0).toVariant().toString(); + try { + const QString filePath = FileInfo::resolvePath(engine->m_currentDirPathStack.top(), + relativeFilePath); + result = engine->newObject(); + engine->importFile(filePath, result); + } catch (const ErrorInfo &e) { + result = context->throwError(e.toString()); + } + + return result; +} + +void ScriptEngine::addEnvironmentVariable(const QString &name, const QString &value) +{ + m_usedEnvironment.insert(name, value); +} + +void ScriptEngine::addCanonicalFilePathResult(const QString &filePath, + const QString &resultFilePath) +{ + m_canonicalFilePathResult.insert(filePath, resultFilePath); +} + +void ScriptEngine::addFileExistsResult(const QString &filePath, bool exists) +{ + m_fileExistsResult.insert(filePath, exists); +} + +void ScriptEngine::addDirectoryEntriesResult(const QString &path, QDir::Filters filters, + const QStringList &entries) +{ + m_directoryEntriesResult.insert(QPair(path, static_cast(filters)), + entries); +} + +void ScriptEngine::addFileLastModifiedResult(const QString &filePath, const FileTime &fileTime) +{ + m_fileLastModifiedResult.insert(filePath, fileTime); +} + +QSet ScriptEngine::imports() const +{ + QSet filePaths; + foreach (const JsImport &jsImport, m_jsImportCache.keys()) { + foreach (const QString &filePath, jsImport.filePaths) + filePaths << filePath; + } + return filePaths; +} + +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; +} + +void ScriptEngine::cancel() +{ + QTimer::singleShot(0, this, [this] { abort(); }); +} + +void ScriptEngine::abort() +{ + abortEvaluation(m_cancelationError); +} + +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; +}; + +static QScriptValue js_consoleError(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("error expects 1 argument")); + logger->qbsLog(LoggerError) << context->argument(0).toString(); + return engine->undefinedValue(); +} + +static QScriptValue js_consoleWarn(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("error expects 1 argument")); + logger->qbsWarning() << context->argument(0).toString(); + return engine->undefinedValue(); +} + +static QScriptValue js_consoleInfo(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("error expects 1 argument")); + logger->qbsInfo() << context->argument(0).toString(); + return engine->undefinedValue(); +} + +static QScriptValue js_consoleDebug(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + if (Q_UNLIKELY(context->argumentCount() != 1)) + return context->throwError(QScriptContext::SyntaxError, + QLatin1String("error expects 1 argument")); + logger->qbsDebug() << context->argument(0).toString(); + return engine->undefinedValue(); +} + +static QScriptValue js_consoleLog(QScriptContext *context, QScriptEngine *engine, Logger *logger) +{ + return js_consoleDebug(context, engine, logger); +} + +void ScriptEngine::installQbsBuiltins() +{ + globalObject().setProperty(QLatin1String("qbs"), m_qbsObject = newObject()); + + globalObject().setProperty(QLatin1String("console"), m_consoleObject = newObject()); + installConsoleFunction(QLatin1String("debug"), + reinterpret_cast(&js_consoleDebug)); + installConsoleFunction(QLatin1String("error"), + reinterpret_cast(&js_consoleError)); + installConsoleFunction(QLatin1String("info"), + reinterpret_cast(&js_consoleInfo)); + installConsoleFunction(QLatin1String("log"), + reinterpret_cast(&js_consoleLog)); + installConsoleFunction(QLatin1String("warn"), + reinterpret_cast(&js_consoleWarn)); +} + +void ScriptEngine::extendJavaScriptBuiltins() +{ + JSTypeExtender arrayExtender(this, QLatin1String("Array")); + arrayExtender.addFunction(QLatin1String("contains"), + QLatin1String("(function(e){return this.indexOf(e) !== -1;})")); + arrayExtender.addFunction(QLatin1String("containsAll"), + QLatin1String("(function(e){var $this = this;" + "return e.every(function (v) { return $this.contains(v) });})")); + arrayExtender.addFunction(QLatin1String("containsAny"), + QLatin1String("(function(e){var $this = this;" + "return e.some(function (v) { return $this.contains(v) });})")); + arrayExtender.addFunction(QLatin1String("uniqueConcat"), + QLatin1String("(function(other){" + "var r = this.concat();" + "var s = {};" + "r.forEach(function(x){ s[x] = true; });" + "other.forEach(function(x){" + "if (!s[x]) {" + "s[x] = true;" + "r.push(x);" + "}" + "});" + "return r;})")); + + 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;})")); +} + +void ScriptEngine::installFunction(const QString &name, int length, QScriptValue *functionValue, + FunctionSignature f, QScriptValue *targetObject = 0) +{ + if (!functionValue->isValid()) + *functionValue = newFunction(f, length); + (targetObject ? *targetObject : globalObject()).setProperty(name, *functionValue); +} + +void ScriptEngine::installQbsFunction(const QString &name, int length, FunctionSignature f) +{ + QScriptValue functionValue; + installFunction(name, length, &functionValue, f, &m_qbsObject); +} + +void ScriptEngine::installConsoleFunction(const QString &name, FunctionWithArgSignature f) +{ + m_consoleObject.setProperty(name, newFunction(f, &m_logger)); +} + +void ScriptEngine::installImportFunctions() +{ + installFunction(QLatin1String("loadFile"), 1, &m_loadFileFunction, js_loadFile); + installFunction(QLatin1String("loadExtension"), 1, &m_loadExtensionFunction, js_loadExtension); +} + +void ScriptEngine::uninstallImportFunctions() +{ + globalObject().setProperty(QLatin1String("loadFile"), QScriptValue()); + globalObject().setProperty(QLatin1String("loadExtension"), QScriptValue()); +} + +ScriptEngine::PropertyCacheKey::PropertyCacheKey(const QString &moduleName, + const QString &propertyName, const PropertyMapConstPtr &propertyMap) + : m_moduleName(moduleName), m_propertyName(propertyName), m_propertyMap(propertyMap) +{ +} + +} // 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 00000000..837ba4cd --- /dev/null +++ b/src/lib/corelib/language/scriptengine.h @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SCRIPTENGINE_H +#define QBS_SCRIPTENGINE_H + +#include "forward_decls.h" +#include "property.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { +class Artifact; +class JsImport; +class ScriptImporter; +class ScriptPropertyObserver; + +enum class EvalContext { PropertyEvaluation, ProbeExecution, RuleExecution, JsCommand }; +class DubiousContext +{ +public: + enum Suggestion { NoSuggestion, SuggestMoving }; + DubiousContext(EvalContext c, Suggestion s = NoSuggestion) : context(c), suggestion(s) { } + EvalContext context; + Suggestion suggestion; +}; +using DubiousContextList = std::vector; + +class ScriptEngine : public QScriptEngine +{ + Q_OBJECT +public: + ScriptEngine(Logger &logger, EvalContext evalContext, QObject *parent = 0); + ~ScriptEngine(); + + Logger &logger() const { return m_logger; } + void import(const FileContextBaseConstPtr &fileCtx, QScriptValue &targetObject); + void import(const JsImport &jsImport, QScriptValue &targetObject); + void clearImportsCache(); + + void setEvalContext(EvalContext c) { m_evalContext = c; } + EvalContext evalContext() const { return m_evalContext; } + void checkContext(const QString &operation, const DubiousContextList &dubiousContexts); + + void addPropertyRequestedInScript(const Property &property) { + m_propertiesRequestedInScript += property; + } + void addPropertyRequestedFromArtifact(const Artifact *artifact, const Property &property); + void clearRequestedProperties() { + m_propertiesRequestedInScript.clear(); + m_propertiesRequestedFromArtifact.clear(); + } + PropertySet propertiesRequestedInScript() const { return m_propertiesRequestedInScript; } + QHash propertiesRequestedFromArtifact() const { + return m_propertiesRequestedFromArtifact; + } + + void enableProfiling(bool enable); + + void setPropertyCacheEnabled(bool enable) { m_propertyCacheEnabled = enable; } + bool isPropertyCacheEnabled() const { return m_propertyCacheEnabled; } + 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); + void setDeprecatedProperty(QScriptValue &object, const QString &name, const QString &newName, + const QScriptValue &value); + + QProcessEnvironment environment() const; + void setEnvironment(const QProcessEnvironment &env); + void addEnvironmentVariable(const QString &name, const QString &value); + QHash usedEnvironment() const { return m_usedEnvironment; } + void addCanonicalFilePathResult(const QString &filePath, const QString &resultFilePath); + void addFileExistsResult(const QString &filePath, bool exists); + void addDirectoryEntriesResult(const QString &path, QDir::Filters filters, + const QStringList &entries); + void addFileLastModifiedResult(const QString &filePath, const FileTime &fileTime); + QHash canonicalFilePathResults() const { return m_canonicalFilePathResult; } + QHash fileExistsResults() const { return m_fileExistsResult; } + QHash, QStringList> directoryEntriesResults() const + { + return m_directoryEntriesResult; + } + + QHash fileLastModifiedResults() const { return m_fileLastModifiedResult; } + QSet imports() const; + static QScriptValueList argumentList(const QStringList &argumentNames, + const QScriptValue &context); + void registerOwnedVariantMap(QVariantMap *vm) { m_ownedVariantMaps.append(vm); } + + QStringList uncaughtExceptionBacktraceOrEmpty() const { + return hasUncaughtException() ? uncaughtExceptionBacktrace() : QStringList(); + } + bool hasErrorOrException(const QScriptValue &v) const { + return v.isError() || hasUncaughtException(); + } + QScriptValue lastErrorValue(const QScriptValue &v) const { + return v.isError() ? v : uncaughtException(); + } + QString lastErrorString(const QScriptValue &v) const { return lastErrorValue(v).toString(); } + + void cancel(); + +private: + void abort(); + + void installQbsBuiltins(); + void extendJavaScriptBuiltins(); + void installFunction(const QString &name, int length, QScriptValue *functionValue, + FunctionSignature f, QScriptValue *targetObject); + void installQbsFunction(const QString &name, int length, FunctionSignature f); + void installConsoleFunction(const QString &name, FunctionWithArgSignature f); + void installImportFunctions(); + void uninstallImportFunctions(); + void importFile(const QString &filePath, QScriptValue &targetObject); + static QScriptValue js_loadExtension(QScriptContext *context, QScriptEngine *qtengine); + static QScriptValue js_loadFile(QScriptContext *context, QScriptEngine *qtengine); + + class PropertyCacheKey + { + public: + PropertyCacheKey(const QString &moduleName, const QString &propertyName, + const PropertyMapConstPtr &propertyMap); + private: + const QString &m_moduleName; + const QString &m_propertyName; + const PropertyMapConstPtr &m_propertyMap; + + friend bool operator==(const PropertyCacheKey &lhs, const PropertyCacheKey &rhs); + friend uint qHash(const ScriptEngine::PropertyCacheKey &k, uint seed); + }; + + friend bool operator==(const PropertyCacheKey &lhs, const PropertyCacheKey &rhs); + friend uint qHash(const ScriptEngine::PropertyCacheKey &k, uint seed); + + ScriptImporter *m_scriptImporter; + QHash m_jsImportCache; + bool m_propertyCacheEnabled; + QHash m_propertyCache; + PropertySet m_propertiesRequestedInScript; + QHash m_propertiesRequestedFromArtifact; + Logger &m_logger; + QScriptValue m_definePropertyFunction; + QScriptValue m_emptyFunction; + QProcessEnvironment m_environment; + QHash m_usedEnvironment; + QHash m_canonicalFilePathResult; + QHash m_fileExistsResult; + QHash, QStringList> m_directoryEntriesResult; + QHash m_fileLastModifiedResult; + QStack m_currentDirPathStack; + QStack m_extensionSearchPathsStack; + QScriptValue m_loadFileFunction; + QScriptValue m_loadExtensionFunction; + QScriptValue m_qbsObject; + QScriptValue m_consoleObject; + QScriptValue m_cancelationError; + QList m_ownedVariantMaps; + qint64 m_elapsedTimeImporting = -1; + EvalContext m_evalContext; +}; + +class EvalContextSwitcher +{ +public: + EvalContextSwitcher(ScriptEngine *engine, EvalContext newContext) + : m_engine(engine), m_oldContext(engine->evalContext()) + { + engine->setEvalContext(newContext); + } + + ~EvalContextSwitcher() { m_engine->setEvalContext(m_oldContext); } + +private: + ScriptEngine * const m_engine; + const EvalContext m_oldContext; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_SCRIPTENGINE_H diff --git a/src/lib/corelib/language/scriptimporter.cpp b/src/lib/corelib/language/scriptimporter.cpp new file mode 100644 index 00000000..2d48e28b --- /dev/null +++ b/src/lib/corelib/language/scriptimporter.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scriptimporter.h" + +#include +#include +#include +#include +#include + +#include + +namespace qbs { +namespace Internal { + +class IdentifierExtractor : private QbsQmlJS::AST::Visitor +{ +public: + void start(QbsQmlJS::AST::Node *node) + { + m_first = true; + m_barrier = false; + m_suffix += QLatin1String("\nreturn {"); + node->accept(this); + m_suffix += QLatin1String("}})()"); + } + + const QString &suffix() const { return m_suffix; } + +private: + bool visit(QbsQmlJS::AST::SourceElements *) override + { + // Only consider the top level of source elements. + if (m_barrier) + return false; + m_barrier = true; + return true; + } + + void endVisit(QbsQmlJS::AST::SourceElements *) override + { + m_barrier = false; + } + + bool visit(QbsQmlJS::AST::FunctionSourceElement *fse) override + { + add(fse->declaration->name); + return false; + } + + bool visit(QbsQmlJS::AST::VariableDeclaration *vd) override + { + add(vd->name); + return false; + } + + void add(const QStringRef &name) + { + if (m_first) { + m_first = false; + m_suffix.reserve(m_suffix.length() + name.length() * 2 + 1); + } else { + m_suffix.reserve(m_suffix.length() + name.length() * 2 + 2); + m_suffix += QLatin1Char(','); + } + m_suffix += name; + m_suffix += QLatin1Char(':'); + m_suffix += name; + } + + bool m_first; + bool m_barrier; + QString m_suffix; +}; + + +ScriptImporter::ScriptImporter(QScriptEngine *scriptEngine) + : m_engine(scriptEngine) +{ +} + +// ### merge with Evaluator::handleEvaluationError +static ErrorInfo errorInfoFromScriptValue(const QScriptValue &value, const QString &filePath) +{ + if (!value.isError()) + return ErrorInfo(value.toString(), CodeLocation(filePath)); + + return ErrorInfo(value.property(QStringLiteral("message")).toString(), + CodeLocation(value.property(QStringLiteral("fileName")).toString(), + value.property(QStringLiteral("lineNumber")).toInt32(), + false)); +} + +void ScriptImporter::importSourceCode(const QString &sourceCode, const QString &filePath, + QScriptValue &targetObject) +{ + Q_ASSERT(targetObject.isObject()); + // The targetObject 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. + + QString &code = m_sourceCodeCache[filePath]; + if (code.isEmpty()) { + QbsQmlJS::Engine engine; + QbsQmlJS::Lexer lexer(&engine); + lexer.setCode(sourceCode, 1, false); + QbsQmlJS::Parser parser(&engine); + if (!parser.parseProgram()) { + throw ErrorInfo(parser.errorMessage(), CodeLocation(filePath, parser.errorLineNumber(), + parser.errorColumnNumber())); + } + + IdentifierExtractor extractor; + extractor.start(parser.rootNode()); + code = QLatin1String("(function(){\n") + sourceCode + extractor.suffix(); + } + + QScriptValue result = m_engine->evaluate(code, filePath, 0); + if (m_engine->hasUncaughtException()) + throw errorInfoFromScriptValue(result, filePath); + copyProperties(result, targetObject); +} + +void ScriptImporter::copyProperties(const QScriptValue &src, QScriptValue &dst) +{ + QScriptValueIterator it(src); + while (it.hasNext()) { + it.next(); + dst.setProperty(it.name(), it.value()); + } +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/language/scriptimporter.h b/src/lib/corelib/language/scriptimporter.h new file mode 100644 index 00000000..54cd00e0 --- /dev/null +++ b/src/lib/corelib/language/scriptimporter.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SCRIPTIMPORTER_H +#define SCRIPTIMPORTER_H + +#include +#include + +namespace qbs { +namespace Internal { + +class ScriptImporter +{ +public: + ScriptImporter(QScriptEngine *scriptEngine); + void importSourceCode(const QString &sourceCode, const QString &filePath, QScriptValue &targetObject); + +private: + static void copyProperties(const QScriptValue &src, QScriptValue &dst); + + QScriptEngine *m_engine; + QHash m_sourceCodeCache; +}; + +} // namespace Internal +} // namespace qbs + +#endif // SCRIPTIMPORTER_H diff --git a/src/lib/corelib/language/scriptpropertyobserver.h b/src/lib/corelib/language/scriptpropertyobserver.h new file mode 100644 index 00000000..c0c6d161 --- /dev/null +++ b/src/lib/corelib/language/scriptpropertyobserver.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SCRIPTPROPERTYOBSERVER_H +#define QBS_SCRIPTPROPERTYOBSERVER_H + +#include + +QT_BEGIN_NAMESPACE +class QScriptValue; +class QString; +QT_END_NAMESPACE + +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 00000000..53164be8 --- /dev/null +++ b/src/lib/corelib/language/testdata/Banana @@ -0,0 +1 @@ +Peanut butter jelly time! diff --git a/src/lib/corelib/language/testdata/MyProperties.qbs b/src/lib/corelib/language/testdata/MyProperties.qbs new file mode 100644 index 00000000..b29bfc18 --- /dev/null +++ b/src/lib/corelib/language/testdata/MyProperties.qbs @@ -0,0 +1,4 @@ +import qbs + +Properties { +} diff --git a/src/lib/corelib/language/testdata/ParentWithExport.qbs b/src/lib/corelib/language/testdata/ParentWithExport.qbs new file mode 100644 index 00000000..16f9a2cd --- /dev/null +++ b/src/lib/corelib/language/testdata/ParentWithExport.qbs @@ -0,0 +1,6 @@ +Product { + Export { + Depends { name: "dummy" } + dummy.defines: [product.name.toUpperCase()] + } +} diff --git a/src/lib/corelib/language/testdata/aboutdialog.cpp b/src/lib/corelib/language/testdata/aboutdialog.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/corelib/language/testdata/base-validate/base-validate.qbs b/src/lib/corelib/language/testdata/base-validate/base-validate.qbs new file mode 100644 index 00000000..b1b025f9 --- /dev/null +++ b/src/lib/corelib/language/testdata/base-validate/base-validate.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + Depends { name: "m" } +} diff --git a/src/lib/corelib/language/testdata/base-validate/modules/m/MParent.qbs b/src/lib/corelib/language/testdata/base-validate/modules/m/MParent.qbs new file mode 100644 index 00000000..9874fbd5 --- /dev/null +++ b/src/lib/corelib/language/testdata/base-validate/modules/m/MParent.qbs @@ -0,0 +1,6 @@ +import qbs + +Module { + condition: false + validate: true +} diff --git a/src/lib/corelib/language/testdata/base-validate/modules/m/m.qbs b/src/lib/corelib/language/testdata/base-validate/modules/m/m.qbs new file mode 100644 index 00000000..6fcdc9eb --- /dev/null +++ b/src/lib/corelib/language/testdata/base-validate/modules/m/m.qbs @@ -0,0 +1,11 @@ +import qbs + +MParent { + condition: true + validate: { + var parentResult = base; + if (!parentResult) + throw "Parent failed"; + throw "Parent succeeded, child failed."; + } +} diff --git a/src/lib/corelib/language/testdata/baseproperty.qbs b/src/lib/corelib/language/testdata/baseproperty.qbs new file mode 100644 index 00000000..74024aed --- /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 00000000..85b64b76 --- /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 00000000..62391931 --- /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 00000000..0df9f4d5 --- /dev/null +++ b/src/lib/corelib/language/testdata/builtinFunctionInSearchPathsProperty.qbs @@ -0,0 +1,9 @@ +import qbs +import qbs.Environment + +Project { + qbsSearchPaths: { + if (!Environment.getEnv("PATH")) + throw "Environment.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 00000000..16c040ba --- /dev/null +++ b/src/lib/corelib/language/testdata/canonicalArchitecture.qbs @@ -0,0 +1,6 @@ +import qbs +import qbs.Utilities + +Product { + name: Utilities.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 00000000..8ad3660e --- /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 00000000..81782ba4 --- /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/defaultvalue/egon.qbs b/src/lib/corelib/language/testdata/defaultvalue/egon.qbs new file mode 100644 index 00000000..7b254b0c --- /dev/null +++ b/src/lib/corelib/language/testdata/defaultvalue/egon.qbs @@ -0,0 +1,14 @@ +import qbs + +Project { + Product { + name: "dep" + Export { Depends { name: "higher" } } + } + + Product { + name: "egon" + Depends { name: "dep" } + lower.prop1: "blubb" + } +} diff --git a/src/lib/corelib/language/testdata/defaultvalue/modules/higher/higher.qbs b/src/lib/corelib/language/testdata/defaultvalue/modules/higher/higher.qbs new file mode 100644 index 00000000..9894f80c --- /dev/null +++ b/src/lib/corelib/language/testdata/defaultvalue/modules/higher/higher.qbs @@ -0,0 +1,7 @@ +import qbs + +Module { + Depends { name: "lower" } + lower.prop2: lower.prop1 === "egon" ? "withEgon" : original + lower.listProp: lower.prop1 === "egon" ? ["egon"] : original +} diff --git a/src/lib/corelib/language/testdata/defaultvalue/modules/lower/lower.qbs b/src/lib/corelib/language/testdata/defaultvalue/modules/lower/lower.qbs new file mode 100644 index 00000000..0540185e --- /dev/null +++ b/src/lib/corelib/language/testdata/defaultvalue/modules/lower/lower.qbs @@ -0,0 +1,7 @@ +import qbs + +Module { + property string prop1 + property string prop2: prop1 === "blubb" ? "withBlubb" : "withoutBlubb" + property stringList listProp: prop1 === "blubb" ? ["blubb", "other"] : ["other"] +} diff --git a/src/lib/corelib/language/testdata/defaultvalue/test.txt b/src/lib/corelib/language/testdata/defaultvalue/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/corelib/language/testdata/dependencyOnAllProfiles.qbs b/src/lib/corelib/language/testdata/dependencyOnAllProfiles.qbs new file mode 100644 index 00000000..f3b77131 --- /dev/null +++ b/src/lib/corelib/language/testdata/dependencyOnAllProfiles.qbs @@ -0,0 +1,19 @@ +import qbs + +Project { + property string profile1 + property string profile2 + + Product { + name: "dep" + profiles: [project.profile1, project.profile2] + } + + Product { + name: "main" + Depends { + name: "dep" + profiles: [] + } + } +} diff --git a/src/lib/corelib/language/testdata/derived-sub-project/DerivedSubProject.qbs b/src/lib/corelib/language/testdata/derived-sub-project/DerivedSubProject.qbs new file mode 100644 index 00000000..313e04f2 --- /dev/null +++ b/src/lib/corelib/language/testdata/derived-sub-project/DerivedSubProject.qbs @@ -0,0 +1,4 @@ +import qbs + +SubProject { +} diff --git a/src/lib/corelib/language/testdata/derived-sub-project/project.qbs b/src/lib/corelib/language/testdata/derived-sub-project/project.qbs new file mode 100644 index 00000000..1aadd92a --- /dev/null +++ b/src/lib/corelib/language/testdata/derived-sub-project/project.qbs @@ -0,0 +1,10 @@ +import qbs + +Project { + DerivedSubProject { + filePath: "subproject.qbs" + Properties { + name: "something" + } + } +} diff --git a/src/lib/corelib/language/testdata/derived-sub-project/subproject.qbs b/src/lib/corelib/language/testdata/derived-sub-project/subproject.qbs new file mode 100644 index 00000000..18dc695f --- /dev/null +++ b/src/lib/corelib/language/testdata/derived-sub-project/subproject.qbs @@ -0,0 +1,4 @@ +import qbs + +Product { +} diff --git a/src/lib/corelib/language/testdata/drawline.asm b/src/lib/corelib/language/testdata/drawline.asm new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/corelib/language/testdata/dummy.txt b/src/lib/corelib/language/testdata/dummy.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/corelib/language/testdata/environmentvariable.qbs b/src/lib/corelib/language/testdata/environmentvariable.qbs new file mode 100644 index 00000000..2e04c799 --- /dev/null +++ b/src/lib/corelib/language/testdata/environmentvariable.qbs @@ -0,0 +1,5 @@ +import qbs.Environment + +Product { + name: Environment.getEnv("PRODUCT_NAME") +} diff --git a/src/lib/corelib/language/testdata/erroneous/ParentItem.qbs b/src/lib/corelib/language/testdata/erroneous/ParentItem.qbs new file mode 100644 index 00000000..a5ee0326 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/ParentItem.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + property bool cpp +} diff --git a/src/lib/corelib/language/testdata/erroneous/ParentWithExport.qbs b/src/lib/corelib/language/testdata/erroneous/ParentWithExport.qbs new file mode 100644 index 00000000..0037d2c0 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/ParentWithExport.qbs @@ -0,0 +1,7 @@ +import qbs + +Product { + Export { + property bool theProp + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/conflicting-properties-in-export-items.qbs b/src/lib/corelib/language/testdata/erroneous/conflicting-properties-in-export-items.qbs new file mode 100644 index 00000000..021dc786 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/conflicting-properties-in-export-items.qbs @@ -0,0 +1,7 @@ +import qbs + +ParentWithExport { + Export { + property string theProp + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs b/src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs new file mode 100644 index 00000000..54b3343e --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/conflicting_fileTagsFilter.qbs @@ -0,0 +1,13 @@ +import qbs 1.0 + +Application { + Group { + fileTagsFilter: "application" + qbs.install: true + } + Group { + fileTagsFilter: "application" + qbs.install: false + } +} + diff --git a/src/lib/corelib/language/testdata/erroneous/dependency_cycle.qbs b/src/lib/corelib/language/testdata/erroneous/dependency_cycle.qbs new file mode 100644 index 00000000..dce212d9 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/dependency_cycle.qbs @@ -0,0 +1,19 @@ +import qbs + +Project { + CppApplication { + name: "A" + Depends { name: "B" } + files: ["main.cpp"] + } + CppApplication { + name: "B" + Depends { name: "C" } + files: ["main.cpp"] + } + CppApplication { + name: "C" + Depends { name: "A" } + files: ["main.cpp"] + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/dependency_cycle2.qbs b/src/lib/corelib/language/testdata/erroneous/dependency_cycle2.qbs new file mode 100644 index 00000000..ab7a0d6f --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/dependency_cycle2.qbs @@ -0,0 +1,23 @@ +import qbs + +Project { + CppApplication { + name: "A" + Depends { name: "B" } + files: ["main.cpp"] + } + CppApplication { + name: "B" + Depends { name: "C" } + files: ["main.cpp"] + } + CppApplication { + name: "C" + Depends { name: "A" } + files: ["main.cpp"] + } + CppApplication { + name: "D" + files: ["main.cpp"] + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/dependency_cycle3.qbs b/src/lib/corelib/language/testdata/erroneous/dependency_cycle3.qbs new file mode 100644 index 00000000..ab162977 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/dependency_cycle3.qbs @@ -0,0 +1,13 @@ +import qbs + +Project { + Product { + type: ["a"] + name: "A" + Depends { name: "B" } + } + Product { + name: "B" + Depends { productTypes: ["a"] } + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/dependency_cycle4.qbs b/src/lib/corelib/language/testdata/erroneous/dependency_cycle4.qbs new file mode 100644 index 00000000..5d33a59c --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/dependency_cycle4.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + Depends { name: "module-a" } +} diff --git a/src/lib/corelib/language/testdata/erroneous/duplicate_sources.qbs b/src/lib/corelib/language/testdata/erroneous/duplicate_sources.qbs new file mode 100644 index 00000000..0546ac2e --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/duplicate_sources.qbs @@ -0,0 +1,8 @@ +import qbs + +Product { + files: ["main.cpp"] + Group { + files: ["main.cpp"] + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/duplicate_sources_wildcards.qbs b/src/lib/corelib/language/testdata/erroneous/duplicate_sources_wildcards.qbs new file mode 100644 index 00000000..a51aa26d --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/duplicate_sources_wildcards.qbs @@ -0,0 +1,8 @@ +import qbs + +Product { + files: ["*.qbs"] + Group { + files: ["duplicate_sources_wildcards.qbs"] + } +} 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 00000000..91e8f620 --- /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 00000000..c41fe7e9 --- /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-property-option.qbs b/src/lib/corelib/language/testdata/erroneous/invalid-property-option.qbs new file mode 100644 index 00000000..6e74faf8 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/invalid-property-option.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + Depends { name: "module-with-wrong-property-option" } +} 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 00000000..2341d4b9 --- /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 00000000..18f2b044 --- /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 00000000..b9b39273 --- /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 00000000..fc30a2af --- /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 00000000..4fdd6384 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { } diff --git a/src/lib/corelib/language/testdata/erroneous/misused-inherited-property.qbs b/src/lib/corelib/language/testdata/erroneous/misused-inherited-property.qbs new file mode 100644 index 00000000..37ee0415 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/misused-inherited-property.qbs @@ -0,0 +1,5 @@ +import qbs + +ParentItem { + cpp.compilerName: "blubb" +} diff --git a/src/lib/corelib/language/testdata/erroneous/modules/module-a/module-a.qbs b/src/lib/corelib/language/testdata/erroneous/modules/module-a/module-a.qbs new file mode 100644 index 00000000..e0f24191 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/modules/module-a/module-a.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + Depends { name: "module-b" } +} diff --git a/src/lib/corelib/language/testdata/erroneous/modules/module-b/module-b.qbs b/src/lib/corelib/language/testdata/erroneous/modules/module-b/module-b.qbs new file mode 100644 index 00000000..fe3eb2b1 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/modules/module-b/module-b.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + Depends { name: "module-a" } +} diff --git a/src/lib/corelib/language/testdata/erroneous/modules/module-with-wrong-property-option/m.qbs b/src/lib/corelib/language/testdata/erroneous/modules/module-with-wrong-property-option/m.qbs new file mode 100644 index 00000000..8ea67f2c --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/modules/module-with-wrong-property-option/m.qbs @@ -0,0 +1,9 @@ +import qbs + +Module { + property string someProp + PropertyOptions { + name: "s0meProp" + description: "Oops, spelt the property name wrong" + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/modules/prefix1/prefix1.qbs b/src/lib/corelib/language/testdata/erroneous/modules/prefix1/prefix1.qbs new file mode 100644 index 00000000..3e67ba10 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/modules/prefix1/prefix1.qbs @@ -0,0 +1,4 @@ +import qbs + +Module { +} diff --git a/src/lib/corelib/language/testdata/erroneous/modules/prefix1/suffix/suffix.qbs b/src/lib/corelib/language/testdata/erroneous/modules/prefix1/suffix/suffix.qbs new file mode 100644 index 00000000..39479476 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/modules/prefix1/suffix/suffix.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + Depends { name: "prefix1" } +} diff --git a/src/lib/corelib/language/testdata/erroneous/modules/prefix2/prefix2.qbs b/src/lib/corelib/language/testdata/erroneous/modules/prefix2/prefix2.qbs new file mode 100644 index 00000000..bda21b8d --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/modules/prefix2/prefix2.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + Depends { name: "prefix2.suffix" } +} diff --git a/src/lib/corelib/language/testdata/erroneous/modules/prefix2/suffix/suffix.qbs b/src/lib/corelib/language/testdata/erroneous/modules/prefix2/suffix/suffix.qbs new file mode 100644 index 00000000..3e67ba10 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/modules/prefix2/suffix/suffix.qbs @@ -0,0 +1,4 @@ +import qbs + +Module { +} 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 00000000..17c7f6a1 --- /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 00000000..0ecb41b3 --- /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 00000000..6c5899b5 --- /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/oldQbsVersion.qbs b/src/lib/corelib/language/testdata/erroneous/oldQbsVersion.qbs new file mode 100644 index 00000000..fb0915d8 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/oldQbsVersion.qbs @@ -0,0 +1,8 @@ +import qbs + +Project { + minimumQbsVersion: "999.5.4" + Product { + qbs.enableSound: true + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/properties-item-with-invalid-condition.qbs b/src/lib/corelib/language/testdata/erroneous/properties-item-with-invalid-condition.qbs new file mode 100644 index 00000000..00e3267c --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/properties-item-with-invalid-condition.qbs @@ -0,0 +1,9 @@ +import qbs + +Product { + Depends { name: "cpp" } + Properties { + condition: cpp.nonexistingproperty.contains("somevalue") + cpp.defines: ["ABC"] + } +} 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 00000000..6d0960f0 --- /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 00000000..0b0d2734 --- /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 00000000..2a237d15 --- /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 00000000..3940109d --- /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/same-module-prefix1.qbs b/src/lib/corelib/language/testdata/erroneous/same-module-prefix1.qbs new file mode 100644 index 00000000..a5275eb1 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/same-module-prefix1.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + Depends { name: "prefix1.suffix" } +} diff --git a/src/lib/corelib/language/testdata/erroneous/same-module-prefix2.qbs b/src/lib/corelib/language/testdata/erroneous/same-module-prefix2.qbs new file mode 100644 index 00000000..3c4ec02a --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/same-module-prefix2.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + Depends { name: "prefix2" } +} 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 00000000..0a9cd289 --- /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 00000000..ab92d76d --- /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 00000000..af1e50f5 --- /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 00000000..fc251b1a --- /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 00000000..b2edbf01 --- /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 00000000..1dad5f74 --- /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/undeclared_property_in_Properties_item.qbs b/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_Properties_item.qbs new file mode 100644 index 00000000..d9bd00ce --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_Properties_item.qbs @@ -0,0 +1,8 @@ +import qbs + +Product { + Properties { + condition: true + blubb.bla: "x" + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item.qbs b/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item.qbs new file mode 100644 index 00000000..e5fc74b7 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item.qbs @@ -0,0 +1,14 @@ +import qbs + +Project { + Product { + name: "p1" + Export { + Depends { name: "cpp" } + cpp.blubb: "x" + } + } + Product { + Depends { name: "p1" } + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item2.qbs b/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item2.qbs new file mode 100644 index 00000000..1cb4c42e --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item2.qbs @@ -0,0 +1,13 @@ +import qbs + +Project { + Product { + name: "p1" + Export { + something.other: "x" + } + } + Product { + Depends { name: "p1" } + } +} diff --git a/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item3.qbs b/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item3.qbs new file mode 100644 index 00000000..cd23aefe --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/undeclared_property_in_export_item3.qbs @@ -0,0 +1,9 @@ +import qbs + +Project { + Product { + name: "p1" + Export { blubb: false } + } + Product { Depends { name: "p1" } } +} diff --git a/src/lib/corelib/language/testdata/erroneous/undeclared_property_wrapper.qbs b/src/lib/corelib/language/testdata/erroneous/undeclared_property_wrapper.qbs new file mode 100644 index 00000000..ee263ef7 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/undeclared_property_wrapper.qbs @@ -0,0 +1,5 @@ +import qbs + +SubProject { + filePath: "undeclared_property.qbs" +} diff --git a/src/lib/corelib/language/testdata/erroneous/undefined_stringlist_element.qbs b/src/lib/corelib/language/testdata/erroneous/undefined_stringlist_element.qbs new file mode 100644 index 00000000..8e33cd76 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/undefined_stringlist_element.qbs @@ -0,0 +1,4 @@ +Product { + property string blubb + files: ["foo", blubb, "bar"] +} 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 00000000..9e34e924 --- /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 00000000..dcfc79a9 --- /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/erroneous/wrongQbsVersionFormat.qbs b/src/lib/corelib/language/testdata/erroneous/wrongQbsVersionFormat.qbs new file mode 100644 index 00000000..36c9fea7 --- /dev/null +++ b/src/lib/corelib/language/testdata/erroneous/wrongQbsVersionFormat.qbs @@ -0,0 +1,5 @@ +import qbs + +Project { + minimumQbsVersion: "hfyh1234wat?" +} diff --git a/src/lib/corelib/language/testdata/exports.qbs b/src/lib/corelib/language/testdata/exports.qbs new file mode 100644 index 00000000..c480f009 --- /dev/null +++ b/src/lib/corelib/language/testdata/exports.qbs @@ -0,0 +1,105 @@ +import qbs 1.0 +import "exports_product.qbs" as ProductWithInheritedExportItem + +Project { + Application { + name: "myapp" + Depends { name: "mylib" } + Depends { name: "dummy" } + Depends { name: "qbs" } + dummy.defines: ["BUILD_" + product.name.toUpperCase()] + dummy.includePaths: ["./app"] + } + + references: [ + "subdir/exports-mylib.qbs", + "subdir2/exports-mylib2.qbs" + ] + + Application { + name: "A" + Depends { name: "qbs" } + Depends { name: "B" } + } + StaticLibrary { + name: "B" + Export { + Depends { name: "C" } + Depends { name: "qbs" } + } + } + StaticLibrary { + name: "C" + Export { + Depends { name: "D" } + Depends { name: "qbs" } + } + } + StaticLibrary { + name: "D" + } + + Application { + name: "myapp2" + Depends { name: "productWithInheritedExportItem" } + Depends { name: "qbs" } + } + ProductWithInheritedExportItem { + name: "productWithInheritedExportItem" + Export { + dummy.cFlags: base.concat("PRODUCT_" + product.name.toUpperCase()) + dummy.cxxFlags: ["-bar"] + } + } + Application { + name: "myapp3" + Depends { name: "productWithInheritedExportItem" } + } + + Project { + name: "sub1" + Product { + name: "sub p1" + Export { + Depends { name: "dummy" } + dummy.someString: project.name + } + } + } + + Project { + name: "sub2" + Product { + name: "sub p2" + Depends { name: "sub p1" } + } + } + + ParentWithExport { + name: "libA" + Export { Depends { name: "libB" } } + } + + ParentWithExport { name: "libB" } + + ParentWithExport { + name: "libC" + Export { Depends { name: "libA" } } + } + + ParentWithExport { + name: "libD" + Export { Depends { name: "libA" } } + } + + Product { + name: "libE" + + Depends { name: "libD" } + Depends { name: "libC" } + + Group { + qbs.install: false + } + } +} 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 00000000..7fd21329 --- /dev/null +++ b/src/lib/corelib/language/testdata/exports_product.qbs @@ -0,0 +1,8 @@ +Product { + Export { + Depends { name: "dummy" } + dummy.cFlags: ["BASE_" + product.name.toUpperCase()] + 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 00000000..5c435b3b --- /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 00000000..eb1cf57c --- /dev/null +++ b/src/lib/corelib/language/testdata/filetags.qbs @@ -0,0 +1,61 @@ +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" + Group { + files: "main.cpp" // gets file tag "cpp" through the FileTagger + fileTags: ["c++"] + } + } + + Product { + name: "add_file_tag_via_group" + Group { + overrideTags: false + files: "main.cpp" + 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 00000000..ab294349 --- /dev/null +++ b/src/lib/corelib/language/testdata/getNativeSetting.qbs @@ -0,0 +1,24 @@ +import qbs.FileInfo +import qbs.Utilities + +Project { + Product { + name: { + if (qbs.hostOS.contains("macos")) { + return Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductName"); + } else if (qbs.hostOS.contains("windows")) { + var productName = Utilities.getNativeSetting("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName"); + if (productName.contains("Windows")) { + return "Windows"; + } + return undefined; + } else { + return Utilities.getNativeSetting(FileInfo.joinPaths(path, "nativesettings.ini"), "osname"); + } + } + } + + Product { + name: Utilities.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 00000000..244c80c2 --- /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 00000000..22e58765 --- /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 00000000..1ceeb5bb --- /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/id-uniqueness.qbs b/src/lib/corelib/language/testdata/id-uniqueness.qbs new file mode 100644 index 00000000..cfbaf854 --- /dev/null +++ b/src/lib/corelib/language/testdata/id-uniqueness.qbs @@ -0,0 +1,12 @@ +import qbs 1.0 +import "idusagebase.qbs" as DerivedProduct + +Project { + id: theProject + DerivedProduct { + id: baseProduct // OK - even though 'baseProduct' is used in the base item. + } + DerivedProduct { + id: baseProduct // ERROR + } +} diff --git a/src/lib/corelib/language/testdata/idusage.qbs b/src/lib/corelib/language/testdata/idusage.qbs new file mode 100644 index 00000000..42dc43ad --- /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 00000000..483a00cc --- /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/import-collection/collection/file1.js b/src/lib/corelib/language/testdata/import-collection/collection/file1.js new file mode 100644 index 00000000..09c5cc75 --- /dev/null +++ b/src/lib/corelib/language/testdata/import-collection/collection/file1.js @@ -0,0 +1 @@ +function f1() { return "C2f1"; } diff --git a/src/lib/corelib/language/testdata/import-collection/collection/file2.js b/src/lib/corelib/language/testdata/import-collection/collection/file2.js new file mode 100644 index 00000000..ecc0f7c6 --- /dev/null +++ b/src/lib/corelib/language/testdata/import-collection/collection/file2.js @@ -0,0 +1 @@ +function f2() { return "C2f2"; } diff --git a/src/lib/corelib/language/testdata/import-collection/imports/Collection/file1.js b/src/lib/corelib/language/testdata/import-collection/imports/Collection/file1.js new file mode 100644 index 00000000..d305c528 --- /dev/null +++ b/src/lib/corelib/language/testdata/import-collection/imports/Collection/file1.js @@ -0,0 +1 @@ +function f1() { return "C1f1"; } diff --git a/src/lib/corelib/language/testdata/import-collection/imports/Collection/file2.js b/src/lib/corelib/language/testdata/import-collection/imports/Collection/file2.js new file mode 100644 index 00000000..b3516822 --- /dev/null +++ b/src/lib/corelib/language/testdata/import-collection/imports/Collection/file2.js @@ -0,0 +1 @@ +function f2() { return "C1f2"; } diff --git a/src/lib/corelib/language/testdata/import-collection/product.qbs b/src/lib/corelib/language/testdata/import-collection/product.qbs new file mode 100644 index 00000000..0682fcfa --- /dev/null +++ b/src/lib/corelib/language/testdata/import-collection/product.qbs @@ -0,0 +1,7 @@ +import Collection as Collection1 +import "collection" as Collection2 + +Product { + name: "da product" + targetName: Collection1.f1() + Collection1.f2() + Collection2.f1() + Collection2.f2() +} diff --git a/src/lib/corelib/language/testdata/import-collection/project.qbs b/src/lib/corelib/language/testdata/import-collection/project.qbs new file mode 100644 index 00000000..ee34869b --- /dev/null +++ b/src/lib/corelib/language/testdata/import-collection/project.qbs @@ -0,0 +1,6 @@ +import qbs + +Project { + references: ["product.qbs"] +} + diff --git a/src/lib/corelib/language/testdata/invalidBindingInDisabledItem.qbs b/src/lib/corelib/language/testdata/invalidBindingInDisabledItem.qbs new file mode 100644 index 00000000..e3e03a31 --- /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 00000000..df74a263 --- /dev/null +++ b/src/lib/corelib/language/testdata/jsextensions.js @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company 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 00000000..4e939505 --- /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 00000000..388cf974 --- /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 00000000..e69de29b diff --git a/src/lib/corelib/language/testdata/moduleproperties.qbs b/src/lib/corelib/language/testdata/moduleproperties.qbs new file mode 100644 index 00000000..16fe2ea7 --- /dev/null +++ b/src/lib/corelib/language/testdata/moduleproperties.qbs @@ -0,0 +1,50 @@ +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"] + } + Product { + name: "merge_lists_with_prototype_values" + Depends { name: "dummyqt"; submodules: ["gui", "network"] } + Depends { name: "dummy" } + } + + Product { + name: "list_property_that_references_product" + type: ["blubb"] + Depends { name: "dummy" } + dummy.listProp: ["x"] + } + + Product { + name: "list_property_depending_on_overridden_property" + Depends { name: "dummy" } + dummy.listProp2: ["PRODUCT_STUFF"] + dummy.controllingProp: true + } + + Product { + name: "overridden_list_property" + Depends { name: "dummy" } + Properties { + condition: true + overrideListProperties: true + dummy.listProp: ["PRODUCT_STUFF"] + } + } +} diff --git a/src/lib/corelib/language/testdata/modulepropertiesingroups.qbs b/src/lib/corelib/language/testdata/modulepropertiesingroups.qbs new file mode 100644 index 00000000..7a0a99b3 --- /dev/null +++ b/src/lib/corelib/language/testdata/modulepropertiesingroups.qbs @@ -0,0 +1,73 @@ +import qbs 1.0 + +Project { + Product { + name: "grouptest" + + Depends { name: "gmod.gmod1" } + Depends { name: "gmod3" } + Depends { name: "gmod4" } + + gmod.gmod1.gmod1_list2: base.concat([name, gmod.gmod1.gmod1_string]) + gmod.gmod1.gmod1_list3: ["product"] + gmod.gmod1.p1: 1 + + Group { + name: "g1" + files: ["Banana"] + + gmod.gmod1.gmod1_string: name + gmod.gmod1.gmod1_list2: outer.concat([name]) + gmod.gmod1.p2: 2 + gmod2.prop: 1 + gmod2.commonName: "g1" + gmod3.gmod3_string: "g1_gmod3" + gmod4.gmod4_string: "g1_gmod4" + + Group { + name: "g1.1" + + gmod.gmod1.gmod1_string: name + gmod.gmod1.gmod1_list2: outer.concat([name]) + gmod.gmod1.p2: 4 + gmod2.prop: 2 + gmod2.commonName: name + gmod3.gmod3_string: "g1.1_gmod3" + gmod4.gmod4_string: "g1.1_gmod4" + } + + Group { + name: "g1.2" + + gmod.gmod1.gmod1_string: name + gmod.gmod1.gmod1_list2: outer.concat([name]) + gmod.gmod1.p2: 8 + gmod2.commonName: name + gmod3.gmod3_string: "g1.2_gmod3" + } + } + + Group { + name: "g2" + files: ["zort"] + + gmod.gmod1.gmod1_string: name + gmod.gmod1.p1: 2 + gmod.gmod1.p2: 4 + gmod2.prop: 2 + gmod3.gmod3_string: name + "_gmod3" + gmod4.gmod4_string: name + "_gmod4" + + Group { + name: "g2.1" + + Group { + name: "g2.1.1" + + gmod.gmod1.gmod1_list2: [name] + gmod.gmod1.p2: 15 + } + } + } + } +} diff --git a/src/lib/corelib/language/testdata/modules.qbs b/src/lib/corelib/language/testdata/modules.qbs new file mode 100644 index 00000000..c9591631 --- /dev/null +++ b/src/lib/corelib/language/testdata/modules.qbs @@ -0,0 +1,57 @@ +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: "deep_module_name" + property var foo: deepdummy.deep.moat.depth + Depends { + name: "deepdummy.deep.moat" + } + } + Product { + name: "deep_module_name_submodule_syntax1" + property var foo: deepdummy.deep.moat.depth + Depends { + name: "deepdummy.deep" + submodules: ["moat"] + } + } + Product { + name: "deep_module_name_submodule_syntax2" + property var foo: deepdummy.deep.moat.depth + Depends { + name: "deepdummy" + submodules: ["deep.moat"] + } + } + Product { + name: "dummy_twice" + Depends { name: "dummy" } + Depends { name: "dummy" } + } +} diff --git a/src/lib/corelib/language/testdata/modules/deepdummy/deep/moat/dummydeepmoat.qbs b/src/lib/corelib/language/testdata/modules/deepdummy/deep/moat/dummydeepmoat.qbs new file mode 100644 index 00000000..3383d69e --- /dev/null +++ b/src/lib/corelib/language/testdata/modules/deepdummy/deep/moat/dummydeepmoat.qbs @@ -0,0 +1,4 @@ +Module { + property string depth: "abysmal" + 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 00000000..8a235555 --- /dev/null +++ b/src/lib/corelib/language/testdata/modules/dummy/dummy.qbs @@ -0,0 +1,19 @@ +import "dummy_base.qbs" as DummyBase + +DummyBase { + condition: true + property stringList defines + property stringList cFlags + property stringList cxxFlags + property stringList rpaths: ["$ORIGIN"] + property string someString + property string productName: product.name + property string upperCaseProductName: productName.toUpperCase() + property string zort: "zort in dummy" + property pathList includePaths + property stringList listProp: product.type.contains("blubb") ? ["123"] : ["456"] + + property bool controllingProp: false + property stringList listProp2: controllingProp + ? ["DEFAULT_STUFF", "EXTRA_STUFF"] : ["DEFAULT_STUFF"] +} 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 00000000..0ecd8a1d --- /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 00000000..f60c38a7 --- /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 00000000..e87bb27f --- /dev/null +++ b/src/lib/corelib/language/testdata/modules/dummyqt/core/dummycore.qbs @@ -0,0 +1,17 @@ +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 + property string zort: "zort in dummyqt.core" + + Depends { name: "dummy" } + dummy.defines: ["QT_CORE"] + dummy.rpaths: ["/opt/qt/lib"] + dummy.cFlags: [zort] +} 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 00000000..52dc82d2 --- /dev/null +++ b/src/lib/corelib/language/testdata/modules/dummyqt/gui/dummygui.qbs @@ -0,0 +1,12 @@ +import qbs 1.0 + +Module { + Depends { name: "dummyqt.core" } + property string guiProperty: "guiProperty" + property string someString: "ene mene muh" + + Depends { name: "dummy" } + dummy.defines: ["QT_GUI"] + dummy.someString: someString + dummy.zort: dummyqt.core.zort +} 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 00000000..2da3af05 --- /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/gmod/gmod1/gmod1.qbs b/src/lib/corelib/language/testdata/modules/gmod/gmod1/gmod1.qbs new file mode 100644 index 00000000..d3bb92ff --- /dev/null +++ b/src/lib/corelib/language/testdata/modules/gmod/gmod1/gmod1.qbs @@ -0,0 +1,18 @@ +import qbs + +Module { + Depends { name: "gmod2" } + Depends { name: "gmod4" } + property stringList gmod1_list1: ["gmod1_list1_proto", gmod1_string] + property stringList gmod1_list2: ["gmod1_list2_proto"] + property stringList gmod1_list3: [gmod1_string] + property string gmod1_string: "gmod1_string_proto" + property string commonName: "commonName_in_gmod1" + property int depProp: gmod2.prop + property int p0: p1 + p2 + property int p1: 0 + property int p2: 0 + + gmod2.gmod2_string: gmod1_string + gmod2.gmod2_list: [gmod1_string, commonName] +} diff --git a/src/lib/corelib/language/testdata/modules/gmod2/gmod2.qbs b/src/lib/corelib/language/testdata/modules/gmod2/gmod2.qbs new file mode 100644 index 00000000..e221f50b --- /dev/null +++ b/src/lib/corelib/language/testdata/modules/gmod2/gmod2.qbs @@ -0,0 +1,8 @@ +import qbs + +Module { + property int prop: 0 + property string gmod2_string: "gmod2_string_proto" + property string commonName: "commonName_in_gmod2" + property stringList gmod2_list: ["gmod2_list_proto"] +} diff --git a/src/lib/corelib/language/testdata/modules/gmod3/qmod3.qbs b/src/lib/corelib/language/testdata/modules/gmod3/qmod3.qbs new file mode 100644 index 00000000..1c8704b5 --- /dev/null +++ b/src/lib/corelib/language/testdata/modules/gmod3/qmod3.qbs @@ -0,0 +1,7 @@ +import qbs + +Module { + Depends { name: "gmod2" } + property string gmod3_string: "gmod3_string_proto" + gmod2.gmod2_list: [gmod3_string] +} diff --git a/src/lib/corelib/language/testdata/modules/gmod4/gmod4.qbs b/src/lib/corelib/language/testdata/modules/gmod4/gmod4.qbs new file mode 100644 index 00000000..c06f4e96 --- /dev/null +++ b/src/lib/corelib/language/testdata/modules/gmod4/gmod4.qbs @@ -0,0 +1,8 @@ +import qbs + +Module { + Depends { name: "gmod2" } + Depends { name: "gmod3" } + property string gmod4_string: "gmod4_string_proto" + gmod2.gmod2_list: [gmod4_string + "_" + gmod3.gmod3_string] +} 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 00000000..ba7dbcbf --- /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 00000000..c127f0c6 --- /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 00000000..16a9875f --- /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 00000000..e69de29b diff --git a/src/lib/corelib/language/testdata/narf.zort b/src/lib/corelib/language/testdata/narf.zort new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/corelib/language/testdata/nativesettings.ini b/src/lib/corelib/language/testdata/nativesettings.ini new file mode 100644 index 00000000..4caf32e5 --- /dev/null +++ b/src/lib/corelib/language/testdata/nativesettings.ini @@ -0,0 +1 @@ +osname = Unix diff --git a/src/lib/corelib/language/testdata/non-required-products.qbs b/src/lib/corelib/language/testdata/non-required-products.qbs new file mode 100644 index 00000000..fb21e609 --- /dev/null +++ b/src/lib/corelib/language/testdata/non-required-products.qbs @@ -0,0 +1,35 @@ +import qbs + +Project { + Product { + name: "depender" + Depends { name: "dummy" } + Depends { name: "dependee"; required: false } + Properties { + condition: dependee.present + dummy.defines: ["WITH_DEPENDEE"] + } + } + Project { + name: "subproject" + Product { + name: "dependee" + } + } + + Product { + name: "p1" + condition: p2.present + Depends { name: "p2"; required: false } + } + Product { + name: "p2" + condition: p3.present + Depends { name: "p3"; required: false } + } + Product { + name: "p3" + condition: nosuchmodule.present + Depends { name: "nosuchmodule"; required: false } + } +} diff --git a/src/lib/corelib/language/testdata/outerInGroup.qbs b/src/lib/corelib/language/testdata/outerInGroup.qbs new file mode 100644 index 00000000..751392a4 --- /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 00000000..f0eeabf5 --- /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 00000000..e815cc88 --- /dev/null +++ b/src/lib/corelib/language/testdata/productconditions.qbs @@ -0,0 +1,36 @@ +import qbs 1.0 +import qbs.Probes + +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.architecture !== (qbs.architecture + "foo") + } + Product { + name: "product_probe_condition_true" + condition: trueProbe.found + Probe { + id: trueProbe + configure: { found = true; } + } + } + Product { + name: "product_probe_condition_false" + condition: falseProbe.found + Probe { + id: falseProbe + configure: { found = false; } + } + } +} diff --git a/src/lib/corelib/language/testdata/productdirectories.qbs b/src/lib/corelib/language/testdata/productdirectories.qbs new file mode 100644 index 00000000..dc431520 --- /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 00000000..156f9f1b --- /dev/null +++ b/src/lib/corelib/language/testdata/profilevaluesandoverriddenvalues.qbs @@ -0,0 +1,21 @@ +import qbs 1.0 + +Project { + Application { + name: "product1" + type: { + if (!(dummy.cFlags instanceof Array)) + throw new Error("dummy.cFlags: Array type expected."); + if (!(dummy.cxxFlags instanceof Array)) + throw new Error("dummy.cxxFlags: Array type expected."); + if (!(dummy.defines instanceof Array)) + throw new Error("dummy.defines: Array type expected."); + return "application"; + } + consoleApplication: true + 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/properties-block-in-group.qbs b/src/lib/corelib/language/testdata/properties-block-in-group.qbs new file mode 100644 index 00000000..c2bfea0a --- /dev/null +++ b/src/lib/corelib/language/testdata/properties-block-in-group.qbs @@ -0,0 +1,16 @@ +import qbs + +Product { + name: "in-group" + property bool featureEnabled: true + Depends { name: "dummy" } + dummy.defines: ["BASEDEF"] + Group { + name: "the group" + files: ["dummy.txt" ] + Properties { + condition: featureEnabled + dummy.defines: outer.concat("FEATURE_ENABLED") + } + } +} diff --git a/src/lib/corelib/language/testdata/propertiesblocks.qbs b/src/lib/corelib/language/testdata/propertiesblocks.qbs new file mode 100644 index 00000000..db1054f0 --- /dev/null +++ b/src/lib/corelib/language/testdata/propertiesblocks.qbs @@ -0,0 +1,181 @@ +import qbs 1.0 +import "propertiesblocks_base.qbs" as ProductBase + +Project { + Product { + name: "property_overwrite" + Depends { name: "dummy" } + dummy.defines: ["SOMETHING"] + Properties { + condition: true + dummy.defines: ["OVERWRITTEN"] + } + } + Product { + name: "property_overwrite_no_outer" + Depends { name: "dummy" } + Properties { + condition: true + dummy.defines: ["OVERWRITTEN"] + } + } + Product { + name: "property_append_to_outer" + Depends { name: "dummy" } + dummy.defines: ["ONE"] + Properties { + condition: true + dummy.defines: outer.concat(["TWO"]) + } + } + Product { + name: "multiple_exclusive_properties" + Depends { name: "dummy" } + dummy.defines: ["SOMETHING"] + Properties { + condition: true + dummy.defines: ["OVERWRITTEN"] + } + Properties { + condition: false + dummy.defines: ["IMPOSSIBLE"] + } + } + Product { + name: "multiple_exclusive_properties_no_outer" + Depends { name: "dummy" } + Properties { + condition: true + dummy.defines: ["OVERWRITTEN"] + } + Properties { + condition: false + dummy.defines: ["IMPOSSIBLE"] + } + } + Product { + name: "multiple_exclusive_properties_append_to_outer" + Depends { name: "dummy" } + dummy.defines: ["ONE"] + Properties { + condition: true + dummy.defines: outer.concat(["TWO"]) + } + Properties { + condition: false + dummy.defines: ["IMPOSSIBLE"] + } + } + Product { + name: "ambiguous_properties" + Depends { name: "dummy" } + dummy.defines: ["ONE"] + Properties { + condition: true + dummy.defines: outer.concat(["TWO"]) + } + Properties { + condition: false + dummy.defines: outer.concat(["IMPOSSIBLE"]) + } + Properties { // will be ignored + condition: true + dummy.defines: outer.concat(["THREE"]) + } + } + Product { + name: "condition_refers_to_product_property" + property bool narf: true + property string someString: "SOMETHING" + Depends { name: "dummy" } + Properties { + condition: narf + dummy.defines: ["OVERWRITTEN"] + someString: "OVERWRITTEN" + } + } + property bool zort: true + Product { + name: "condition_refers_to_project_property" + property string someString: "SOMETHING" + Depends { name: "dummy" } + Properties { + condition: project.zort + dummy.defines: ["OVERWRITTEN"] + someString: "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_retain_base4" + Properties { + condition: false + dummy.defines: ["NEVERMORE"] + } + // no "else case" for dummy.defines. The value is derived from ProductBase. + } + 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 { name: "dummy" } + Properties { + condition: knolf.name === "gnampf" + dummy.defines: ["OVERWRITTEN"] + } + } + Product { + name: "using_derived_Properties_item" + Depends { name: "dummy" } + MyProperties { + condition: true + dummy.defines: ["string from MyProperties"] + } + } + Product { + name: "conditional-depends" + Depends { + name: "dummy" + condition: false + } + Properties { + condition: false + dummy.defines: ["a string"] + } + } +} 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 00000000..71b09a3d --- /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/qbs-properties-in-project-condition.qbs b/src/lib/corelib/language/testdata/qbs-properties-in-project-condition.qbs new file mode 100644 index 00000000..d3aa2d2a --- /dev/null +++ b/src/lib/corelib/language/testdata/qbs-properties-in-project-condition.qbs @@ -0,0 +1,9 @@ +import qbs + +Project { + condition: qbs.targetOS.contains("whatever") + + Product { + name: "never reached" + } +} diff --git a/src/lib/corelib/language/testdata/recursive-dependencies/recursive-dependencies.qbs b/src/lib/corelib/language/testdata/recursive-dependencies/recursive-dependencies.qbs new file mode 100644 index 00000000..34d7089d --- /dev/null +++ b/src/lib/corelib/language/testdata/recursive-dependencies/recursive-dependencies.qbs @@ -0,0 +1,19 @@ +import qbs + +Project { + Product { + name: "p1" + Depends { name: "p3" } + } + Product { + name: "p2" + Depends { name: "p3" } + } + Product { + name: "p3" + Export { + Depends { name: "p4" } + } + } + Product { name: "p4" } +} diff --git a/src/lib/corelib/language/testdata/relaxed-error-mode/file1.txt b/src/lib/corelib/language/testdata/relaxed-error-mode/file1.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/corelib/language/testdata/relaxed-error-mode/file2.txt b/src/lib/corelib/language/testdata/relaxed-error-mode/file2.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/corelib/language/testdata/relaxed-error-mode/relaxed-error-mode.qbs b/src/lib/corelib/language/testdata/relaxed-error-mode/relaxed-error-mode.qbs new file mode 100644 index 00000000..fa6190ca --- /dev/null +++ b/src/lib/corelib/language/testdata/relaxed-error-mode/relaxed-error-mode.qbs @@ -0,0 +1,31 @@ +import qbs + +Project { + Product { + name: "recursive depender" + Depends { name: "depender required" } + files: "file1.txt" + } + Product { + name: "broken" + Depends { name: "nosuchmodule" } + } + Product { + name: "depender required" + Depends { name: "broken" } + files: "file1.txt" + } + Product { + name: "depender nonrequired" + Depends { name: "broken"; required: false } + files: "file1.txt" + } + Product { + name: "missing file" + files: ["file1.txt", "file3.txt", "file2.txt"] + } + Product { + name: "fine" + files: "file2.txt" + } +} diff --git a/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/complicated.qbs b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/complicated.qbs new file mode 100644 index 00000000..2173948f --- /dev/null +++ b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/complicated.qbs @@ -0,0 +1,7 @@ +import qbs + +Product { + Depends { name: "failing-validation"; required: false } + Depends { name: "failing-validation-indirect" } + Depends { name: "failing-validation-indirect"; required: false } +} diff --git a/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/dependency-via-export.qbs b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/dependency-via-export.qbs new file mode 100644 index 00000000..a4de65cb --- /dev/null +++ b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/dependency-via-export.qbs @@ -0,0 +1,15 @@ +import qbs + +Project { + Product { + name: "dep" + Export { + Depends { name: "failing-validation" } + } + } + + Product { + Depends { name: "failing-validation"; required: false } + Depends { name: "dep" } + } +} diff --git a/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/dependency-via-module.qbs b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/dependency-via-module.qbs new file mode 100644 index 00000000..74ab675e --- /dev/null +++ b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/dependency-via-module.qbs @@ -0,0 +1,6 @@ +import qbs + +Product { + Depends { name: "failing-validation"; required: false } + Depends { name: "failing-validation-indirect" } +} diff --git a/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/direct-dependencies.qbs b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/direct-dependencies.qbs new file mode 100644 index 00000000..cf2f0f24 --- /dev/null +++ b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/direct-dependencies.qbs @@ -0,0 +1,6 @@ +import qbs + +Product { + Depends { name: "failing-validation"; required: false } + Depends { name: "failing-validation" } +} diff --git a/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/modules/failing-validation-indirect/failing-validation-indirect.qbs b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/modules/failing-validation-indirect/failing-validation-indirect.qbs new file mode 100644 index 00000000..45613b58 --- /dev/null +++ b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/modules/failing-validation-indirect/failing-validation-indirect.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + Depends { name: "failing-validation" } +} diff --git a/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/modules/failing-validation/failing-validation.qbs b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/modules/failing-validation/failing-validation.qbs new file mode 100644 index 00000000..dcc650b9 --- /dev/null +++ b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/modules/failing-validation/failing-validation.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + validate: { throw "validation error!"; } +} diff --git a/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-export-indirect.qbs b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-export-indirect.qbs new file mode 100644 index 00000000..293f737c --- /dev/null +++ b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-export-indirect.qbs @@ -0,0 +1,22 @@ +import qbs + +Project { + Product { + name: "dep2" + Export { + Depends { name: "dep1" } + } + } + + Product { + name: "dep1" + Export { + Depends { name: "failing-validation-indirect" } + } + } + + Product { + Depends { name: "failing-validation"; required: false } + Depends { name: "dep2"; required: false } + } +} diff --git a/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-export.qbs b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-export.qbs new file mode 100644 index 00000000..6f2ec556 --- /dev/null +++ b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-export.qbs @@ -0,0 +1,15 @@ +import qbs + +Project { + Product { + name: "dep" + Export { + Depends { name: "failing-validation" } + } + } + + Product { + Depends { name: "failing-validation"; required: false } + Depends { name: "dep"; required: false } + } +} diff --git a/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-module.qbs b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-module.qbs new file mode 100644 index 00000000..ecc800e7 --- /dev/null +++ b/src/lib/corelib/language/testdata/required-and-nonrequired-dependencies/required-chain-module.qbs @@ -0,0 +1,6 @@ +import qbs + +Product { + Depends { name: "failing-validation"; required: false } + Depends { name: "failing-validation-indirect"; required: false } +} diff --git a/src/lib/corelib/language/testdata/rfc1034identifier.qbs b/src/lib/corelib/language/testdata/rfc1034identifier.qbs new file mode 100644 index 00000000..eb73dc4d --- /dev/null +++ b/src/lib/corelib/language/testdata/rfc1034identifier.qbs @@ -0,0 +1,7 @@ +import qbs +import qbs.Utilities + +CppApplication { + name: Utilities.rfc1034Identifier("this!has@special#characters$uh-oh,Undersc0r3s_Are.Bad") + bundle.infoPlist: { return {"CFBundleIdentifier": "$(PRODUCT_NAME:rfc1034identifier)"}; } +} diff --git a/src/lib/corelib/language/testdata/subdir/exports-mylib.qbs b/src/lib/corelib/language/testdata/subdir/exports-mylib.qbs new file mode 100644 index 00000000..e3802de5 --- /dev/null +++ b/src/lib/corelib/language/testdata/subdir/exports-mylib.qbs @@ -0,0 +1,14 @@ +import qbs + +StaticLibrary { + name: "mylib" + Depends { name: "dummy" } + dummy.defines: ["BUILD_" + product.name.toUpperCase()] + property string definePrefix: "USE_" + Export { + Depends { name: "dummy" } + Depends { name: "mylib2" } + dummy.defines: [product.definePrefix + product.name.toUpperCase()] + dummy.includePaths: ["./lib"] + } +} 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 00000000..62427169 --- /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/subdir2/exports-mylib2.qbs b/src/lib/corelib/language/testdata/subdir2/exports-mylib2.qbs new file mode 100644 index 00000000..a0cec6eb --- /dev/null +++ b/src/lib/corelib/language/testdata/subdir2/exports-mylib2.qbs @@ -0,0 +1,13 @@ +import qbs + +StaticLibrary { + name: "mylib2" + Depends { name: "dummy" } + dummy.defines: ["BUILD_" + product.name.toUpperCase()] + property string definePrefix: "USE_" + Export { + Depends { name: "dummy" } + dummy.defines: [product.definePrefix + product.name.toUpperCase()] + dummy.includePaths: ["./lib"] + } +} diff --git a/src/lib/corelib/language/testdata/throwing-probe.qbs b/src/lib/corelib/language/testdata/throwing-probe.qbs new file mode 100644 index 00000000..7f4a77a5 --- /dev/null +++ b/src/lib/corelib/language/testdata/throwing-probe.qbs @@ -0,0 +1,12 @@ +import qbs + +Product { + name: "theProduct" + property bool enableProbe + Probe { + condition: enableProbe + configure: { + throw "Error!"; + } + } +} diff --git a/src/lib/corelib/language/testdata/versionCompare.qbs b/src/lib/corelib/language/testdata/versionCompare.qbs new file mode 100644 index 00000000..0a7271d1 --- /dev/null +++ b/src/lib/corelib/language/testdata/versionCompare.qbs @@ -0,0 +1,21 @@ +import qbs +import qbs.Utilities + +Product { + name: { + var e = "unexpected comparison result"; + if (Utilities.versionCompare("1.5", "1.5") !== 0) + throw e; + if (Utilities.versionCompare("1.5", "1.5.0") !== 0) + throw e; + if (Utilities.versionCompare("1.5", "1.6") >= 0) + throw e; + if (Utilities.versionCompare("1.6", "1.5") <= 0) + throw e; + if (Utilities.versionCompare("2.5", "1.6") <= 0) + throw e; + if (Utilities.versionCompare("1.6", "2.5") >= 0) + throw e; + return "versionCompare"; + } +} diff --git a/src/lib/corelib/language/testdata/zort b/src/lib/corelib/language/testdata/zort new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/corelib/language/tst_language.cpp b/src/lib/corelib/language/tst_language.cpp new file mode 100644 index 00000000..9823a8d3 --- /dev/null +++ b/src/lib/corelib/language/tst_language.cpp @@ -0,0 +1,2256 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#undef QT_NO_CAST_FROM_ASCII // I am qmake, and I approve this hack. + +#include "tst_language.h" + +#include "../../../../tests/auto/shared.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +Q_DECLARE_METATYPE(QList) + +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"); + defaultParameters.setBuildRoot("/some/build/directory"); + defaultParameters.setPropertyCheckingMode(ErrorHandlingMode::Strict); +} + +TestLanguage::~TestLanguage() +{ +} + +QHash TestLanguage::productsFromProject(ResolvedProjectPtr project) +{ + QHash result; + foreach (const ResolvedProductPtr &product, project->allProducts()) + 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('.')); + QVariantMap properties; + if (propertyNameComponents.count() > 1) { + propertyNameComponents.prepend(QLatin1String("modules")); + properties = product->moduleProperties->value(); + } else { + properties = product->productProperties; + } + return getConfigProperty(properties, 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; + } +} + +#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, EvalContext::PropertyEvaluation, this); + loader = new Loader(m_engine, m_logger); + loader->setSearchPaths(QStringList() + << QLatin1String(SRCDIR "/../../../share/qbs")); + defaultParameters.setTopLevelProfile(profileName()); + defaultParameters.setConfigurationName("default"); + defaultParameters.expandBuildConfiguration(); + 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 products = productsFromProject(project); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(product); + QVariantMap cfg = product->productProperties; + 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::baseValidation() +{ + qbs::SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("base-validate/base-validate.qbs")); + try { + project = loader->loadProject(params); + QVERIFY2(false, "exception expected"); + } catch (const qbs::ErrorInfo &e) { + QVERIFY2(e.toString().contains("Parent succeeded, child failed."), + qPrintable(e.toString())); + } +} + +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::versionCompare() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters parameters = defaultParameters; + parameters.setProjectFilePath(testProject("versionCompare.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 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::rfc1034Identifier() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("rfc1034identifier.qbs")); + project = loader->loadProject(defaultParameters); + QVERIFY(project); + QHash products = productsFromProject(project); + ResolvedProductPtr product = products.value(QLatin1String("this-has-special-characters-" + "uh-oh-Undersc0r3s-Are.Bad")); + 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 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 qbs::Internal::TestLanguage::dependencyOnAllProfiles() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("dependencyOnAllProfiles.qbs")); + Settings settings((QString())); + TemporaryProfile p1("p1", &settings); + p1.p.setValue("qbs.architecture", "arch1"); + TemporaryProfile p2("p2", &settings); + p2.p.setValue("qbs.architecture", "arch2"); + QVariantMap overriddenValues; + overriddenValues.insert("project.profile1", "p1"); + overriddenValues.insert("project.profile2", "p2"); + params.setOverriddenValues(overriddenValues); + project = loader->loadProject(params); + QVERIFY(project); + QCOMPARE(project->products.count(), 3); + const ResolvedProductConstPtr mainProduct = productsFromProject(project).value("main"); + QVERIFY(mainProduct); + QCOMPARE(mainProduct->dependencies.count(), 2); + foreach (const ResolvedProductConstPtr &p, mainProduct->dependencies) { + QCOMPARE(p->name, QLatin1String("dep")); + QVERIFY(p->profile == "p1" || p->profile == "p2"); + } + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::derivedSubProject() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("derived-sub-project/project.qbs")); + const TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(project); + const QHash products = productsFromProject(project); + QCOMPARE(products.count(), 1); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::defaultValue() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("defaultvalue/egon.qbs")); + QFETCH(QString, prop1Value); + QVariantMap overridden; + if (!prop1Value.isEmpty()) + overridden.insert("lower.prop1", prop1Value); + params.setOverriddenValues(overridden); + TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(project); + QHash products = productsFromProject(project); + QCOMPARE(products.count(), 2); + const ResolvedProductPtr product = products.value("egon"); + QVERIFY(product); + QStringList propertyName = QStringList() << "modules" << "lower" << "prop2"; + QVariant propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QFETCH(QVariant, expectedProp2Value); + QCOMPARE(propertyValue, expectedProp2Value); + propertyName = QStringList() << "modules" << "lower" << "listProp"; + propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QFETCH(QVariant, expectedListPropValue); + QCOMPARE(propertyValue.toStringList(), expectedListPropValue.toStringList()); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::defaultValue_data() +{ + QTest::addColumn("prop1Value"); + QTest::addColumn("expectedProp2Value"); + QTest::addColumn("expectedListPropValue"); + QTest::newRow("controlling property with random value") << "random" << QVariant("withoutBlubb") + << QVariant(QStringList({"other"})); + QTest::newRow("controlling property with blubb value") << "blubb" << QVariant("withBlubb") + << QVariant(QStringList({"blubb", "other"})); + QTest::newRow("controlling property with egon value") << "egon" << QVariant("withEgon") + << QVariant(QStringList({"egon", "other"})); + QTest::newRow("controlling property not overwritten") << "" << QVariant("withBlubb") + << QVariant(QStringList({"blubb", "other"})); +} + +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 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("errorMessage"); + QTest::newRow("unknown_module") + << "Dependency 'neitherModuleNorProduct' not found"; + 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("dependency_cycle") + << "Cyclic dependencies detected."; + QTest::newRow("dependency_cycle2") + << "Cyclic dependencies detected."; + QTest::newRow("dependency_cycle3") + << "Cyclic dependencies detected."; + QTest::newRow("dependency_cycle4") + << "Cyclic dependencies detected."; + 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") + << "Element at index 1 of list property 'files' does not have string type."; + QTest::newRow("undefined_stringlist_element") + << "Element at index 1 of list property 'files' is undefined. String expected."; + QTest::newRow("undeclared_item") + << "Item 'cpp' is not declared."; + QTest::newRow("undeclared_property_wrapper") + << "Property 'doesntexist' is not declared."; + QTest::newRow("undeclared_property_in_export_item") + << "Property 'blubb' is not declared."; + QTest::newRow("undeclared_property_in_export_item2") + << "Item 'something' is not declared."; + QTest::newRow("undeclared_property_in_export_item3") + << "Property 'blubb' 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'."; + QTest::newRow("conflicting_fileTagsFilter") + << "Conflicting fileTagsFilter in Group items"; + QTest::newRow("duplicate_sources") + << "Duplicate source file '.*main.cpp'" + ".*duplicate_sources.qbs:4:12.*duplicate_sources.qbs:6:16."; + QTest::newRow("duplicate_sources_wildcards") + << "Duplicate source file '.*duplicate_sources_wildcards.qbs'" + ".*duplicate_sources_wildcards.qbs:4:12" + ".*duplicate_sources_wildcards.qbs:6:16."; + QTest::newRow("oldQbsVersion") + << "The project requires at least qbs version \\d+\\.\\d+.\\d+, " + "but this is qbs version " QBS_VERSION "."; + QTest::newRow("wrongQbsVersionFormat") + << "The value '.*' of Project.minimumQbsVersion is not a valid version string."; + QTest::newRow("properties-item-with-invalid-condition") + << "TypeError: Result of expression 'cpp.nonexistingproperty'"; + QTest::newRow("misused-inherited-property") << "Binding to non-item property"; + QTest::newRow("undeclared_property_in_Properties_item") << "Item 'blubb' is not declared"; + QTest::newRow("same-module-prefix1") << "The name of module 'prefix1' is equal to the first " + "component of the name of module 'prefix1.suffix'"; + QTest::newRow("same-module-prefix2") << "The name of module 'prefix2' is equal to the first " + "component of the name of module 'prefix2.suffix'"; + QTest::newRow("conflicting-properties-in-export-items") + << "Export item in inherited item redeclares property 'theProp' with different type."; + QTest::newRow("invalid-property-option") + << "PropertyOptions item refers to non-existing property 's0meProp'"; +} + +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(QRegExp(errorMessage))) { + qDebug() << "Message: " << e.toString(); + qDebug() << "Expected: " << errorMessage; + QFAIL("Unexpected error message."); + } + return; + } + QEXPECT_FAIL("undeclared_property_in_Properties_item", "Too expensive to check", Continue); + QVERIFY(!"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 products = productsFromProject(project); + QCOMPARE(products.count(), 17); + ResolvedProductPtr product; + product = products.value("myapp"); + QVERIFY(product); + QStringList propertyName = QStringList() << "modules" << "dummy" << "defines"; + QVariant propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYAPP" << "USE_MYLIB" + << "USE_MYLIB2"); + propertyName = QStringList() << "modules" << "dummy" << "includePaths"; + QVariantList propertyValues = getConfigProperty(product->moduleProperties->value(), + propertyName).toList(); + QCOMPARE(propertyValues.count(), 3); + QVERIFY(propertyValues.at(0).toString().endsWith("/app")); + QVERIFY(propertyValues.at(1).toString().endsWith("/subdir/lib")); + QVERIFY(propertyValues.at(2).toString().endsWith("/subdir2/lib")); + + QCOMPARE(PropertyFinder().propertyValue(product->moduleProperties->value(), "dummy", + "productName").toString(), QString("myapp")); + + product = products.value("mylib"); + QVERIFY(product); + propertyName = QStringList() << "modules" << "dummy" << "defines"; + propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYLIB"); + + product = products.value("mylib2"); + QVERIFY(product); + propertyName = QStringList() << "modules" << "dummy" << "defines"; + propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "BUILD_MYLIB2"); + + 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" << "dummy" << "cFlags"; + propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() + << "BASE_PRODUCTWITHINHERITEDEXPORTITEM" + << "PRODUCT_PRODUCTWITHINHERITEDEXPORTITEM"); + propertyName = QStringList() << "modules" << "dummy" << "cxxFlags"; + propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "-bar"); + propertyName = QStringList() << "modules" << "dummy" << "defines"; + propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QCOMPARE(propertyValue.toStringList(), QStringList() << "ABC"); + QCOMPARE(PropertyFinder().propertyValue(product->moduleProperties->value(), "dummy", + "productName").toString(), QString("myapp2")); + QCOMPARE(PropertyFinder().propertyValue(product->moduleProperties->value(), "dummy", + "upperCaseProductName").toString(), QString("MYAPP2")); + + // Check whether we're returning incorrect cached values. + product = products.value("myapp3"); + QVERIFY(product); + QCOMPARE(PropertyFinder().propertyValue(product->moduleProperties->value(), "dummy", + "productName").toString(), QString("myapp3")); + QCOMPARE(PropertyFinder().propertyValue(product->moduleProperties->value(), "dummy", + "upperCaseProductName").toString(), QString("MYAPP3")); + + // Verify we refer to the right "project" variable. + product = products.value("sub p2"); + QVERIFY(product); + QCOMPARE(PropertyFinder().propertyValue(product->moduleProperties->value(), "dummy", + "someString").toString(), QString("sub1")); + + product = products.value("libE"); + QVERIFY(product); + propertyName = QStringList() << "modules" << "dummy" << "defines"; + propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QCOMPARE(propertyValue.toStringList(), + QStringList() << "LIBA" << "LIBB" << "LIBC" << "LIBD"); + propertyName = QStringList() << "modules" << "dummy" << "productName"; + propertyValue = getConfigProperty(product->moduleProperties->value(), propertyName); + QCOMPARE(propertyValue.toString(), QString("libE")); + } + 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 products = productsFromProject(project); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(product); + QVariantMap cfg = product->productProperties; + 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::isMacosHost()) + expectedProductName = QLatin1String("Mac OS X"); + else if (HostOsInfo::isWindowsHost()) + expectedProductName = QLatin1String("Windows"); + else + expectedProductName = QLatin1String("Unix"); + + QVERIFY(project); + QHash 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("groupCount"); + QTest::addColumn >("groupEnabled"); + QTest::newRow("init") << 0 << QList(); + QTest::newRow("no_condition_no_group") + << 1 << (QList() << true); + QTest::newRow("no_condition") + << 2 << (QList() << true << true); + QTest::newRow("true_condition") + << 2 << (QList() << true << true); + QTest::newRow("false_condition") + << 2 << (QList() << true << false); + QTest::newRow("true_condition_from_product") + << 2 << (QList() << true << true); + QTest::newRow("true_condition_from_project") + << 2 << (QList() << true << true); + QTest::newRow("condition_accessing_module_property") + << 2 << (QList() << true << false); + QTest::newRow("cleanup") << 0 << QList(); +} + +void TestLanguage::groupConditions() +{ + HANDLE_INIT_CLEANUP_DATATAGS("groupconditions.qbs"); + QFETCH(int, groupCount); + QFETCH(QList, groupEnabled); + QCOMPARE(groupCount, groupEnabled.count()); + const QHash 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 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 products = productsFromProject(project); + QCOMPARE(products.count(), 1); + + ResolvedProductPtr product = products.value("home"); + QVERIFY(product); + + QDir dir = QDir::home(); + QCOMPARE(product->productProperties.value("home").toString(), dir.canonicalPath()); + QCOMPARE(product->productProperties.value("homeSlash").toString(), + dir.canonicalPath()); + + dir.cdUp(); + QCOMPARE(product->productProperties.value("homeUp").toString(), + dir.canonicalPath()); + + dir = QDir::home(); + QCOMPARE(product->productProperties.value("homeFile").toString(), + dir.filePath("a")); + + QCOMPARE(product->productProperties.value("bogus1").toString(), + FileInfo::resolvePath(product->sourceDirectory, QLatin1String("a~b"))); + QCOMPARE(product->productProperties.value("bogus2").toString(), + FileInfo::resolvePath(product->sourceDirectory, QLatin1String("a/~/bb"))); + QCOMPARE(product->productProperties.value("user").toString(), + FileInfo::resolvePath(product->sourceDirectory, QLatin1String("~foo/bar"))); + } + catch (const ErrorInfo &e) { + qDebug() << e.toString(); + } +} + +void TestLanguage::identifierSearch_data() +{ + QTest::addColumn("expectedHasNarf"); + QTest::addColumn("expectedHasZort"); + QTest::addColumn("sourceCode"); + QTest::newRow("no narf, no zort") << false << false << QString( + "Product {\n" + " name: {\n" + " var foo = 'bar';\n" + " console.info(foo);\n" + " return foo;\n" + " }\n" + "}\n"); + QTest::newRow("narf, no zort") << true << false << QString( + "Product {\n" + " name: {\n" + " var foo = 'zort';\n" + " console.info(narf + foo);\n" + " return foo;\n" + " }\n" + "}\n"); + QTest::newRow("no narf, zort") << false << true << QString( + "Product {\n" + " name: {\n" + " var foo = 'narf';\n" + " console.info(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 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::idUniqueness() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("id-uniqueness.qbs")); + loader->loadProject(defaultParameters); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + const QList items = e.items(); + QCOMPARE(items.count(), 3); + QCOMPARE(items.at(0).toString(), QString::fromUtf8("The id 'baseProduct' is not unique.")); + QVERIFY(items.at(1).toString().contains("id-uniqueness.qbs:6:5 First occurrence is here.")); + QVERIFY(items.at(2).toString().contains("id-uniqueness.qbs:9:5 Next occurrence is here.")); + } + QVERIFY(exceptionCaught); +} + +void TestLanguage::importCollection() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("import-collection/project.qbs")); + const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(project); + QHash products = productsFromProject(project); + const ResolvedProductConstPtr product = products.value("da product"); + QCOMPARE(product->productProperties.value("targetName").toString(), + QLatin1String("C1f1C1f2C2f1C2f2")); + } + 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 products = productsFromProject(project); + QCOMPARE(products.count(), 2); + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QVERIFY(!exceptionCaught); +} + +class JSSourceValueCreator +{ + FileContextPtr m_fileContext; + QList m_strings; +public: + JSSourceValueCreator(const FileContextPtr &fileContext) + : m_fileContext(fileContext) + { + } + + ~JSSourceValueCreator() + { + qDeleteAll(m_strings); + } + + JSSourceValuePtr create(const QString &sourceCode) + { + JSSourceValuePtr value = JSSourceValue::create(); + value->setFile(m_fileContext); + QString *str = new QString(sourceCode); + m_strings += str; + value->setSourceCode(QStringRef(str)); + 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(m_engine->lastErrorString(evaluated))); + } +} + +void TestLanguage::jsImportUsedInMultipleScopes_data() +{ + QTest::addColumn("configurationName"); + QTest::addColumn("expectedProductName"); + QTest::newRow("debug") << QString("debug") << QString("MyProduct_debug"); + QTest::newRow("release") << QString("release") << QString("MyProduct"); +} + +void TestLanguage::jsImportUsedInMultipleScopes() +{ + QFETCH(QString, configurationName); + QFETCH(QString, expectedProductName); + + bool exceptionCaught = false; + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("jsimportsinmultiplescopes.qbs")); + params.setConfigurationName(configurationName); + params.expandBuildConfiguration(); + TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(project); + QHash 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("propertyName"); + QTest::addColumn("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("merge_lists_with_prototype_values") + << "rpaths" + << (QStringList() << "/opt/qt/lib" << "$ORIGIN"); + QTest::newRow("list_property_that_references_product") + << "listProp" + << (QStringList() << "x" << "123"); + QTest::newRow("list_property_depending_on_overridden_property") + << "listProp2" + << (QStringList() << "PRODUCT_STUFF" << "DEFAULT_STUFF" << "EXTRA_STUFF"); + QTest::newRow("overridden_list_property") << "listProp" << (QStringList() << "PRODUCT_STUFF"); + QTest::newRow("cleanup") << QString() << QStringList(); +} + +void TestLanguage::moduleProperties() +{ + HANDLE_INIT_CLEANUP_DATATAGS("moduleproperties.qbs"); + QFETCH(QString, propertyName); + QFETCH(QStringList, expectedValues); + QHash products = productsFromProject(project); + const QString productName = QString::fromLocal8Bit(QTest::currentDataTag()); + ResolvedProductPtr product = products.value(productName); + QVERIFY(product); + QVariant values = PropertyFinder().propertyValue(product->moduleProperties->value(), + "dummy", propertyName); + QStringList valueStrings; + foreach (const QVariant &v, values.toList()) + valueStrings += v.toString(); + QCOMPARE(valueStrings, expectedValues); +} + +void TestLanguage::modulePropertiesInGroups() +{ + defaultParameters.setProjectFilePath(testProject("modulepropertiesingroups.qbs")); + bool exceptionCaught = false; + try { + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(project); + const QHash products = productsFromProject(project); + const ResolvedProductPtr product = products.value("grouptest"); + QVERIFY(product); + GroupConstPtr g1; + GroupConstPtr g11; + GroupConstPtr g12; + GroupConstPtr g2; + GroupConstPtr g21; + GroupConstPtr g211; + foreach (const GroupConstPtr &g, product->groups) { + if (g->name == "g1") + g1= g; + else if (g->name == "g2") + g2 = g; + else if (g->name == "g1.1") + g11 = g; + else if (g->name == "g1.2") + g12 = g; + else if (g->name == "g2.1") + g21 = g; + else if (g->name == "g2.1.1") + g211 = g; + } + QVERIFY(g1); + QVERIFY(g2); + QVERIFY(g11); + QVERIFY(g12); + QVERIFY(g21); + QVERIFY(g211); + + const QVariantMap productProps = product->moduleProperties->value(); + PropertyFinder pf; + + const auto &productGmod1List1 = pf.propertyValue(productProps, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(productGmod1List1, QStringList() << "gmod1_list1_proto" << "gmod1_string_proto"); + const auto &productGmod1List2 = pf.propertyValue(productProps, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(productGmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" + << "gmod1_list2_proto"); + const auto &productGmod1List3 = pf.propertyValue(productProps, "gmod.gmod1", "gmod1_list3") + .toStringList(); + QCOMPARE(productGmod1List3, QStringList() << "product" << "gmod1_string_proto"); + const int productP0 = pf.propertyValue(productProps, "gmod.gmod1", "p0").toInt(); + QCOMPARE(productP0, 1); + const int productDepProp = pf.propertyValue(productProps, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(productDepProp, 0); + const auto &productGmod2String = pf.propertyValue(productProps, "gmod2", "gmod2_string") + .toString(); + QCOMPARE(productGmod2String, QString("gmod1_string_proto")); + const auto &productGmod2List = pf.propertyValue(productProps, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(productGmod2List, QStringList() << "gmod1_string_proto" << "commonName_in_gmod1" + << "gmod4_string_proto_gmod3_string_proto" << "gmod3_string_proto" + << "gmod2_list_proto"); + + const QVariantMap g1Props = g1->properties->value(); + const auto &g1Gmod1List1 = pf.propertyValue(g1Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g1Gmod1List1, QStringList() << "gmod1_list1_proto" << "g1"); + const auto &g1Gmod1List2 = pf.propertyValue(g1Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(g1Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" + << "gmod1_list2_proto" << "g1"); + const auto &g1Gmod1List3 = pf.propertyValue(g1Props, "gmod.gmod1", "gmod1_list3") + .toStringList(); + QCOMPARE(g1Gmod1List3, QStringList() << "product" << "g1"); + const int g1P0 = pf.propertyValue(g1Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g1P0, 3); + const int g1DepProp = pf.propertyValue(g1Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g1DepProp, 1); + const auto &g1Gmod2String = pf.propertyValue(g1Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g1Gmod2String, QString("g1")); + const auto &g1Gmod2List = pf.propertyValue(g1Props, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(g1Gmod2List, QStringList() << "g1" << "commonName_in_gmod1" << "g1_gmod4_g1_gmod3" + << "g1_gmod3" << "gmod2_list_proto"); + + const QVariantMap g11Props = g11->properties->value(); + const auto &g11Gmod1List1 = pf.propertyValue(g11Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g11Gmod1List1, QStringList() << "gmod1_list1_proto" << "g1.1"); + const auto &g11Gmod1List2 = pf.propertyValue(g11Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(g11Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" + << "gmod1_list2_proto" << "g1" << "g1.1"); + const auto &g11Gmod1List3 = pf.propertyValue(g11Props, "gmod.gmod1", "gmod1_list3") + .toStringList(); + QCOMPARE(g11Gmod1List3, QStringList() << "product" << "g1.1"); + const int g11P0 = pf.propertyValue(g11Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g11P0, 5); + const int g11DepProp = pf.propertyValue(g11Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g11DepProp, 2); + const auto &g11Gmod2String = pf.propertyValue(g11Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g11Gmod2String, QString("g1.1")); + const auto &g11Gmod2List = pf.propertyValue(g11Props, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(g11Gmod2List, QStringList() << "g1.1" << "commonName_in_gmod1" + << "g1.1_gmod4_g1.1_gmod3" << "g1.1_gmod3" << "gmod2_list_proto"); + + const QVariantMap g12Props = g12->properties->value(); + const auto &g12Gmod1List1 = pf.propertyValue(g12Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g12Gmod1List1, QStringList() << "gmod1_list1_proto" << "g1.2"); + const auto &g12Gmod1List2 = pf.propertyValue(g12Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(g12Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" + << "gmod1_list2_proto" << "g1" << "g1.2"); + const auto &g12Gmod1List3 = pf.propertyValue(g12Props, "gmod.gmod1", "gmod1_list3") + .toStringList(); + QCOMPARE(g12Gmod1List3, QStringList() << "product" << "g1.2"); + const int g12P0 = pf.propertyValue(g12Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g12P0, 9); + const int g12DepProp = pf.propertyValue(g12Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g12DepProp, 1); + const auto &g12Gmod2String = pf.propertyValue(g12Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g12Gmod2String, QString("g1.2")); + const auto &g12Gmod2List = pf.propertyValue(g12Props, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(g12Gmod2List, QStringList() << "g1.2" << "commonName_in_gmod1" + << "g1_gmod4_g1.2_gmod3" << "g1.2_gmod3" << "gmod2_list_proto"); + + const QVariantMap g2Props = g2->properties->value(); + const auto &g2Gmod1List1 = pf.propertyValue(g2Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g2Gmod1List1, QStringList() << "gmod1_list1_proto" << "g2"); + const auto &g2Gmod1List2 = pf.propertyValue(g2Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(g2Gmod1List2, QStringList() << "grouptest" << "g2" << "gmod1_list2_proto"); + const int g2P0 = pf.propertyValue(g2Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g2P0, 6); + const int g2DepProp = pf.propertyValue(g2Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g2DepProp, 2); + const auto &g2Gmod2String = pf.propertyValue(g2Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g2Gmod2String, QString("g2")); + const auto &g2Gmod2List = pf.propertyValue(g2Props, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(g2Gmod2List, QStringList() << "g2" << "commonName_in_gmod1" << "g2_gmod4_g2_gmod3" + << "g2_gmod3" << "gmod2_list_proto"); + + const QVariantMap g21Props = g21->properties->value(); + const auto &g21Gmod1List1 = pf.propertyValue(g21Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g21Gmod1List1, QStringList() << "gmod1_list1_proto" << "g2"); + const auto &g21Gmod1List2 = pf.propertyValue(g21Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QEXPECT_FAIL(0, "no re-eval when no module props set", Continue); + QCOMPARE(g21Gmod1List2, QStringList() << "grouptest" << "g2.1" << "gmod1_list2_proto"); + const int g21P0 = pf.propertyValue(g21Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g21P0, 6); + const int g21DepProp = pf.propertyValue(g21Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g21DepProp, 2); + const auto &g21Gmod2String = pf.propertyValue(g21Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g21Gmod2String, QString("g2")); + const auto &g21Gmod2List = pf.propertyValue(g21Props, "gmod2", "gmod2_list") + .toStringList(); + QEXPECT_FAIL(0, "no re-eval when no module props set", Continue); + QCOMPARE(g21Gmod2List, QStringList() << "g2" << "commonName_in_gmod1" + << "g2.1_gmod4_g2.1_gmod3" << "g2.1_gmod3" << "gmod2_list_proto"); + + const QVariantMap g211Props = g211->properties->value(); + const auto &g211Gmod1List1 = pf.propertyValue(g211Props, "gmod.gmod1", "gmod1_list1") + .toStringList(); + QCOMPARE(g211Gmod1List1, QStringList() << "gmod1_list1_proto" << "g2"); + const auto &g211Gmod1List2 = pf.propertyValue(g211Props, "gmod.gmod1", "gmod1_list2") + .toStringList(); + QCOMPARE(g211Gmod1List2, QStringList() << "g2.1.1"); + const int g211P0 = pf.propertyValue(g211Props, "gmod.gmod1", "p0").toInt(); + QCOMPARE(g211P0, 17); + const int g211DepProp = pf.propertyValue(g211Props, "gmod.gmod1", "depProp").toInt(); + QCOMPARE(g211DepProp, 2); + const auto &g211Gmod2String + = pf.propertyValue(g211Props, "gmod2", "gmod2_string").toString(); + QCOMPARE(g211Gmod2String, QString("g2.1.1")); + const auto &g211Gmod2List = pf.propertyValue(g211Props, "gmod2", "gmod2_list") + .toStringList(); + QCOMPARE(g211Gmod2List, QStringList() << "g2.1.1" << "commonName_in_gmod1" + << "g2.1.1_gmod4_g2.1.1_gmod3" << "g2.1.1_gmod3" << "gmod2_list_proto"); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +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 products = productsFromProject(project); + QCOMPARE(products.count(), 1); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(product); + IntPropertyFinder ipf(product->moduleProperties->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("expectedModulesInProduct"); + QTest::addColumn("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("deep_module_name") + << (QStringList() << "qbs" << "deepdummy.deep.moat" << "dummy") + << QString("abysmal"); + QTest::newRow("deep_module_name_submodule_syntax1") + << (QStringList() << "qbs" << "deepdummy.deep.moat" << "dummy") + << QString("abysmal"); + QTest::newRow("deep_module_name_submodule_syntax2") + << (QStringList() << "qbs" << "deepdummy.deep.moat" << "dummy") + << QString("abysmal"); + 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 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->productProperties.value("foo").toString(), expectedProductProperty); +} + +void TestLanguage::nonRequiredProducts() +{ + bool exceptionCaught = false; + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("non-required-products.qbs")); + QFETCH(bool, subProjectEnabled); + QFETCH(bool, dependeeEnabled); + QVariantMap overriddenValues; + if (!subProjectEnabled) + overriddenValues.insert("subproject.condition", false); + else if (!dependeeEnabled) + overriddenValues.insert("dependee.condition", false); + params.setOverriddenValues(overriddenValues); + const TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(project); + const auto products = productsFromProject(project); + QCOMPARE(products.count(), 4 + !!subProjectEnabled); + const ResolvedProductConstPtr dependee = products.value("dependee"); + QCOMPARE(subProjectEnabled, !dependee.isNull()); + if (dependee) + QCOMPARE(dependeeEnabled, dependee->enabled); + const ResolvedProductConstPtr depender = products.value("depender"); + QVERIFY(depender); + const QStringList defines = PropertyFinder() + .propertyValue(depender->moduleProperties->value(), "dummy", "defines") + .toStringList(); + QCOMPARE(subProjectEnabled && dependeeEnabled, defines.contains("WITH_DEPENDEE")); + + for (const auto &name : QVector({ "p3", "p2", "p1"})) { + const ResolvedProductConstPtr &product = products.value(name); + QVERIFY2(product, name); + QVERIFY2(!product->enabled, name); + } + } + catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::nonRequiredProducts_data() +{ + QTest::addColumn("subProjectEnabled"); + QTest::addColumn("dependeeEnabled"); + QTest::newRow("dependee enabled") << true << true; + QTest::newRow("dependee disabled") << true << false; + QTest::newRow("sub project disabled") << false << true; +} + +void TestLanguage::outerInGroup() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("outerInGroup.qbs")); + TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(project); + QHash 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 products = productsFromProject(project); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(product); + QString projectFileDir = QFileInfo(defaultParameters.projectFilePath()).absolutePath(); + const QVariantMap productProps = product->productProperties; + const QVariantMap moduleProps = product->moduleProperties->value(); + QCOMPARE(productProps.value("projectFileDir").toString(), projectFileDir); + QStringList filesInProjectFileDir = QStringList() + << FileInfo::resolvePath(projectFileDir, "aboutdialog.h") + << FileInfo::resolvePath(projectFileDir, "aboutdialog.cpp"); + QCOMPARE(productProps.value("filesInProjectFileDir").toStringList(), filesInProjectFileDir); + QStringList includePaths = getConfigProperty(moduleProps, + QStringList() << "modules" << "dummy" << "includePaths").toStringList(); + QCOMPARE(includePaths, QStringList() << projectFileDir); + QCOMPARE(productProps.value("base_fileInProductDir").toString(), + FileInfo::resolvePath(projectFileDir, QLatin1String("foo"))); + QCOMPARE(productProps.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 { + Settings settings((QString())); + TemporaryProfile tp(QLatin1String("tst_lang_profile"), &settings); + Profile profile = tp.p; + profile.setValue("dummy.defines", "IN_PROFILE"); + profile.setValue("dummy.cFlags", "IN_PROFILE"); + profile.setValue("dummy.cxxFlags", "IN_PROFILE"); + profile.setValue("qbs.architecture", "x86"); + SetupProjectParameters parameters = defaultParameters; + parameters.setTopLevelProfile(profile.name()); + QVariantMap overriddenValues; + overriddenValues.insert("dummy.cFlags", "OVERRIDDEN"); + parameters.setOverriddenValues(overriddenValues); + parameters.setProjectFilePath(testProject("profilevaluesandoverriddenvalues.qbs")); + parameters.expandBuildConfiguration(); + project = loader->loadProject(parameters); + QVERIFY(project); + QHash products = productsFromProject(project); + ResolvedProductPtr product = products.value("product1"); + QVERIFY(product); + PropertyFinder pf; + QVariantList values; + values = pf.propertyValue(product->moduleProperties->value(), "dummy", "cxxFlags").toList(); + QCOMPARE(values.length(), 1); + QCOMPARE(values.first().toString(), QString("IN_PROFILE")); + values = pf.propertyValue(product->moduleProperties->value(), "dummy", "defines").toList(); + QCOMPARE(values, QVariantList() << QLatin1String("IN_FILE") << QLatin1String("IN_PROFILE")); + values = pf.propertyValue(product->moduleProperties->value(), "dummy", "cFlags").toList(); + 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 products = productsFromProject(project); + QCOMPARE(products.count(), 6); + 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); + + product = products.value("product_probe_condition_false"); + QVERIFY(product); + QVERIFY(!product->enabled); + + product = products.value("product_probe_condition_true"); + 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 products = productsFromProject(project); + QCOMPARE(products.count(), 1); + ResolvedProductPtr product; + product = products.value("MyApp"); + QVERIFY(product); + const QVariantMap config = product->productProperties; + QCOMPARE(config.value(QLatin1String("buildDirectory")).toString(), + product->buildDirectory()); + 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("propertyName"); + QTest::addColumn("expectedValues"); + QTest::addColumn("expectedStringValue"); + + QTest::newRow("init") << QString() << QStringList() << QString(); + QTest::newRow("property_overwrite") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString(); + QTest::newRow("property_overwrite_no_outer") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString(); + QTest::newRow("property_append_to_outer") + << QString("dummy.defines") + << (QStringList() << QString("ONE") << QString("TWO")) + << QString(); + + QTest::newRow("multiple_exclusive_properties") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString(); + QTest::newRow("multiple_exclusive_properties_no_outer") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString(); + QTest::newRow("multiple_exclusive_properties_append_to_outer") + << QString("dummy.defines") + << (QStringList() << QString("ONE") << QString("TWO")) + << QString(); + + QTest::newRow("condition_refers_to_product_property") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString("OVERWRITTEN"); + QTest::newRow("condition_refers_to_project_property") + << QString("dummy.defines") + << QStringList("OVERWRITTEN") + << QString("OVERWRITTEN"); + + QTest::newRow("ambiguous_properties") + << QString("dummy.defines") + << (QStringList() << QString("ONE") << QString("TWO")) + << QString(); + QTest::newRow("inheritance_overwrite_in_subitem") + << QString("dummy.defines") + << (QStringList() << QString("OVERWRITTEN_IN_SUBITEM")) + << QString(); + QTest::newRow("inheritance_retain_base1") + << QString("dummy.defines") + << (QStringList() << QString("BASE") << QString("SUB")) + << QString(); + QTest::newRow("inheritance_retain_base2") + << QString("dummy.defines") + << (QStringList() << QString("BASE") << QString("SUB")) + << QString(); + QTest::newRow("inheritance_retain_base3") + << QString("dummy.defines") + << (QStringList() << QString("BASE") << QString("SUB")) + << QString(); + QTest::newRow("inheritance_retain_base4") + << QString("dummy.defines") + << (QStringList() << QString("BASE")) + << QString(); + QTest::newRow("inheritance_condition_in_subitem1") + << QString("dummy.defines") + << (QStringList() << QString("SOMETHING") << QString("SUB")) + << QString(); + QTest::newRow("inheritance_condition_in_subitem2") + << QString("dummy.defines") + << (QStringList() << QString("SOMETHING")) + << QString(); + QTest::newRow("condition_references_id") + << QString("dummy.defines") + << (QStringList() << QString("OVERWRITTEN")) + << QString(); + QTest::newRow("using_derived_Properties_item") << "dummy.defines" + << (QStringList() << "string from MyProperties") << QString(); + QTest::newRow("conditional-depends") + << QString("dummy.defines") + << QStringList() + << QString(); + QTest::newRow("cleanup") << QString() << QStringList() << QString(); +} + +void TestLanguage::propertiesBlocks() +{ + HANDLE_INIT_CLEANUP_DATATAGS("propertiesblocks.qbs"); + QFETCH(QString, propertyName); + QFETCH(QStringList, expectedValues); + QFETCH(QString, expectedStringValue); + QVERIFY(project); + QHash 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); + if (!expectedStringValue.isEmpty()) { + v = productPropertyValue(product, "someString"); + QCOMPARE(v.toString(), expectedStringValue); + } +} + +void TestLanguage::propertiesBlockInGroup() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath(testProject("properties-block-in-group.qbs")); + const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(project); + QCOMPARE(project->allProducts().count(), 1); + const ResolvedProductConstPtr product = project->allProducts().first(); + const auto groupIt = std::find_if(product->groups.constBegin(), product->groups.constEnd(), + [](const GroupConstPtr &g) { return g->name == "the group"; }); + QVERIFY(groupIt != product->groups.constEnd()); + const QVariantMap propertyMap = (*groupIt)->properties->value(); + const QVariantList value + = PropertyFinder().propertyValue(propertyMap, "dummy", "defines").toList(); + QStringList stringListValue; + std::transform(value.constBegin(), value.constEnd(), std::back_inserter(stringListValue), + [](const QVariant &v) { return v.toString(); }); + QCOMPARE(stringListValue, QStringList() << "BASEDEF" << "FEATURE_ENABLED"); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::qbsPropertiesInProjectCondition() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath( + testProject("qbs-properties-in-project-condition.qbs")); + const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(project); + const QHash products = productsFromProject(project); + QCOMPARE(products.count(), 0); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::relaxedErrorMode() +{ + QFETCH(bool, strictMode); + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("relaxed-error-mode/relaxed-error-mode.qbs")); + params.setProductErrorMode(strictMode ? ErrorHandlingMode::Strict + : ErrorHandlingMode::Relaxed); + const TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(!strictMode); + const auto productMap = productsFromProject(project); + const ResolvedProductConstPtr brokenProduct = productMap.value("broken"); + QVERIFY(!brokenProduct->enabled); + QVERIFY(brokenProduct->location.isValid()); + QCOMPARE(brokenProduct->allFiles().count(), 0); + const ResolvedProductConstPtr dependerRequired = productMap.value("depender required"); + QVERIFY(!dependerRequired->enabled); + QVERIFY(dependerRequired->location.isValid()); + QCOMPARE(dependerRequired->allFiles().count(), 1); + const ResolvedProductConstPtr dependerNonRequired + = productMap.value("depender nonrequired"); + QVERIFY(dependerNonRequired->enabled); + QCOMPARE(dependerNonRequired->allFiles().count(), 1); + const ResolvedProductConstPtr recursiveDepender = productMap.value("recursive depender"); + QVERIFY(!recursiveDepender->enabled); + QVERIFY(recursiveDepender->location.isValid()); + QCOMPARE(recursiveDepender->allFiles().count(), 1); + const ResolvedProductConstPtr missingFile = productMap.value("missing file"); + QVERIFY(missingFile->enabled); + QCOMPARE(missingFile->groups.count(), 1); + QVERIFY(missingFile->groups.first()->enabled); + QCOMPARE(missingFile->groups.first()->allFiles().count(), 2); + const ResolvedProductConstPtr fine = productMap.value("fine"); + QVERIFY(fine->enabled); + QCOMPARE(fine->allFiles().count(), 1); + } catch (const ErrorInfo &e) { + QVERIFY2(strictMode, qPrintable(e.toString())); + } +} + +void TestLanguage::relaxedErrorMode_data() +{ + QTest::addColumn("strictMode"); + + QTest::newRow("strict mode") << true; + QTest::newRow("relaxed mode") << false; +} + +void TestLanguage::requiredAndNonRequiredDependencies() +{ + QFETCH(QString, projectFile); + QFETCH(bool, exceptionExpected); + try { + SetupProjectParameters params = defaultParameters; + const QString projectFilePath = "required-and-nonrequired-dependencies/" + projectFile; + params.setProjectFilePath(testProject(projectFilePath.toLocal8Bit())); + const TopLevelProjectConstPtr project = loader->loadProject(params); + QVERIFY(project); + QVERIFY(!exceptionExpected); + } catch (const ErrorInfo &e) { + QVERIFY(exceptionExpected); + QVERIFY2(e.toString().contains("validation error!"), qPrintable(e.toString())); + } +} + +void TestLanguage::requiredAndNonRequiredDependencies_data() +{ + QTest::addColumn("projectFile"); + QTest::addColumn("exceptionExpected"); + + QTest::newRow("same file") << "direct-dependencies.qbs" << true; + QTest::newRow("dependency via module") << "dependency-via-module.qbs" << true; + QTest::newRow("dependency via export") << "dependency-via-export.qbs" << true; + QTest::newRow("more indirection") << "complicated.qbs" << true; + QTest::newRow("required chain (module)") << "required-chain-module.qbs" << false; + QTest::newRow("required chain (export)") << "required-chain-export.qbs" << false; + QTest::newRow("required chain (export indirect)") << "required-chain-export-indirect.qbs" + << false; +} + +void TestLanguage::throwingProbe() +{ + QFETCH(bool, enableProbe); + try { + SetupProjectParameters params = defaultParameters; + params.setProjectFilePath(testProject("throwing-probe.qbs")); + QVariantMap properties; + properties.insert(QLatin1String("theProduct.enableProbe"), enableProbe); + params.setOverriddenValues(properties); + const TopLevelProjectPtr project = loader->loadProject(params); + QVERIFY(project); + QVERIFY(!enableProbe); + } catch (const ErrorInfo &e) { + QVERIFY2(enableProbe, qPrintable(e.toString())); + } +} + +void TestLanguage::throwingProbe_data() +{ + QTest::addColumn("enableProbe"); + + QTest::newRow("enabled probe") << true; + QTest::newRow("disabled probe") << false; +} + +void TestLanguage::qualifiedId() +{ + QString str = "foo.bar.baz"; + QualifiedId id = QualifiedId::fromString(str); + QCOMPARE(id.count(), 3); + QCOMPARE(id.toString(), str); + + id = QualifiedId("blubb.di.blubb"); // c'tor does not split + QCOMPARE(id.count(), 1); + + QList ids; + ids << QualifiedId::fromString("a") + << QualifiedId::fromString("a.a") + << QualifiedId::fromString("b") + << QualifiedId::fromString("c.a") + << QualifiedId::fromString("c.b.a") + << QualifiedId::fromString("c.c"); + QList sorted = ids; + std::sort(sorted.begin(), sorted.end()); + QCOMPARE(ids, sorted); +} + +void TestLanguage::recursiveProductDependencies() +{ + bool exceptionCaught = false; + try { + defaultParameters.setProjectFilePath( + testProject("recursive-dependencies/recursive-dependencies.qbs")); + const TopLevelProjectPtr project = loader->loadProject(defaultParameters); + QVERIFY(project); + const QHash products = productsFromProject(project); + QCOMPARE(products.count(), 4); + const ResolvedProductConstPtr p1 = products.value("p1"); + QVERIFY(p1); + const ResolvedProductConstPtr p2 = products.value("p2"); + QVERIFY(p2); + const ResolvedProductPtr p3 = products.value("p3"); + QVERIFY(p3); + const ResolvedProductPtr p4 = products.value("p4"); + QVERIFY(p4); + QVERIFY(p1->dependencies == QSet() << p3 << p4); + QVERIFY(p2->dependencies == QSet() << p3 << p4); + } catch (const ErrorInfo &e) { + exceptionCaught = true; + qDebug() << e.toString(); + } + QCOMPARE(exceptionCaught, false); +} + +void TestLanguage::fileTags_data() +{ + QTest::addColumn("numberOfGroups"); + QTest::addColumn("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("cleanup") << 0 << QStringList(); +} + +void TestLanguage::fileTags() +{ + HANDLE_INIT_CLEANUP_DATATAGS("filetags.qbs"); + QFETCH(int, numberOfGroups); + QFETCH(QStringList, expectedFileTags); + QHash 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("useGroup"); + QTest::addColumn("filesToCreate"); + QTest::addColumn("projectFileSubDir"); + QTest::addColumn("prefix"); + QTest::addColumn("patterns"); + QTest::addColumn("excludePatterns"); + QTest::addColumn("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"); + QTest::newRow(QByteArray("non-existing absolute path")) + << useGroup + << QStringList() + << QString() + << QString("/dir") + << (QStringList() << "*.whatever") + << QStringList() + << QStringList(); +} + +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 products = productsFromProject(project); + product = products.value("MyProduct"); + QVERIFY(product); + GroupPtr group; + if (useGroup) { + QCOMPARE(product->groups.count(), HostOsInfo::isMacosHost() ? 3 : 2); + foreach (const GroupPtr &rg, product->groups) { + if (rg->name == groupName) { + group = rg; + break; + } + } + } else { + QCOMPARE(product->groups.count(), HostOsInfo::isMacosHost() ? 2 : 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 00000000..f03f4be7 --- /dev/null +++ b/src/lib/corelib/language/tst_language.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_LANGUAGE_H +#define TST_LANGUAGE_H + +#include +#include +#include +#include +#include +#include + +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 productsFromProject(ResolvedProjectPtr project); + ResolvedModuleConstPtr findModuleByName(ResolvedProductPtr product, const QString &name); + QVariant productPropertyValue(ResolvedProductPtr product, QString propertyName); + void handleInitCleanupDataTags(const char *projectFileName, bool *handled); + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void baseProperty(); + void baseValidation(); + void buildConfigStringListSyntax(); + void builtinFunctionInSearchPathsProperty(); + void canonicalArchitecture(); + void conditionalDepends(); + void dependencyOnAllProfiles(); + void derivedSubProject(); + void environmentVariable(); + void erroneousFiles_data(); + void erroneousFiles(); + void exports(); + void fileContextProperties(); + void fileTags_data(); + void fileTags(); + void groupConditions_data(); + void groupConditions(); + void groupName(); + void getNativeSetting(); + void homeDirectory(); + void identifierSearch_data(); + void identifierSearch(); + void idUsage(); + void idUniqueness(); + void importCollection(); + void invalidBindingInDisabledItem(); + void itemPrototype(); + void itemScope(); + void jsExtensions(); + void jsImportUsedInMultipleScopes_data(); + void jsImportUsedInMultipleScopes(); + void moduleProperties_data(); + void moduleProperties(); + void modulePropertiesInGroups(); + void moduleScope(); + void modules_data(); + void modules(); + void nonRequiredProducts(); + void nonRequiredProducts_data(); + void outerInGroup(); + void pathProperties(); + void productConditions(); + void productDirectories(); + void profileValuesAndOverriddenValues(); + void propertiesBlocks_data(); + void propertiesBlocks(); + void propertiesBlockInGroup(); + void qbsPropertiesInProjectCondition(); + void relaxedErrorMode(); + void relaxedErrorMode_data(); + void requiredAndNonRequiredDependencies(); + void requiredAndNonRequiredDependencies_data(); + void throwingProbe(); + void throwingProbe_data(); + void defaultValue(); + void defaultValue_data(); + void qualifiedId(); + void recursiveProductDependencies(); + void rfc1034Identifier(); + void versionCompare(); + 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 00000000..2dce8d02 --- /dev/null +++ b/src/lib/corelib/language/value.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "value.h" + +#include "filecontext.h" +#include "item.h" + +#include + +namespace qbs { +namespace Internal { + +Value::Value(Type t, bool createdByPropertiesBlock) + : m_type(t), m_definingItem(0), m_createdByPropertiesBlock(createdByPropertiesBlock) +{ +} + +Value::Value(const Value &other) + : m_type(other.m_type), + m_definingItem(other.m_definingItem), + m_next(other.m_next ? other.m_next->clone() : ValuePtr()), + m_createdByPropertiesBlock(other.m_createdByPropertiesBlock) +{ +} + +Value::~Value() +{ +} + +Item *Value::definingItem() const +{ + return m_definingItem; +} + +void Value::setDefiningItem(Item *item) +{ + m_definingItem = item; +} + +ValuePtr Value::next() const +{ + return m_next; +} + +void Value::setNext(const ValuePtr &next) +{ + QBS_ASSERT(next.data() != this, return); + m_next = next; +} + + +JSSourceValue::JSSourceValue(bool createdByPropertiesBlock) + : Value(JSSourceValueType, createdByPropertiesBlock) + , m_line(-1) + , m_column(-1) +{ +} + +JSSourceValuePtr JSSourceValue::create(bool createdByPropertiesBlock) +{ + return JSSourceValuePtr(new JSSourceValue(createdByPropertiesBlock)); +} + +JSSourceValue::~JSSourceValue() +{ +} + +ValuePtr JSSourceValue::clone() const +{ + return JSSourceValuePtr(new JSSourceValue(*this)); +} + +QString JSSourceValue::sourceCodeForEvaluation() const +{ + if (!hasFunctionForm()) + return m_sourceCode.toString(); + + // rewrite blocks to be able to use return statements in property assignments + static const QString prefix = QStringLiteral("(function()"); + static const QString suffix = QStringLiteral(")()"); + return prefix + m_sourceCode.toString() + suffix; +} + +void JSSourceValue::setLocation(int line, int column) +{ + m_line = line; + m_column = column; +} + +CodeLocation JSSourceValue::location() const +{ + return CodeLocation(m_file->filePath(), m_line, m_column); +} + +void JSSourceValue::setHasFunctionForm(bool b) +{ + if (b) + m_flags |= HasFunctionForm; + else + m_flags &= ~HasFunctionForm; +} + +void JSSourceValue::setDefiningItem(Item *item) +{ + Value::setDefiningItem(item); + foreach (const JSSourceValue::Alternative &a, m_alternatives) + a.value->setDefiningItem(item); +} + +ItemValue::ItemValue(Item *item, bool createdByPropertiesBlock) + : Value(ItemValueType, createdByPropertiesBlock) + , m_item(item) +{ + QBS_CHECK(m_item); +} + +ItemValuePtr ItemValue::create(Item *item, bool createdByPropertiesBlock) +{ + return ItemValuePtr(new ItemValue(item, createdByPropertiesBlock)); +} + +ValuePtr ItemValue::clone() const +{ + return create(m_item->clone(), createdByPropertiesBlock()); +} + +VariantValue::VariantValue(const QVariant &v) + : Value(VariantValueType, false) + , m_value(v) +{ +} + +VariantValuePtr VariantValue::create(const QVariant &v) +{ + return VariantValuePtr(new VariantValue(v)); +} + +ValuePtr VariantValue::clone() const +{ + return VariantValuePtr(new VariantValue(*this)); +} + +} // 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 00000000..4231f4c3 --- /dev/null +++ b/src/lib/corelib/language/value.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_VALUE_H +#define QBS_VALUE_H + +#include "forward_decls.h" +#include +#include + +namespace qbs { +namespace Internal { +class Item; +class ValueHandler; + +class Value +{ +public: + enum Type + { + JSSourceValueType, + ItemValueType, + VariantValueType + }; + + Value(Type t, bool createdByPropertiesBlock); + Value(const Value &other); + virtual ~Value(); + + Type type() const { return m_type; } + virtual void apply(ValueHandler *) = 0; + virtual ValuePtr clone() const = 0; + virtual CodeLocation location() const { return CodeLocation(); } + + Item *definingItem() const; + virtual void setDefiningItem(Item *item); + + ValuePtr next() const; + void setNext(const ValuePtr &next); + + bool createdByPropertiesBlock() const { return m_createdByPropertiesBlock; } + +private: + Type m_type; + Item *m_definingItem; + ValuePtr m_next; + const bool m_createdByPropertiesBlock; +}; + +class ValueHandler +{ +public: + virtual void handle(JSSourceValue *value) = 0; + virtual void handle(ItemValue *value) = 0; + virtual void handle(VariantValue *value) = 0; +}; + +class JSSourceValue : public Value +{ + friend class ItemReaderASTVisitor; + JSSourceValue(bool createdByPropertiesBlock); + + enum Flag + { + NoFlags = 0x00, + SourceUsesBase = 0x01, + SourceUsesOuter = 0x02, + SourceUsesOriginal = 0x04, + HasFunctionForm = 0x08, + ExclusiveListValue = 0x10, + }; + Q_DECLARE_FLAGS(Flags, Flag) + +public: + static JSSourceValuePtr create(bool createdByPropertiesBlock = false); + ~JSSourceValue(); + + void apply(ValueHandler *handler) { handler->handle(this); } + ValuePtr clone() const; + + void setSourceCode(const QStringRef &sourceCode) { m_sourceCode = sourceCode; } + const QStringRef &sourceCode() const { return m_sourceCode; } + QString sourceCodeForEvaluation() const; + + void setLocation(int line, int column); + int line() const { return m_line; } + int column() const { return m_column; } + CodeLocation location() const; + + void setFile(const FileContextPtr &file) { m_file = file; } + const FileContextPtr &file() const { return m_file; } + + void setSourceUsesBaseFlag() { m_flags |= SourceUsesBase; } + bool sourceUsesBase() const { return m_flags.testFlag(SourceUsesBase); } + bool sourceUsesOuter() const { return m_flags.testFlag(SourceUsesOuter); } + bool sourceUsesOriginal() const { return m_flags.testFlag(SourceUsesOriginal); } + bool hasFunctionForm() const { return m_flags.testFlag(HasFunctionForm); } + void setHasFunctionForm(bool b); + void setIsExclusiveListValue() { m_flags |= ExclusiveListValue; } + bool isExclusiveListValue() { return m_flags.testFlag(ExclusiveListValue); } + + const JSSourceValuePtr &baseValue() const { return m_baseValue; } + void setBaseValue(const JSSourceValuePtr &v) { m_baseValue = v; } + + struct Alternative + { + QString condition; + QString overrideListProperties; + JSSourceValuePtr value; + }; + + const QList &alternatives() const { return m_alternatives; } + void setAlternatives(const QList &alternatives) { m_alternatives = alternatives; } + void addAlternative(const Alternative &alternative) { m_alternatives.append(alternative); } + + void setDefiningItem(Item *item); + +private: + QStringRef m_sourceCode; + int m_line; + int m_column; + FileContextPtr m_file; + Flags m_flags; + JSSourceValuePtr m_baseValue; + QList m_alternatives; +}; + + +class ItemValue : public Value +{ + ItemValue(Item *item, bool createdByPropertiesBlock); +public: + static ItemValuePtr create(Item *item, bool createdByPropertiesBlock = false); + + Item *item() const { return m_item; } + void setItem(Item *item) { m_item = item; } + +private: + void apply(ValueHandler *handler) override { handler->handle(this); } + ValuePtr clone() const override; + + Item *m_item; +}; + + +class VariantValue : public Value +{ + VariantValue(const QVariant &v); +public: + static VariantValuePtr create(const QVariant &v = QVariant()); + + void apply(ValueHandler *handler) { handler->handle(this); } + ValuePtr clone() const; + + 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 00000000..4bec7f61 --- /dev/null +++ b/src/lib/corelib/logging/ilogsink.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "ilogsink.h" + +#include + +#include +#include + +namespace qbs { + +QString logLevelTag(LoggerLevel level) +{ + if (level == LoggerInfo) + return QString(); + 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 00000000..c5c31072 --- /dev/null +++ b/src/lib/corelib/logging/ilogsink.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_ILOGSINK_H +#define QBS_ILOGSINK_H + +#include "../tools/qbs_export.h" + +#include + +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 00000000..7aadd785 --- /dev/null +++ b/src/lib/corelib/logging/logger.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined(_MSC_VER) && _MSC_VER > 0 +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "logger.h" + +#include +#include +#include +#include +#include + +#include +#include + +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 &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 = QLatin1String(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::fromLatin1(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); +} + +void Logger::printWarning(const ErrorInfo &warning) +{ + if (m_storeWarnings) + m_warnings << warning; + logSink()->printWarning(warning); +} + +LogWriter Logger::qbsLog(LoggerLevel level, bool force) const +{ + return LogWriter(m_logSink, level, force); +} + +} // 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 00000000..ef35198b --- /dev/null +++ b/src/lib/corelib/logging/logger.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_LOGGER_H +#define QBS_LOGGER_H + +#include "ilogsink.h" + +#include + +#include +#include +#include + +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 &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); + QList warnings() const { return m_warnings; } + void clearWarnings() { m_warnings.clear(); } + void storeWarnings() { m_storeWarnings = true; } + + 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; + QList m_warnings; + bool m_storeWarnings = false; +}; + +} // 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 00000000..029a4fb3 --- /dev/null +++ b/src/lib/corelib/logging/logging.pri @@ -0,0 +1,16 @@ +include(../../../install_prefix.pri) + +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 00000000..4eca789e --- /dev/null +++ b/src/lib/corelib/logging/translator.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_TRANSLATOR_H +#define QBS_TRANSLATOR_H + +#include + +#include + +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 00000000..e6a8a534 --- /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 00000000..2d347502 --- /dev/null +++ b/src/lib/corelib/parser/qmlerror.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlerror.h" + +#include +#include +#include + +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(""); + } 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 00000000..cfac506b --- /dev/null +++ b/src/lib/corelib/parser/qmlerror.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLERROR_H +#define QQMLERROR_H + + + +#include +#include + +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 00000000..c1953737 --- /dev/null +++ b/src/lib/corelib/parser/qmljs.g @@ -0,0 +1,3011 @@ +----------------------------------------------------------------------------- +-- +-- Copyright (C) 2016 The Qt Company Ltd. +-- Contact: https://www.qt.io/licensing/ +-- +-- This file is part of Qbs. +-- +-- $QT_BEGIN_LICENSE:LGPL$ +-- 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 The Qt Company. For licensing terms +-- and conditions see https://www.qt.io/terms-conditions. For further +-- information use the contact form at https://www.qt.io/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 3 as published by the Free Software +-- Foundation and appearing in the file LICENSE.LGPL3 included in the +-- packaging of this file. Please review the following information to +-- ensure the GNU Lesser General Public License version 3 requirements +-- will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +-- +-- GNU General Public License Usage +-- Alternatively, this file may be used under the terms of the GNU +-- General Public License version 2.0 or (at your option) the GNU General +-- Public license version 3 or any later version approved by the KDE Free +-- Qt Foundation. The licenses are as published by the Free Software +-- Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +-- included in the packaging of this file. Please review the following +-- information to ensure the GNU General Public License requirements will +-- be met: https://www.gnu.org/licenses/gpl-2.0.html and +-- https://www.gnu.org/licenses/gpl-3.0.html. +-- +-- $QT_END_LICENSE$ +-- +----------------------------------------------------------------------------- + +%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) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +#include "qmljsengine_p.h" +#include "qmljslexer_p.h" +#include "qmljsast_p.h" +#include "qmljsmemorypool_p.h" + +./ + +/:/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 +#include + +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(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 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 diagnostic_messages; +}; + +} // end of namespace QmlJS + + +:/ + + +/. + +#include "qmljsparser_p.h" +#include + +// +// 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 (realloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast (realloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast (realloc(location_stack, stack_size * sizeof(AST::SourceLocation))); + string_stack = reinterpret_cast (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 nameIds; + QVarLengthArray locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast(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(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(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 00000000..1d0d87b2 --- /dev/null +++ b/src/lib/corelib/parser/qmljsast.cpp @@ -0,0 +1,925 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 00000000..4506eeb1 --- /dev/null +++ b/src/lib/corelib/parser/qmljsast_p.h @@ -0,0 +1,2633 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 + +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 +_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(sourceElement)) + return funDecl->firstSourceLocation(); + else if (VariableStatement *varStmt = cast(sourceElement)) + return varStmt->firstSourceLocation(); + + return SourceLocation(); + } + + virtual SourceLocation lastSourceLocation() const + { + if (FunctionDeclaration *funDecl = cast(sourceElement)) + return funDecl->lastSourceLocation(); + else if (VariableStatement *varStmt = cast(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 00000000..f032f352 --- /dev/null +++ b/src/lib/corelib/parser/qmljsastfwd_p.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLJSAST_FWD_P_H +#define QMLJSAST_FWD_P_H + +#include "qmljsglobal_p.h" + +#include + +// +// 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 00000000..fcb84ec7 --- /dev/null +++ b/src/lib/corelib/parser/qmljsastvisitor.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 00000000..f0eff5ce --- /dev/null +++ b/src/lib/corelib/parser/qmljsastvisitor_p.h @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 00000000..66e6f372 --- /dev/null +++ b/src/lib/corelib/parser/qmljsengine_p.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmljsengine_p.h" +#include "qmljsglobal_p.h" + +#include +#include +#include + +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 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 00000000..e39308e4 --- /dev/null +++ b/src/lib/corelib/parser/qmljsengine_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 +#include + +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 _comments; + QString _extraCode; + QString _code; + +public: + Engine(); + ~Engine(); + + void setCode(const QString &code); + + void addComment(int pos, int len, int line, int col); + QList 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 00000000..5a875c01 --- /dev/null +++ b/src/lib/corelib/parser/qmljsglobal_p.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QMLJSGLOBAL_P_H +#define QMLJSGLOBAL_P_H + +#include + +#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 00000000..07a193f6 --- /dev/null +++ b/src/lib/corelib/parser/qmljsgrammar.cpp @@ -0,0 +1,1011 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// 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 00000000..a3525408 --- /dev/null +++ b/src/lib/corelib/parser/qmljsgrammar_p.h @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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 + +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 00000000..77abdada --- /dev/null +++ b/src/lib/corelib/parser/qmljskeywords_p.h @@ -0,0 +1,862 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 00000000..94646382 --- /dev/null +++ b/src/lib/corelib/parser/qmljslexer.cpp @@ -0,0 +1,1151 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmljslexer_p.h" +#include "qmljsengine_p.h" +#include "qmljsmemorypool_p.h" + +#include +#include +#include + +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 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 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 00000000..3c92928a --- /dev/null +++ b/src/lib/corelib/parser/qmljslexer_p.h @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 + +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 00000000..fa16ac40 --- /dev/null +++ b/src/lib/corelib/parser/qmljsmemorypool_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 +#include +#include + +#include + +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 00000000..189855da --- /dev/null +++ b/src/lib/corelib/parser/qmljsparser.cpp @@ -0,0 +1,1820 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +#include "qmljsengine_p.h" +#include "qmljslexer_p.h" +#include "qmljsast_p.h" +#include "qmljsmemorypool_p.h" + + + +#include "qmljsparser_p.h" +#include + +// +// 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 (realloc(sym_stack, stack_size * sizeof(Value))); + state_stack = reinterpret_cast (realloc(state_stack, stack_size * sizeof(int))); + location_stack = reinterpret_cast (realloc(location_stack, stack_size * sizeof(AST::SourceLocation))); + string_stack = reinterpret_cast (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 nameIds; + QVarLengthArray locations; + + AST::ExpressionNode *it = expr; + while (AST::FieldMemberExpression *m = AST::cast(it)) { + nameIds.append(m->name); + locations.append(m->identifierToken); + it = m->base; + } + + if (AST::IdentifierExpression *idExpr = AST::cast(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(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(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 00000000..1a9958e5 --- /dev/null +++ b/src/lib/corelib/parser/qmljsparser_p.h @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +// +// 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 +#include + +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(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 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 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 00000000..ef0d5f64 --- /dev/null +++ b/src/lib/corelib/qbs.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_H +#define QBS_H + +#include "api/jobs.h" +#include "api/languageinfo.h" +#include "api/project.h" +#include "api/projectdata.h" +#include "api/rulecommand.h" +#include "logging/ilogsink.h" +#include "tools/architectures.h" +#include "tools/buildoptions.h" +#include "tools/cleanoptions.h" +#include "tools/error.h" +#include "tools/generateoptions.h" +#include "tools/installoptions.h" +#include "tools/preferences.h" +#include "tools/profile.h" +#include "tools/processresult.h" +#include "tools/settings.h" +#include "tools/settingsmodel.h" +#include "tools/setupprojectparameters.h" +#include "tools/toolchains.h" + +#endif // QBS_H diff --git a/src/lib/corelib/tools/applecodesignutils.cpp b/src/lib/corelib/tools/applecodesignutils.cpp new file mode 100644 index 00000000..54e7ced0 --- /dev/null +++ b/src/lib/corelib/tools/applecodesignutils.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "applecodesignutils.h" +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace qbs { +namespace Internal { + +QByteArray smimeMessageContent(const QByteArray &data) +{ + QCFType decoder = NULL; + if (CMSDecoderCreate(&decoder) != noErr) + return QByteArray(); + + if (CMSDecoderUpdateMessage(decoder, data.constData(), data.size()) != noErr) + return QByteArray(); + + if (CMSDecoderFinalizeMessage(decoder) != noErr) + return QByteArray(); + + QCFType content = NULL; + if (CMSDecoderCopyContent(decoder, &content) != noErr) + return QByteArray(); + + return QByteArray::fromCFData(content); +} + +QVariantMap certificateInfo(const QByteArray &data) +{ + const QSslCertificate cert(data, QSsl::Der); + + // Also potentially useful, but these are for signing pkgs which aren't used here + // 1.2.840.113635.100.4.9 - 3rd Party Mac Developer Installer: + // 1.2.840.113635.100.4.13 - Developer ID Installer: + for (const auto &extension : cert.extensions()) { + if (extension.name() == QStringLiteral("extendedKeyUsage")) { + if (!extension.value().toStringList().contains(QStringLiteral("Code Signing"))) + return QVariantMap(); + } + } + + const auto subjectInfo = [](const QSslCertificate &cert) { + QVariantMap map; + for (const auto &attr : cert.subjectInfoAttributes()) + map.insert(QString::fromLatin1(attr), cert.subjectInfo(attr).first()); + return map; + }; + + return { + {QStringLiteral("SHA1"), cert.digest(QCryptographicHash::Sha1).toHex().toUpper()}, + {QStringLiteral("subjectInfo"), subjectInfo(cert)}, + {QStringLiteral("validBefore"), cert.effectiveDate()}, + {QStringLiteral("validAfter"), cert.expiryDate()} + }; +} + +QVariantMap identitiesProperties() +{ + // Apple documentation states that the Sec* family of functions are not thread-safe on macOS + // https://developer.apple.com/library/mac/documentation/Security/Reference/certifkeytrustservices/ + static QMutex securityMutex; + QMutexLocker locker(&securityMutex); + Q_UNUSED(locker); + + const void *keys[] = {kSecClass, kSecMatchLimit, kSecAttrCanSign}; + const void *values[] = {kSecClassIdentity, kSecMatchLimitAll, kCFBooleanTrue}; + QCFType query = CFDictionaryCreate(kCFAllocatorDefault, + keys, + values, + sizeof(keys) / sizeof(keys[0]), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + QCFType result = NULL; + if (SecItemCopyMatching(query, &result) != errSecSuccess) + return QVariantMap(); + + QVariantMap items; + const auto tryAppend = [&](SecIdentityRef identity) { + if (!identity) + return; + + QCFType certificate = NULL; + if (SecIdentityCopyCertificate(identity, &certificate) != errSecSuccess) + return; + + QCFType certificateData = SecCertificateCopyData(certificate); + if (!certificateData) + return; + + auto props = certificateInfo(QByteArray::fromRawCFData(certificateData)); + if (!props.isEmpty()) + items.insert(props[QStringLiteral("SHA1")].toString(), props); + }; + + if (CFGetTypeID(result) == SecIdentityGetTypeID()) { + tryAppend((SecIdentityRef)result.operator const void *()); + } else if (CFGetTypeID(result) == CFArrayGetTypeID()) { + for (CFIndex i = 0; i < CFArrayGetCount((CFArrayRef)result.operator const void *()); ++i) + tryAppend((SecIdentityRef)CFArrayGetValueAtIndex(result.as(), i)); + } + + return items; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/applecodesignutils.h b/src/lib/corelib/tools/applecodesignutils.h new file mode 100644 index 00000000..95b9186a --- /dev/null +++ b/src/lib/corelib/tools/applecodesignutils.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_APPLECODESIGNUTILS_H +#define QBS_APPLECODESIGNUTILS_H + +#include "qbs_export.h" +#include + +namespace qbs { +namespace Internal { + +QByteArray smimeMessageContent(const QByteArray &data); +QVariantMap certificateInfo(const QByteArray &data); +QVariantMap identitiesProperties(); + +} // namespace Internal +} // namespace qbs + +#endif // QBS_APPLECODESIGNUTILS_H diff --git a/src/lib/corelib/tools/architectures.cpp b/src/lib/corelib/tools/architectures.cpp new file mode 100644 index 00000000..ca43b90c --- /dev/null +++ b/src/lib/corelib/tools/architectures.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "architectures.h" + +#include +#include +#include + +namespace qbs { + +QString canonicalArchitecture(const QString &architecture) +{ + QMap 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") + << QLatin1String("mingw32")); + + archMap.insert(QLatin1String("x86_64"), QStringList() + << QLatin1String("x86-64") + << QLatin1String("x64") + << QLatin1String("amd64") + << QLatin1String("ia32e") + << QLatin1String("em64t") + << QLatin1String("intel64") + << QLatin1String("mingw64")); + + archMap.insert(QLatin1String("arm64"), QStringList() + << QLatin1String("aarch64")); + + archMap.insert(QLatin1String("ia64"), QStringList() + << QLatin1String("ia-64") + << QLatin1String("itanium")); + + archMap.insert(QLatin1String("ppc"), QStringList() + << QLatin1String("powerpc")); + + archMap.insert(QLatin1String("ppc64"), QStringList() + << QLatin1String("powerpc64")); + + archMap.insert(QLatin1String("ppc64le"), QStringList() + << QLatin1String("powerpc64le")); + + QMapIterator i(archMap); + while (i.hasNext()) { + i.next(); + if (i.value().contains(architecture.toLower())) + return i.key(); + } + + return architecture; + +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/architectures.h b/src/lib/corelib/tools/architectures.h new file mode 100644 index 00000000..bb7f0d21 --- /dev/null +++ b/src/lib/corelib/tools/architectures.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_ARCHITECTURES_H +#define QBS_ARCHITECTURES_H + +#include "qbs_export.h" + +namespace qbs { + +QBS_EXPORT QString canonicalArchitecture(const QString &architecture); + +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/tools/buildgraphlocker.cpp b/src/lib/corelib/tools/buildgraphlocker.cpp new file mode 100644 index 00000000..529ac097 --- /dev/null +++ b/src/lib/corelib/tools/buildgraphlocker.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "buildgraphlocker.h" + +#include "error.h" +#include "hostosinfo.h" +#include "processutils.h" +#include "progressobserver.h" +#include "version.h" + +#include + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +static bool hasQtBug45497() +{ + return Version::fromString(QLatin1String(qVersion())) < Version(5, 5, 1); +} + +static bool hasQtBug53392() +{ + if (!HostOsInfo::isWindowsHost()) + return false; + const Version qtVersion = Version::fromString(QLatin1String(qVersion())); + // The fix is included in 5.6.2 and 5.7.1, but not in 5.7.0. + return qtVersion == Version(5, 7, 0) || qtVersion < Version(5, 6, 2); +} + +static void tryCreateBuildDirectory(const QString &buildDir, const QString &buildGraphFilePath) +{ + if (!QDir::root().mkpath(buildDir)) { + throw ErrorInfo(Tr::tr("Cannot lock build graph file '%1': Failed to create directory.") + .arg(buildGraphFilePath)); + } +} + +BuildGraphLocker::BuildGraphLocker(const QString &buildGraphFilePath, const Logger &logger, + bool waitIndefinitely, ProgressObserver *observer) + : m_lockFile(buildGraphFilePath + QLatin1String(".lock")) + , m_logger(logger) +{ + const QString buildDir = QFileInfo(buildGraphFilePath).absolutePath(); + rememberCreatedDirectories(buildDir); + if (waitIndefinitely) + m_logger.qbsDebug() << "Waiting to acquire lock file..."; + m_lockFile.setStaleLockTime(0); + int attemptsToGetInfo = 0; + do { + if (observer->canceled()) + break; + tryCreateBuildDirectory(buildDir, buildGraphFilePath); + if (m_lockFile.tryLock(250)) + return; + switch (m_lockFile.error()) { + case QLockFile::LockFailedError: { + if (waitIndefinitely) + continue; + qint64 pid; + QString hostName; + QString appName; + if (m_lockFile.getLockInfo(&pid, &hostName, &appName)) { + if ((!hasQtBug45497() && !hasQtBug53392()) || appName == processNameByPid(pid)) { + throw ErrorInfo(Tr::tr("Cannot lock build graph file '%1': " + "Already locked by '%2' (PID %3).") + .arg(buildGraphFilePath, appName).arg(pid)); + } + + // The process id was reused by some other process. + m_logger.qbsInfo() << Tr::tr("Removing stale lock file."); + m_lockFile.removeStaleLockFile(); + } + break; + } + case QLockFile::PermissionError: + throw ErrorInfo(Tr::tr("Cannot lock build graph file '%1': Permission denied.") + .arg(buildGraphFilePath)); + case QLockFile::UnknownError: + case QLockFile::NoError: + throw ErrorInfo(Tr::tr("Cannot lock build graph file '%1' (reason unknown).") + .arg(buildGraphFilePath)); + } + } while (++attemptsToGetInfo < 10 || waitIndefinitely); + + // This very unlikely case arises if tryLock() repeatedly returns LockFailedError + // with the subsequent getLockInfo() failing as well. + throw ErrorInfo(Tr::tr("Cannot lock build graph file '%1' (reason unknown).") + .arg(buildGraphFilePath)); +} + +BuildGraphLocker::~BuildGraphLocker() +{ + m_lockFile.unlock(); + removeEmptyCreatedDirectories(); +} + +void BuildGraphLocker::rememberCreatedDirectories(const QString &buildDir) +{ + QString parentDir = buildDir; + while (!QFileInfo::exists(parentDir)) { + m_createdParentDirs.enqueue(parentDir); + parentDir = QDir::cleanPath(parentDir + QLatin1String("/..")); + } +} + +void BuildGraphLocker::removeEmptyCreatedDirectories() +{ + QDir root = QDir::root(); + while (!m_createdParentDirs.isEmpty()) { + const QString parentDir = m_createdParentDirs.dequeue(); + QDirIterator it(parentDir, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::System, + QDirIterator::Subdirectories); + if (it.hasNext()) + break; + if (!root.rmdir(parentDir)) { + m_logger.printWarning(ErrorInfo(Tr::tr("Failed to remove empty directory '%1'.") + .arg(parentDir))); + } + } +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/buildgraphlocker.h b/src/lib/corelib/tools/buildgraphlocker.h new file mode 100644 index 00000000..a7554f91 --- /dev/null +++ b/src/lib/corelib/tools/buildgraphlocker.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_BUILDGRAPHLOCKER_H +#define QBS_BUILDGRAPHLOCKER_H + +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +class ProgressObserver; + +class BuildGraphLocker +{ +public: + explicit BuildGraphLocker(const QString &buildGraphFilePath, const Logger &logger, + bool waitIndefinitely, ProgressObserver *observer); + ~BuildGraphLocker(); + +private: + void rememberCreatedDirectories(const QString &buildDir); + void removeEmptyCreatedDirectories(); + + QLockFile m_lockFile; + Logger m_logger; + QQueue m_createdParentDirs; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/tools/buildoptions.cpp b/src/lib/corelib/tools/buildoptions.cpp new file mode 100644 index 00000000..70585fa4 --- /dev/null +++ b/src/lib/corelib/tools/buildoptions.cpp @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "buildoptions.h" + +#include +#include + +namespace qbs { +namespace Internal { + +class BuildOptionsPrivate : public QSharedData +{ +public: + BuildOptionsPrivate() + : maxJobCount(0), dryRun(false), keepGoing(false), forceTimestampCheck(false), + forceOutputCheck(false), + logElapsedTime(false), echoMode(defaultCommandEchoMode()), install(true), + removeExistingInstallation(false), onlyExecuteRules(false) + { + } + + QStringList changedFiles; + QStringList filesToConsider; + QStringList activeFileTags; + int maxJobCount; + bool dryRun; + bool keepGoing; + bool forceTimestampCheck; + bool forceOutputCheck; + bool logElapsedTime; + CommandEchoMode echoMode; + bool install; + bool removeExistingInstallation; + bool onlyExecuteRules; +}; + +} // 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 files to consider. + * \sa setFilesToConsider. + * By default, this list is empty. + */ +QStringList BuildOptions::filesToConsider() const +{ + return d->filesToConsider; +} + +/*! + * \brief If the given list is non-empty, qbs will run only commands whose rule has at least one + * of these files as an input. + * \note The list elements must be absolute file paths. + */ +void BuildOptions::setFilesToConsider(const QStringList &files) +{ + d->filesToConsider = files; +} + +/*! + * \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. call \c setFilesToConsider() with "foo.cpp" and \c setActiveFileTags() with "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 if qbs will test whether rules actually create their + * declared output artifacts. + * The default is \c false. + */ +bool BuildOptions::forceOutputCheck() const +{ + return d->forceOutputCheck; +} + +/*! + * \brief Controls whether qbs should test whether rules actually create their + * declared output artifacts. Enabling this may introduce some small I/O overhead during the build. + */ +void BuildOptions::setForceOutputCheck(bool enabled) +{ + d->forceOutputCheck = 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; +} + +/*! + * \brief The kind of output that is displayed when executing commands. + */ +CommandEchoMode BuildOptions::echoMode() const +{ + return d->echoMode; +} + +/*! + * \brief Controls the kind of output that is displayed when executing commands. + */ +void BuildOptions::setEchoMode(CommandEchoMode echoMode) +{ + d->echoMode = echoMode; +} + +/*! + * \brief Returns true iff installation should happen as part of the build. + * The default is \c true. + */ +bool BuildOptions::install() const +{ + return d->install; +} + +/*! + * \brief Controls whether to install artifacts as part of the build process. + */ +void BuildOptions::setInstall(bool install) +{ + d->install = install; +} + +/*! + * \brief Returns true iff an existing installation will be removed prior to building. + * The default is false. + */ +bool BuildOptions::removeExistingInstallation() const +{ + return d->removeExistingInstallation; +} + +/*! + * 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 BuildOptions::setRemoveExistingInstallation(bool removeExisting) +{ + d->removeExistingInstallation = removeExisting; +} + +/*! + * \brief Returns true iff instead of a full build, only the rules of the project will be run. + * The default is false. + */ +bool BuildOptions::executeRulesOnly() const +{ + return d->onlyExecuteRules; +} + +/*! + * If \a onlyRules is \c true, then no artifacts are built, but only rules are being executed. + * \note If the project contains highly dynamic rules that depend on output artifacts of child + * rules being already present, then the associated build job may fail even though + * the project is perfectly valid. Callers need to take this into consideration. + */ +void BuildOptions::setExecuteRulesOnly(bool onlyRules) +{ + d->onlyExecuteRules = onlyRules; +} + + +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.echoMode() == bo2.echoMode() + && bo1.maxJobCount() == bo2.maxJobCount() + && bo1.install() == bo2.install() + && bo1.removeExistingInstallation() == bo2.removeExistingInstallation(); +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/buildoptions.h b/src/lib/corelib/tools/buildoptions.h new file mode 100644 index 00000000..0931243e --- /dev/null +++ b/src/lib/corelib/tools/buildoptions.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BUILDOPTIONS_H +#define QBS_BUILDOPTIONS_H + +#include "qbs_export.h" + +#include "commandechomode.h" + +#include +#include + +namespace qbs { +namespace Internal { class BuildOptionsPrivate; } + +class QBS_EXPORT BuildOptions +{ +public: + BuildOptions(); + BuildOptions(const BuildOptions &other); + BuildOptions &operator=(const BuildOptions &other); + ~BuildOptions(); + + QStringList filesToConsider() const; + void setFilesToConsider(const QStringList &files); + + 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 forceOutputCheck() const; + void setForceOutputCheck(bool enabled); + + bool logElapsedTime() const; + void setLogElapsedTime(bool log); + + CommandEchoMode echoMode() const; + void setEchoMode(CommandEchoMode echoMode); + + bool install() const; + void setInstall(bool install); + + bool removeExistingInstallation() const; + void setRemoveExistingInstallation(bool removeExisting); + + bool executeRulesOnly() const; + void setExecuteRulesOnly(bool onlyRules); + +private: + QSharedDataPointer 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 00000000..4ea40a4f --- /dev/null +++ b/src/lib/corelib/tools/cleanoptions.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "cleanoptions.h" + +#include + +namespace qbs { +namespace Internal { + +class CleanOptionsPrivate : public QSharedData +{ +public: + CleanOptionsPrivate() + : dryRun(false), + keepGoing(false), logElapsedTime(false) + { } + + 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 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 00000000..942416c2 --- /dev/null +++ b/src/lib/corelib/tools/cleanoptions.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_CLEANOPTIONS_H +#define QBS_CLEANOPTIONS_H + +#include "qbs_export.h" + +#include +#include + +namespace qbs { +namespace Internal { class CleanOptionsPrivate; } + +class QBS_EXPORT CleanOptions +{ +public: + CleanOptions(); + CleanOptions(const CleanOptions &other); + CleanOptions &operator=(const CleanOptions &other); + ~CleanOptions(); + + bool dryRun() const; + void setDryRun(bool dryRun); + + bool keepGoing() const; + void setKeepGoing(bool keepGoing); + + bool logElapsedTime() const; + void setLogElapsedTime(bool log); + +private: + QSharedDataPointer 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 00000000..34c068b8 --- /dev/null +++ b/src/lib/corelib/tools/codelocation.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "codelocation.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { + +class CodeLocation::CodeLocationPrivate : public QSharedData +{ +public: + QString filePath; + int line; + int column; +}; + +CodeLocation::CodeLocation() +{ +} + +CodeLocation::CodeLocation(const QString &aFilePath, int aLine, int aColumn, bool checkPath) + : d(new CodeLocationPrivate) +{ + QBS_ASSERT(!checkPath || Internal::FileInfo::isAbsolute(aFilePath), qDebug() << aFilePath); + d->filePath = aFilePath; + 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::filePath() const +{ + return d ? d->filePath : QString(); +} + +int CodeLocation::line() const +{ + return d ? d->line : -1; +} + +int CodeLocation::column() const +{ + return d ? d->column : -1; +} + +bool CodeLocation::isValid() const +{ + return !filePath().isEmpty(); +} + +QString CodeLocation::toString() const +{ + QString str; + if (isValid()) { + str = QDir::toNativeSeparators(filePath()); + 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; +} + +void CodeLocation::load(Internal::PersistentPool &pool) +{ + int isValid; + pool.stream() >> isValid; + if (!isValid) + return; + d = new CodeLocationPrivate; + d->filePath = pool.idLoadString(); + pool.stream() >> d->line; + pool.stream() >> d->column; +} + +void CodeLocation::store(Internal::PersistentPool &pool) const +{ + if (d) { + pool.stream() << 1; + pool.storeString(d->filePath); + pool.stream() << d->line; + pool.stream() << d->column; + } else { + pool.stream() << 0; + } +} + +bool operator==(const CodeLocation &cl1, const CodeLocation &cl2) +{ + if (cl1.d == cl2.d) + return true; + return cl1.filePath() == cl2.filePath() && cl1.line() == cl2.line() + && cl1.column() == cl2.column(); +} + +bool operator!=(const CodeLocation &cl1, const CodeLocation &cl2) +{ + return !(cl1 == cl2); +} + +QDebug operator<<(QDebug debug, const CodeLocation &location) +{ + return debug << location.toString(); +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/codelocation.h b/src/lib/corelib/tools/codelocation.h new file mode 100644 index 00000000..71ea5131 --- /dev/null +++ b/src/lib/corelib/tools/codelocation.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SOURCELOCATION_H +#define QBS_SOURCELOCATION_H + +#include "qbs_export.h" + +#include +#include + +QT_BEGIN_NAMESPACE +class QDataStream; +class QString; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { class PersistentPool; } + +class QBS_EXPORT CodeLocation +{ + friend QBS_EXPORT bool operator==(const CodeLocation &cl1, const CodeLocation &cl2); +public: + CodeLocation(); + explicit CodeLocation(const QString &aFilePath, int aLine = -1, int aColumn = -1, + bool checkPath = true); + CodeLocation(const CodeLocation &other); + CodeLocation &operator=(const CodeLocation &other); + ~CodeLocation(); + + QString filePath() const; + int line() const; + int column() const; + + bool isValid() const; + QString toString() const; + + void load(Internal::PersistentPool &pool); + void store(Internal::PersistentPool &pool) const; + +private: + class CodeLocationPrivate; + QExplicitlySharedDataPointer d; +}; + +QBS_EXPORT bool operator==(const CodeLocation &cl1, const CodeLocation &cl2); +QBS_EXPORT bool operator!=(const CodeLocation &cl1, const CodeLocation &cl2); + +inline uint qHash(const CodeLocation &cl) { return qHash(cl.toString()); } + +QDebug operator<<(QDebug debug, const CodeLocation &location); + +} // namespace qbs + +#endif // QBS_SOURCELOCATION_H diff --git a/src/lib/corelib/tools/commandechomode.cpp b/src/lib/corelib/tools/commandechomode.cpp new file mode 100644 index 00000000..fc6a3386 --- /dev/null +++ b/src/lib/corelib/tools/commandechomode.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "commandechomode.h" + +/*! + * \enum CommandEchoMode + * This enum type specifies the kind of output to display when executing commands. + * \value CommandEchoModeSilent Indicates that no output will be printed. + * \value CommandEchoModeSummary Indicates that descriptions will be printed. + * \value CommandEchoModeCommandLine Indidcates that full command line invocations will be printed. + * \value CommandEchoModeCommandLineWithEnvironment Indidcates that full command line invocations, + * including environment variables, will be printed. + */ + +namespace qbs { + +CommandEchoMode defaultCommandEchoMode() +{ + return CommandEchoModeSummary; +} + +QString commandEchoModeName(CommandEchoMode mode) +{ + switch (mode) { + case CommandEchoModeSilent: + return QLatin1String("silent"); + case CommandEchoModeSummary: + return QLatin1String("summary"); + case CommandEchoModeCommandLine: + return QLatin1String("command-line"); + case CommandEchoModeCommandLineWithEnvironment: + return QLatin1String("command-line-with-environment"); + default: + break; + } + return QString(); +} + +CommandEchoMode commandEchoModeFromName(const QString &name) +{ + CommandEchoMode mode = defaultCommandEchoMode(); + for (int i = 0; i < CommandEchoModeInvalid; ++i) { + if (commandEchoModeName(static_cast(i)) == name) { + mode = static_cast(i); + break; + } + } + + return mode; +} + +QStringList allCommandEchoModeStrings() +{ + QStringList result; + for (int i = 0; i < CommandEchoModeInvalid; ++i) + result << commandEchoModeName(static_cast(i)); + return result; +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/commandechomode.h b/src/lib/corelib/tools/commandechomode.h new file mode 100644 index 00000000..16d5c97d --- /dev/null +++ b/src/lib/corelib/tools/commandechomode.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_COMMANDECHOMODE_H +#define QBS_COMMANDECHOMODE_H + +#include "qbs_export.h" +#include +#include + +namespace qbs { + +enum CommandEchoMode { + CommandEchoModeSilent, + CommandEchoModeSummary, + CommandEchoModeCommandLine, + CommandEchoModeCommandLineWithEnvironment, + CommandEchoModeInvalid, +}; + +QBS_EXPORT CommandEchoMode defaultCommandEchoMode(); +QBS_EXPORT QString commandEchoModeName(CommandEchoMode mode); +QBS_EXPORT CommandEchoMode commandEchoModeFromName(const QString &name); +QBS_EXPORT QStringList allCommandEchoModeStrings(); + +} // namespace qbs + +#endif // QBS_COMMANDECHOMODE_H + diff --git a/src/lib/corelib/tools/error.cpp b/src/lib/corelib/tools/error.cpp new file mode 100644 index 00000000..394fd8c8 --- /dev/null +++ b/src/lib/corelib/tools/error.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "error.h" + +#include "persistence.h" + +#include +#include +#include + +#include +#include + +namespace qbs { + +class ErrorItem::ErrorItemPrivate : public QSharedData +{ +public: + QString description; + CodeLocation codeLocation; + bool isBacktraceItem = false; +}; + +/*! + * \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, + bool isBacktraceItem) + : d(new ErrorItemPrivate) +{ + d->description = description; + d->codeLocation = codeLocation; + d->isBacktraceItem = isBacktraceItem; +} + +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; +} + +bool ErrorItem::isBacktraceItem() const +{ + return d->isBacktraceItem; +} + +void ErrorItem::load(Internal::PersistentPool &pool) +{ + d->description = pool.idLoadString(); + d->codeLocation.load(pool); + pool.stream() >> d->isBacktraceItem; +} + +void ErrorItem::store(Internal::PersistentPool &pool) const +{ + pool.storeString(d->description); + d->codeLocation.store(pool); + pool.stream() << d->isBacktraceItem; +} + +/*! + * \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 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(const QString &description, const QStringList &backtrace) + : d(new ErrorInfoPrivate) +{ + append(description); + for (const QString &traceLine : backtrace) { + QRegularExpression regexp( + QStringLiteral("^(?.+) at (?.+):(?\\-?[0-9]+)$")); + QRegularExpressionMatch match = regexp.match(traceLine); + if (match.hasMatch()) { + const CodeLocation location(match.captured(QStringLiteral("file")), + match.captured(QStringLiteral("line")).toInt()); + appendBacktrace(match.captured(QStringLiteral("message")), location); + } + } +} + + +ErrorInfo &ErrorInfo::operator =(const ErrorInfo &other) +{ + d = other.d; + return *this; +} + +ErrorInfo::~ErrorInfo() +{ +} + +void ErrorInfo::appendBacktrace(const QString &description, const CodeLocation &location) +{ + d->items.append(ErrorItem(description, location, true)); +} + +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 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) { + if (e.isBacktraceItem()) { + QString line; + if (!e.description().isEmpty()) + line.append(QStringLiteral(" at %1").arg(e.description())); + if (e.codeLocation().isValid()) + line.append(QStringLiteral(" in %1").arg(e.codeLocation().toString())); + if (!line.isEmpty()) + lines.append(QStringLiteral("\t") + line); + } else { + lines.append(e.toString()); + } + } + return lines.join(QLatin1Char('\n')); +} + +/*! + * \brief Returns true if this error represents a bug in qbs, false otherwise. + */ +bool ErrorInfo::isInternalError() const +{ + return d->internalError; +} + +void ErrorInfo::load(Internal::PersistentPool &pool) +{ + int itemCount; + pool.stream() >> itemCount; + for (int i = 0; i < itemCount; ++i) { + ErrorItem e; + e.load(pool); + d->items << e; + } + pool.stream() >> d->internalError; +} + +void ErrorInfo::store(Internal::PersistentPool &pool) const +{ + pool.stream() << d->items.count(); + std::for_each(d->items.constBegin(), d->items.constEnd(), + [&pool](const ErrorItem &e) { e.store(pool); }); + pool.stream() << 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 00000000..afab6aa5 --- /dev/null +++ b/src/lib/corelib/tools/error.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_ERROR +#define QBS_ERROR + +#include "codelocation.h" + +#include +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { class PersistentPool; } +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; + + bool isBacktraceItem() const; + + void load(Internal::PersistentPool &pool); + void store(Internal::PersistentPool &pool) const; + +private: + ErrorItem(const QString &description, const CodeLocation &codeLocation, + bool isBacktraceItem = false); + + class ErrorItemPrivate; + QExplicitlySharedDataPointer d; +}; + +class QBS_EXPORT ErrorInfo +{ +public: + ErrorInfo(); + ErrorInfo(const ErrorInfo &rhs); + ErrorInfo(const QString &description, const CodeLocation &location = CodeLocation(), + bool internalError = false); + ErrorInfo(const QString &description, const QStringList &backtrace); + ErrorInfo &operator=(const ErrorInfo &other); + ~ErrorInfo(); + + void appendBacktrace(const QString &description, const CodeLocation &location = CodeLocation()); + void append(const QString &description, const CodeLocation &location = CodeLocation()); + void prepend(const QString &description, const CodeLocation &location = CodeLocation()); + QList items() const; + bool hasError() const { return !items().isEmpty(); } + void clear(); + QString toString() const; + bool isInternalError() const; + + void load(Internal::PersistentPool &pool); + void store(Internal::PersistentPool &pool) const; + +private: + class ErrorInfoPrivate; + QSharedDataPointer d; +}; + +inline uint qHash(const ErrorInfo &e) { return qHash(e.toString()); } +inline bool operator==(const ErrorInfo &e1, const ErrorInfo &e2) { + return e1.toString() == e2.toString(); +} + +} // namespace qbs + +Q_DECLARE_METATYPE(qbs::ErrorInfo) + +#endif // QBS_ERROR diff --git a/src/lib/corelib/tools/executablefinder.cpp b/src/lib/corelib/tools/executablefinder.cpp new file mode 100644 index 00000000..0cde9781 --- /dev/null +++ b/src/lib/corelib/tools/executablefinder.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "executablefinder.h" + +#include "fileinfo.h" +#include "hostosinfo.h" + +#include + +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; +} + +QStringList ExecutableFinder::m_executableSuffixes = populateExecutableSuffixes(); + +ExecutableFinder::ExecutableFinder(const ResolvedProductPtr &m_product, + const QProcessEnvironment &env, const Logger &logger) + : m_product(m_product) + , m_environment(env) + , m_logger(logger) +{ +} + +QString ExecutableFinder::findExecutable(const QString &path, const QString &workingDirPath) +{ + QString filePath = QDir::fromNativeSeparators(path); + //if (FileInfo::fileName(filePath) == filePath) + if (!FileInfo::isAbsolute(filePath)) + return findInPath(filePath, workingDirPath); + else if (HostOsInfo::isWindowsHost()) + return findBySuffix(filePath); + return filePath; +} + +QString ExecutableFinder::findBySuffix(const QString &filePath) const +{ + QString fullProgramPath = cachedFilePath(filePath); + if (!fullProgramPath.isEmpty()) + return fullProgramPath; + + fullProgramPath = filePath; + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[EXEC] looking for executable by suffix " << fullProgramPath; + const QString emptyDirectory; + candidateCheck(emptyDirectory, fullProgramPath, fullProgramPath); + cacheFilePath(filePath, fullProgramPath); + return fullProgramPath; + +} + +bool ExecutableFinder::candidateCheck(const QString &directory, const QString &program, + QString &fullProgramPath) const +{ + for (int i = 0; i < m_executableSuffixes.count(); ++i) { + QString candidate = directory + program + m_executableSuffixes.at(i); + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[EXEC] candidate: " << candidate; + QFileInfo fi(candidate); + if (fi.isFile() && fi.isExecutable()) { + fullProgramPath = candidate; + return true; + } + } + return false; +} + +QString ExecutableFinder::findInPath(const QString &filePath, const QString &workingDirPath) const +{ + QString fullProgramPath = cachedFilePath(filePath); + if (!fullProgramPath.isEmpty()) + return fullProgramPath; + + fullProgramPath = filePath; + if (m_logger.traceEnabled()) + m_logger.qbsTrace() << "[EXEC] looking for executable in PATH " << fullProgramPath; + QStringList pathEnv = m_environment.value(QLatin1String("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 = workingDirPath; + if (!directory.isEmpty()) { + const QChar lastChar = directory.at(directory.count() - 1); + if (lastChar != QLatin1Char('/') && lastChar != QLatin1Char('\\')) + directory.append(QLatin1Char('/')); + } + if (candidateCheck(directory, fullProgramPath, fullProgramPath)) + break; + } + cacheFilePath(filePath, fullProgramPath); + return fullProgramPath; +} + +QString ExecutableFinder::cachedFilePath(const QString &filePath) const +{ + return m_product ? m_product->cachedExecutablePath(filePath) : QString(); +} + +void ExecutableFinder::cacheFilePath(const QString &filePath, const QString &fullFilePath) const +{ + if (m_product) + m_product->cacheExecutablePath(filePath, fullFilePath); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/executablefinder.h b/src/lib/corelib/tools/executablefinder.h new file mode 100644 index 00000000..816d4d66 --- /dev/null +++ b/src/lib/corelib/tools/executablefinder.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_EXECUTABLEFINDER_H +#define QBS_EXECUTABLEFINDER_H + +#include +#include + +#include + +namespace qbs { +namespace Internal { + +/*! + * \brief Helper class for finding an executable in the PATH of the build environment. + */ +class ExecutableFinder +{ +public: + ExecutableFinder(const ResolvedProductPtr &product, const QProcessEnvironment &env, + const Logger &logger); + + QString findExecutable(const QString &path, const QString &workingDirPath); + +private: + static QStringList m_executableSuffixes; + QString findBySuffix(const QString &filePath) const; + bool candidateCheck(const QString &directory, const QString &program, + QString &fullProgramPath) const; + QString findInPath(const QString &filePath, const QString &workingDirPath) const; + + QString cachedFilePath(const QString &filePath) const; + void cacheFilePath(const QString &filePaht, const QString &filePath) const; + + ResolvedProductPtr m_product; + const QProcessEnvironment &m_environment; + Logger m_logger; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_EXECUTABLEFINDER_H diff --git a/src/lib/corelib/tools/fileinfo.cpp b/src/lib/corelib/tools/fileinfo.cpp new file mode 100644 index 00000000..a41d6887 --- /dev/null +++ b/src/lib/corelib/tools/fileinfo.cpp @@ -0,0 +1,525 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fileinfo.h" + +#include +#include + +#include +#include +#include +#include + +#if defined(Q_OS_UNIX) +#include +#include +#include +#elif defined(Q_OS_WIN) +#include +#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 fn; + return fn.mid(0, dot); +} + +QString FileInfo::completeBaseName(const QString &fp) +{ + QString fn = fileName(fp); + int dot = fn.lastIndexOf(QLatin1Char('.')); + if (dot < 0) + return fn; + return fn.mid(0, dot); +} + +QString FileInfo::path(const QString &fp, HostOsInfo::HostOs hostOs) +{ + if (fp.isEmpty()) + return QString(); + int last = fp.lastIndexOf(QLatin1Char('/')); + if (last < 0) + return QLatin1String("."); + QString p = QDir::cleanPath(fp.mid(0, last)); + if (p.isEmpty() || (hostOs == HostOsInfo::HostOsWindows && p.length() == 2 && p.at(0).isLetter() + && p.at(1) == QLatin1Char(':'))) { + // Make sure we don't return Windows drive roots without an ending slash. + // Those paths are considered relative. + p.append(QLatin1Char('/')); + } + return p; +} + +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, HostOsInfo::HostOs hostOs) +{ + const int n = path.size(); + if (n == 0) + return false; + const QChar at0 = path.at(0); + if (at0 == QLatin1Char('/')) + return true; + if (hostOs == HostOsInfo::HostOsWindows) { + 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), qDebug("base: %s, rel: %s", qPrintable(base), qPrintable(rel)); + 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); + } + if (s == QLatin1String("..")) { + int idx = r.lastIndexOf(QLatin1Char('/')); + if (idx >= 0) + r.truncate(idx); + return r; + } + + r.reserve(r.length() + 1 + s.length()); + r += QLatin1Char('/'); + r += s; + return r; +} + +bool FileInfo::globMatches(const QRegExp ®exp, 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); +} + +#ifdef Q_OS_WIN +static const QString win32LongPathPrefix = QStringLiteral("\\\\?\\"); +#endif + +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 = win32LongPathPrefix + QDir::toNativeSeparators(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.fileName() == fileName(fi.canonicalFilePath()); +#else + Q_UNUSED(filePath) + return true; +#endif +} + +bool FileInfo::fileExists(const QFileInfo &fi) +{ + return fi.isSymLink() || fi.exists(); +} + +#if defined(Q_OS_WIN) + +#define z(x) reinterpret_cast(const_cast(&x)) + +template struct CompileTimeAssert; +template<> struct CompileTimeAssert {}; + +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); + + QString filePath = fileName; + + // The extended-length path prefix cannot be used with a relative path, so make it absolute + if (!isAbsolute(filePath)) + filePath = QDir::currentPath() + QDir::separator() + filePath; + + filePath = win32LongPathPrefix + QDir::toNativeSeparators(QDir::cleanPath(filePath)); + if (!GetFileAttributesEx(reinterpret_cast(filePath.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(&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 (!FileInfo::fileExists(f)) + return true; + if (f.isDir() && !f.isSymLink()) { + const QDir dir(f.absoluteFilePath()); + + // QDir::System is needed for broken symlinks. + foreach (const QFileInfo &fi, dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot + | QDir::Hidden | QDir::System)) + 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, bool copyDirectoryContents, 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()) { + if (copyDirectoryContents) { + 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, + copyDirectoryContents, errorMessage)) + return false; + } + } else { + if (tgtFileInfo.exists() && srcFileInfo.lastModified() <= tgtFileInfo.lastModified()) + return true; + return QDir::root().mkpath(tgtFilePath); + } + } 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 00000000..da2500ad --- /dev/null +++ b/src/lib/corelib/tools/fileinfo.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_FILEINFO_H +#define QBS_FILEINFO_H + +#include "filetime.h" +#include "hostosinfo.h" +#include "qbs_export.h" + +#if defined(Q_OS_UNIX) +#include +#endif + +#include + +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, HostOsInfo::HostOs hostOs = HostOsInfo::hostOs()); + 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, HostOsInfo::HostOs hostOs = HostOsInfo::hostOs()); + 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); + + // Symlink-correct check. + static bool fileExists(const QFileInfo &fi); + +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, bool copyDirectoryContents, QString *errorMessage); + +} // namespace Internal +} // namespace qbs + +#endif diff --git a/src/lib/corelib/tools/filesaver.cpp b/src/lib/corelib/tools/filesaver.cpp new file mode 100644 index 00000000..4b2967b8 --- /dev/null +++ b/src/lib/corelib/tools/filesaver.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filesaver.h" + +#include +#include + +namespace qbs { +namespace Internal { + +FileSaver::FileSaver(const QString &filePath, bool overwriteIfUnchanged) + : m_filePath(filePath), m_overwriteIfUnchanged(overwriteIfUnchanged) +{ +} + +QIODevice *FileSaver::device() +{ + return m_memoryDevice.data(); +} + +bool FileSaver::open() +{ + if (!m_overwriteIfUnchanged) { + QFile file(m_filePath); + if (file.open(QIODevice::ReadOnly)) + m_oldFileContents = file.readAll(); + else + m_oldFileContents.clear(); + } + + m_newFileContents.clear(); + m_memoryDevice.reset(new QBuffer(&m_newFileContents)); + return m_memoryDevice->open(QIODevice::WriteOnly); +} + +bool FileSaver::commit() +{ + if (!m_overwriteIfUnchanged && m_oldFileContents == m_newFileContents) + return true; // no need to write unchanged data + + QSaveFile saveFile(m_filePath); + if (!saveFile.open(QIODevice::WriteOnly)) + return false; + + saveFile.write(m_newFileContents); + return saveFile.commit(); +} + +qint64 FileSaver::write(const QByteArray &data) +{ + return device()->write(data); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/filesaver.h b/src/lib/corelib/tools/filesaver.h new file mode 100644 index 00000000..5a3eb873 --- /dev/null +++ b/src/lib/corelib/tools/filesaver.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FILESAVER_H +#define FILESAVER_H + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +/*! + * QSaveFile wrapper which doesn't update the target file if the contents are unchanged. + */ +class FileSaver { +public: + FileSaver(const QString &filePath, bool overwriteIfUnchanged = false); + + QIODevice *device(); + bool open(); + bool commit(); + qint64 write(const QByteArray &data); + +private: + QByteArray m_newFileContents; + QByteArray m_oldFileContents; + QScopedPointer m_memoryDevice; + const QString m_filePath; + const bool m_overwriteIfUnchanged; +}; + +} // namespace Internal +} // namespace qbs + +#endif // FILESAVER_H diff --git a/src/lib/corelib/tools/filetime.h b/src/lib/corelib/tools/filetime.h new file mode 100644 index 00000000..2a36b12a --- /dev/null +++ b/src/lib/corelib/tools/filetime.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_FILETIME_H +#define QBS_FILETIME_H + +#include +#include + +#if defined(Q_OS_UNIX) +#include +#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(); + static FileTime oldestTime(); + + 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 00000000..ae0d9861 --- /dev/null +++ b/src/lib/corelib/tools/filetime_unix.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filetime.h" + +#include +#include + +#include + +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); +} + +FileTime FileTime::oldestTime() +{ + return 1; +} + +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 00000000..806bc9c8 --- /dev/null +++ b/src/lib/corelib/tools/filetime_win.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filetime.h" + +#include +#include + +namespace qbs { +namespace Internal { + +template struct CompileTimeAssert; +template<> struct CompileTimeAssert {}; + +FileTime::FileTime() + : m_fileTime(0) +{ + static CompileTimeAssert 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(&m_fileTime); + const FILETIME *const t2 = reinterpret_cast(&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(&result.m_fileTime); + SystemTimeToFileTime(&st, ft); + return result; +} + +FileTime FileTime::oldestTime() +{ + SYSTEMTIME st = { + 1601, + 1, + 5, + 2, + 0, + 0, + 0, + 0 + }; + FileTime result; + FILETIME *const ft = reinterpret_cast(&result.m_fileTime); + SystemTimeToFileTime(&st, ft); + return result; +} + +QString FileTime::toString() const +{ + const FILETIME *const ft = reinterpret_cast(&m_fileTime); + SYSTEMTIME stUTC, stLocal; + FileTimeToSystemTime(ft, &stUTC); + SystemTimeToTzSpecificLocalTime(NULL, &stUTC, &stLocal); + const QString result = QString::fromLatin1("%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; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/generateoptions.cpp b/src/lib/corelib/tools/generateoptions.cpp new file mode 100644 index 00000000..fe6423aa --- /dev/null +++ b/src/lib/corelib/tools/generateoptions.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "generateoptions.h" + +#include +#include + +namespace qbs { +namespace Internal { + +class GenerateOptionsPrivate : public QSharedData +{ +public: + GenerateOptionsPrivate() + : generatorName() + {} + + QString generatorName; +}; + +} // namespace Internal + +/*! + * \class GenerateOptions + * \brief The \c GenerateOptions class comprises parameters that influence the behavior of + * generate operations. + */ + +GenerateOptions::GenerateOptions() : d(new Internal::GenerateOptionsPrivate) +{ +} + +GenerateOptions::GenerateOptions(const GenerateOptions &other) : d(other.d) +{ +} + +GenerateOptions &GenerateOptions::operator=(const GenerateOptions &other) +{ + d = other.d; + return *this; +} + +GenerateOptions::~GenerateOptions() +{ +} + +/*! + * Returns the name of the generator used to create the external build system files. + * The default is empty. + */ +QString GenerateOptions::generatorName() const +{ + return d->generatorName; +} + +/*! + * \brief Sets the name of the generator used to create the external build system files. + */ +void GenerateOptions::setGeneratorName(const QString &generatorName) +{ + d->generatorName = generatorName; +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/generateoptions.h b/src/lib/corelib/tools/generateoptions.h new file mode 100644 index 00000000..b6546b7d --- /dev/null +++ b/src/lib/corelib/tools/generateoptions.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_GENERATEOPTIONS_H +#define QBS_GENERATEOPTIONS_H + +#include "qbs_export.h" + +#include + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { class GenerateOptionsPrivate; } + +class QBS_EXPORT GenerateOptions +{ +public: + GenerateOptions(); + GenerateOptions(const GenerateOptions &other); + GenerateOptions &operator=(const GenerateOptions &other); + ~GenerateOptions(); + + QString generatorName() const; + void setGeneratorName(const QString &generatorName); + +private: + QSharedDataPointer d; +}; + +} // namespace qbs + +#endif // QBS_GENERATEOPTIONS_H diff --git a/src/lib/corelib/tools/hostosinfo.h b/src/lib/corelib/tools/hostosinfo.h new file mode 100644 index 00000000..d3fa344f --- /dev/null +++ b/src/lib/corelib/tools/hostosinfo.h @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_HOSTOSINFO_H +#define QBS_HOSTOSINFO_H + +#include "qbs_export.h" +#include "version.h" + +#include +#include +#include +#include +#include + +#if defined(Q_OS_WIN) +#define QBS_HOST_EXE_SUFFIX ".exe" +#define QBS_HOST_DYNAMICLIB_PREFIX "" +#define QBS_HOST_DYNAMICLIB_SUFFIX ".dll" +#elif defined(Q_OS_DARWIN) +#define QBS_HOST_EXE_SUFFIX "" +#define QBS_HOST_DYNAMICLIB_PREFIX "lib" +#define QBS_HOST_DYNAMICLIB_SUFFIX ".dylib" +#else +#define QBS_HOST_EXE_SUFFIX "" +#define QBS_HOST_DYNAMICLIB_PREFIX "lib" +#define QBS_HOST_DYNAMICLIB_SUFFIX ".so" +#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, HostOsMacos, HostOsOtherUnix, HostOsOther }; + + static inline HostOs hostOs(); + + static inline Version hostOsVersion() { + Version v; + if (HostOsInfo::isWindowsHost()) { + QSettings settings(QString::fromLatin1("HKEY_LOCAL_MACHINE\\Software\\" + "Microsoft\\Windows NT\\CurrentVersion"), + QSettings::NativeFormat); + v = v.fromString(settings.value(QStringLiteral("CurrentVersion")).toString() + + QLatin1Char('.') + + settings.value(QStringLiteral("CurrentBuildNumber")).toString()); + Q_ASSERT(v.isValid()); + } else if (HostOsInfo::isMacosHost()) { + QSettings settings(QStringLiteral("/System/Library/CoreServices/SystemVersion.plist"), + QSettings::NativeFormat); + v = v.fromString(settings.value(QStringLiteral("ProductVersion")).toString()); + Q_ASSERT(v.isValid()); + } + return v; + } + + static bool isWindowsHost() { return hostOs() == HostOsWindows; } + static bool isLinuxHost() { return hostOs() == HostOsLinux; } + static bool isMacosHost() { return hostOs() == HostOsMacos; } + static inline bool isAnyUnixHost(); + static inline QString rfc1034Identifier(const QString &str); + + static QString appendExecutableSuffix(const QString &executable) + { + QString finalName = executable; + if (isWindowsHost()) + finalName += QLatin1String(QBS_HOST_EXE_SUFFIX); + return finalName; + } + + static QString dynamicLibraryName(const QString &libraryBaseName) + { + return QLatin1String(QBS_HOST_DYNAMICLIB_PREFIX) + libraryBaseName + + QLatin1String(QBS_HOST_DYNAMICLIB_SUFFIX); + } + + static Qt::CaseSensitivity fileNameCaseSensitivity() + { + return isWindowsHost() ? Qt::CaseInsensitive: Qt::CaseSensitive; + } + + static QString libraryPathEnvironmentVariable() + { + if (isWindowsHost()) + return QStringLiteral("PATH"); + if (isMacosHost()) + return QStringLiteral("DYLD_LIBRARY_PATH"); + return QStringLiteral("LD_LIBRARY_PATH"); + } + + static QChar pathListSeparator(HostOsInfo::HostOs hostOs = HostOsInfo::hostOs()) + { + return hostOs == HostOsWindows ? QLatin1Char(';') : QLatin1Char(':'); + } + + static QChar pathSeparator(HostOsInfo::HostOs hostOs = HostOsInfo::hostOs()) + { + return hostOs == HostOsWindows ? QLatin1Char('\\') : QLatin1Char('/'); + } + + static Qt::KeyboardModifier controlModifier() + { + return isMacosHost() ? 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 HostOsMacos; +#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::rfc1034Identifier(const QString &str) +{ + QString s = str; + for (int i = 0; i < s.size(); ++i) { + QCharRef ch = s[i]; + const char c = ch.toLatin1(); + + const bool okChar = (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') + || (c >= 'a' && c <= 'z') || c == '-' || c == '.'; + if (!okChar) + ch = QChar::fromLatin1('-'); + } + return s; +} + +} // 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 00000000..17baa3c4 --- /dev/null +++ b/src/lib/corelib/tools/id.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "id.h" +#include "qbsassert.h" + +#include +#include +#include + +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 +{ +#ifndef QBS_ALLOW_STATIC_LEAKS + ~IdCache() + { + for (IdCache::iterator it = begin(); it != end(); ++it) + delete[](const_cast(it.key().str)); + } +#endif +}; + + +static int firstUnusedId = Id::IdsPerPlugin * Id::ReservedPlugins; + +static QHash 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)) +{} + +/*! + 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); +} + +/*! + 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 00000000..5fc75515 --- /dev/null +++ b/src/lib/corelib/tools/id.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_TOOLS_ID_H +#define QBS_TOOLS_ID_H + +#include +#include +#include + +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 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 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) + +#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 00000000..a0ea108e --- /dev/null +++ b/src/lib/corelib/tools/installoptions.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "installoptions.h" +#include "propertyfinder.h" +#include "language/language.h" + +#include +#include + +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; +}; + +QString effectiveInstallRoot(const InstallOptions &options, const TopLevelProject *project) +{ + const QString installRoot = options.installRoot(); + if (!installRoot.isEmpty()) + return installRoot; + + QVariantMap configForPropertyFinder; + configForPropertyFinder.insert(QLatin1String("modules"), project->buildConfiguration()); + if (options.installIntoSysroot()) { + return PropertyFinder().propertyValue(configForPropertyFinder, + QLatin1String("qbs"), QLatin1String("sysroot")).toString(); + } + return PropertyFinder().propertyValue(configForPropertyFinder, + QLatin1String("qbs"), QLatin1String("installRoot")).toString(); +} + +} // 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 "/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 00000000..cb463e2e --- /dev/null +++ b/src/lib/corelib/tools/installoptions.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_INSTALLOPTIONS_H +#define QBS_INSTALLOPTIONS_H + +#include "qbs_export.h" + +#include +#include + +namespace qbs { +class InstallOptions; +namespace Internal { +class InstallOptionsPrivate; +class TopLevelProject; +QString effectiveInstallRoot(const InstallOptions &options, const TopLevelProject *project); +} + +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 d; +}; + +} // namespace qbs + +#endif // QBS_INSTALLOPTIONS_H diff --git a/src/lib/corelib/tools/jsliterals.cpp b/src/lib/corelib/tools/jsliterals.cpp new file mode 100644 index 00000000..bb29cf4f --- /dev/null +++ b/src/lib/corelib/tools/jsliterals.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "jsliterals.h" + +namespace qbs { + +QString toJSLiteral(const bool b) +{ + return b ? QLatin1String("true") : QLatin1String("false"); +} + +QString toJSLiteral(const QString &str) +{ + QString js = str; + js.replace(QRegExp(QLatin1String("([\\\\\"])")), QLatin1String("\\\\1")); + js.prepend(QLatin1Char('"')); + js.append(QLatin1Char('"')); + return js; +} + +QString toJSLiteral(const QStringList &strs) +{ + QString js = QLatin1String("["); + for (int i = 0; i < strs.count(); ++i) { + if (i != 0) + js.append(QLatin1String(", ")); + js.append(toJSLiteral(strs.at(i))); + } + js.append(QLatin1Char(']')); + return js; +} + +QString toJSLiteral(const QVariant &val) +{ + if (!val.isValid()) + return QLatin1String("undefined"); + if (val.type() == QVariant::List || val.type() == QVariant::StringList) { + QString res; + foreach (const QVariant &child, val.toList()) { + if (res.length()) res.append(QLatin1String(", ")); + res.append(toJSLiteral(child)); + } + res.prepend(QLatin1Char('[')); + res.append(QLatin1Char(']')); + return res; + } + if (val.type() == QVariant::Map) { + const QVariantMap &vm = val.toMap(); + QString str = QLatin1String("{"); + for (QVariantMap::const_iterator it = vm.begin(); it != vm.end(); ++it) { + if (it != vm.begin()) + str += QLatin1Char(','); + str += toJSLiteral(it.key()) + QLatin1Char(':') + toJSLiteral(it.value()); + } + str += QLatin1Char('}'); + return str; + } + if (val.type() == QVariant::Bool) + return val.toBool() ? QLatin1String("true") : QLatin1String("false"); + if (val.canConvert(QVariant::String)) + return toJSLiteral(val.toString()); + return QString::fromLatin1("Unconvertible type %1").arg(QLatin1String(val.typeName())); +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/jsliterals.h b/src/lib/corelib/tools/jsliterals.h new file mode 100644 index 00000000..6fb7518b --- /dev/null +++ b/src/lib/corelib/tools/jsliterals.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_JSLITERALS_H +#define QBS_JSLITERALS_H + +#include "qbs_export.h" + +#include +#include +#include + +namespace qbs { + +QBS_EXPORT QString toJSLiteral(const bool b); +QBS_EXPORT QString toJSLiteral(const QString &str); +QBS_EXPORT QString toJSLiteral(const QStringList &strs); +QBS_EXPORT QString toJSLiteral(const QVariant &val); + +} // namespace qbs + +#endif // QBS_JSLITERALS_H diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp new file mode 100644 index 00000000..a56f6b0c --- /dev/null +++ b/src/lib/corelib/tools/msvcinfo.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "msvcinfo.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#endif + +#include + +using namespace qbs; +using namespace qbs::Internal; + +static QString mkStr(const char *s) { return QString::fromLocal8Bit(s); } +static QString mkStr(const QByteArray &ba) { return mkStr(ba.constData()); } + +class TemporaryEnvChanger +{ +public: + TemporaryEnvChanger(const QProcessEnvironment &envChanges) + { + QProcessEnvironment currentEnv = QProcessEnvironment::systemEnvironment(); + foreach (const QString &key, envChanges.keys()) { + m_changesToRestore.insert(key, currentEnv.value(key)); + qputenv(qPrintable(key), qPrintable(envChanges.value(key))); + } + } + + ~TemporaryEnvChanger() + { + foreach (const QString &key, m_changesToRestore.keys()) + qputenv(qPrintable(key), qPrintable(m_changesToRestore.value(key))); + } + +private: + QProcessEnvironment m_changesToRestore; +}; + +static QByteArray runProcess(const QString &exeFilePath, const QStringList &args, + const QProcessEnvironment &env = QProcessEnvironment(), + bool allowFailure = false, + const QByteArray &pipeData = QByteArray()) +{ + TemporaryEnvChanger envChanger(env); + QProcess process; + process.start(exeFilePath, args); + if (!process.waitForStarted()) + throw ErrorInfo(mkStr("Could not start %1 (%2)").arg(exeFilePath, process.errorString())); + if (!pipeData.isEmpty()) { + process.write(pipeData); + process.closeWriteChannel(); + } + if (!process.waitForFinished() || process.exitStatus() != QProcess::NormalExit) + throw ErrorInfo(mkStr("Could not run %1 (%2)").arg(exeFilePath, process.errorString())); + if (process.exitCode() != 0 && !allowFailure) { + ErrorInfo e(mkStr("Process '%1' failed with exit code %2.") + .arg(exeFilePath).arg(process.exitCode())); + const QByteArray stdErr = process.readAllStandardError(); + if (!stdErr.isEmpty()) + e.append(mkStr("stderr was: %1").arg(mkStr(stdErr))); + const QByteArray stdOut = process.readAllStandardOutput(); + if (!stdOut.isEmpty()) + e.append(mkStr("stdout was: %1").arg(mkStr(stdOut))); + throw e; + } + return process.readAllStandardOutput().trimmed(); +} + +class DummyFile { +public: + DummyFile(const QString &fp) : filePath(fp) { } + ~DummyFile() { QFile::remove(filePath); } + const QString filePath; +}; + +#ifdef Q_OS_WIN +static QStringList parseCommandLine(const QString &commandLine) +{ + QStringList list; + wchar_t *buf = new wchar_t[commandLine.size() + 1]; + buf[commandLine.toWCharArray(buf)] = 0; + int argCount = 0; + LPWSTR *args = CommandLineToArgvW(buf, &argCount); + if (!args) + throw ErrorInfo(mkStr("Could not parse command line arguments: ") + commandLine); + for (int i = 0; i < argCount; ++i) + list.append(QString::fromWCharArray(args[i])); + delete[] buf; + return list; +} +#endif + +static QVariantMap getMsvcDefines(const QString &compilerFilePath, + const QProcessEnvironment &compilerEnv) +{ +#ifdef Q_OS_WIN + const QByteArray commands("set MSC_CMD_FLAGS\n"); + QStringList out = QString::fromLocal8Bit(runProcess(compilerFilePath, QStringList() + << QStringLiteral("/nologo") + << QStringLiteral("/B1") + << QString::fromWCharArray(_wgetenv(L"COMSPEC")) + << QStringLiteral("/c") + << QStringLiteral("/TC") + << QStringLiteral("NUL"), + compilerEnv, true, commands)).split(QLatin1Char('\n')); + + auto findResult = std::find_if(out.cbegin(), out.cend(), [] (const QString &line) { + return line.startsWith(QLatin1String("MSC_CMD_FLAGS=")); + }); + if (findResult == out.cend()) { + throw ErrorInfo(QStringLiteral("Unexpected compiler frontend output: ") + + out.join(QLatin1Char('\n'))); + } + + QVariantMap map; + const QStringList args = parseCommandLine(findResult->trimmed()); + for (const QString &arg : args) { + if (!arg.startsWith(QStringLiteral("-D"))) + continue; + int idx = arg.indexOf(QLatin1Char('='), 2); + if (idx > 2) + map.insert(arg.mid(2, idx - 2), arg.mid(idx + 1)); + else + map.insert(arg.mid(2), QVariant()); + } + + return map; +#else + Q_UNUSED(compilerFilePath); + Q_UNUSED(compilerEnv); + return QVariantMap(); +#endif +} + +void MSVC::init() +{ + determineCompilerVersion(); +} + +QString MSVC::binPathForArchitecture(const QString &arch) const +{ + QString archSubDir; + if (arch != QStringLiteral("x86")) + archSubDir = arch; + return QDir::cleanPath(vcInstallPath + QLatin1Char('/') + pathPrefix + QLatin1Char('/') + + archSubDir); +} + +QString MSVC::clPathForArchitecture(const QString &arch) const +{ + return binPathForArchitecture(arch) + QLatin1String("/cl.exe"); +} + +QVariantMap MSVC::compilerDefines(const QString &compilerFilePath) const +{ + return getMsvcDefines(compilerFilePath, environment); +} + +void MSVC::determineCompilerVersion() +{ + QString cppFilePath; + { + QTemporaryFile cppFile(QDir::tempPath() + QLatin1String("/qbsXXXXXX.cpp")); + cppFile.setAutoRemove(false); + if (!cppFile.open()) { + throw ErrorInfo(mkStr("Could not create temporary file (%1)") + .arg(cppFile.errorString())); + } + cppFilePath = cppFile.fileName(); + cppFile.write("_MSC_FULL_VER"); + cppFile.close(); + } + DummyFile fileDeleter(cppFilePath); + + const QByteArray origPath = qgetenv("PATH"); + qputenv("PATH", environment.value(QStringLiteral("PATH")).toLatin1() + ';' + origPath); + QByteArray versionStr = runProcess( + binPath + QStringLiteral("/cl.exe"), + QStringList() << QStringLiteral("/nologo") << QStringLiteral("/EP") + << QDir::toNativeSeparators(cppFilePath)); + qputenv("PATH", origPath); + compilerVersion = Version(versionStr.mid(0, 2).toInt(), versionStr.mid(2, 2).toInt(), + versionStr.mid(4).toInt()); +} diff --git a/src/lib/corelib/tools/msvcinfo.h b/src/lib/corelib/tools/msvcinfo.h new file mode 100644 index 00000000..61c70fc4 --- /dev/null +++ b/src/lib/corelib/tools/msvcinfo.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_MSVCINFO_H +#define QBS_MSVCINFO_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +/** + * Represents one MSVC installation for one specific target architecture. + * There are potentially multiple MSVCs in one Visual Studio installation. + */ +class MSVC +{ +public: + QString version; + Version internalVsVersion; + Version compilerVersion; + QString vsInstallPath; + QString vcInstallPath; + QString binPath; + QString pathPrefix; + QString architecture; + QProcessEnvironment environment; + + MSVC() { } + + MSVC(const QString &clPath) + { + QDir parentDir = QFileInfo(clPath).dir(); + QString parentDirName = parentDir.dirName().toLower(); + if (parentDirName == QLatin1String("bin")) + parentDirName = QStringLiteral("x86"); + else + parentDir.cdUp(); + architecture = parentDirName; + vcInstallPath = parentDir.path(); + binPath = vcInstallPath; + } + + QBS_EXPORT void init(); + QBS_EXPORT QString binPathForArchitecture(const QString &arch) const; + QBS_EXPORT QString clPathForArchitecture(const QString &arch) const; + QBS_EXPORT QVariantMap compilerDefines(const QString &compilerFilePath) const; + +private: + void determineCompilerVersion(); +}; + +class WinSDK : public MSVC +{ +public: + bool isDefault; + + WinSDK() + { + pathPrefix = QLatin1String("bin"); + } +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_MSVCINFO_H diff --git a/src/lib/corelib/tools/pathutils.h b/src/lib/corelib/tools/pathutils.h new file mode 100644 index 00000000..a2fad9aa --- /dev/null +++ b/src/lib/corelib/tools/pathutils.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PATHUTILS_H +#define QBS_PATHUTILS_H + +#include "hostosinfo.h" +#include "qbs_export.h" + +namespace qbs { +namespace Internal { + +class QBS_EXPORT PathUtils +{ +public: + static QString toNativeSeparators(const QString &s, + HostOsInfo::HostOs os = HostOsInfo::hostOs()) + { + QString value = s; + if (os == HostOsInfo::HostOsWindows) + value.replace(QLatin1Char('/'), HostOsInfo::pathSeparator(os)); + return value; + } +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_PATHUTILS_H diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp new file mode 100644 index 00000000..c620e087 --- /dev/null +++ b/src/lib/corelib/tools/persistence.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "persistence.h" + +#include "fileinfo.h" +#include +#include +#include + +#include +#include + +namespace qbs { +namespace Internal { + +static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-93"; + +PersistentPool::PersistentPool(Logger &logger) : m_logger(logger) +{ + m_stream.setVersion(QDataStream::Qt_4_8); +} + +PersistentPool::~PersistentPool() +{ + closeStream(); +} + +void PersistentPool::load(const QString &filePath) +{ + QScopedPointer 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 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(qstrlen(QBS_PERSISTENCE_MAGIC), 0) << m_headData.projectConfig; + m_lastStoredObjectId = 0; + m_lastStoredStringId = 0; +} + +void PersistentPool::finalizeWriteStream() +{ + if (m_stream.status() != QDataStream::Ok) + throw ErrorInfo(Tr::tr("Failure serializing build graph.")); + m_stream.device()->seek(0); + m_stream << QByteArray(QBS_PERSISTENCE_MAGIC); + if (m_stream.status() != QDataStream::Ok) + throw ErrorInfo(Tr::tr("Failure serializing build graph.")); + QFile * const file = static_cast(m_stream.device()); + if (!file->flush()) { + file->close(); + file->remove(); + throw ErrorInfo(Tr::tr("Failure serializing build graph: %1").arg(file->errorString())); + } +} + +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::store(const QVariantMap &map) +{ + m_stream << map.count(); + for (QVariantMap::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) { + storeString(it.key()); + store(it.value()); + } +} + +QVariantMap PersistentPool::loadVariantMap() +{ + int count; + m_stream >> count; + QVariantMap map; + for (int i = 0; i < count; ++i) { + const QString key = idLoadString(); + const QVariant value = loadVariant(); + map.insert(key, value); + } + return map; +} + +void PersistentPool::store(const QVariant &variant) +{ + const quint32 type = static_cast(variant.type()); + m_stream << type; + switch (type) { + case QMetaType::QString: + storeString(variant.toString()); + break; + case QMetaType::QStringList: + storeStringList(variant.toStringList()); + break; + case QMetaType::QVariantList: + storeContainer(variant.toList()); + break; + case QMetaType::QVariantMap: + store(variant.toMap()); + break; + default: + m_stream << variant; + } +} + +QVariant PersistentPool::loadVariant() +{ + quint32 type; + m_stream >> type; + QVariant value; + switch (type) { + case QMetaType::QString: + value = idLoadString(); + break; + case QMetaType::QStringList: + value = idLoadStringList(); + break; + case QMetaType::QVariantList: { + QVariantList l; + int count; + m_stream >> count; + for (int i = 0; i < count; ++i) + l << loadVariant(); + value = l; + break; + } + case QMetaType::QVariantMap: + value = loadVariantMap(); + break; + default: + m_stream >> value; + } + return value; +} + +void PersistentPool::clear() +{ + m_loaded.clear(); + m_storageIndices.clear(); + m_stringStorage.clear(); + m_inverseStringStorage.clear(); +} + +QDataStream &PersistentPool::stream() +{ + return m_stream; +} + +const int StringNotFoundId = -1; +const int NullStringId = -2; + +void PersistentPool::storeString(const QString &t) +{ + if (t.isNull()) { + m_stream << NullStringId; + return; + } + + int id = m_inverseStringStorage.value(t, StringNotFoundId); + if (id < 0) { + id = m_lastStoredStringId++; + m_inverseStringStorage.insert(t, id); + m_stream << id << t; + } else { + m_stream << id; + } +} + +QString PersistentPool::loadString(int id) +{ + if (id == NullStringId) + return QString(); + + 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 &t) +{ + m_stream << t.count(); + foreach (const QString &s, t) + storeString(s); +} + +QSet PersistentPool::idLoadStringSet() +{ + int i; + m_stream >> i; + QSet 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 00000000..70e94a87 --- /dev/null +++ b/src/lib/corelib/tools/persistence.h @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PERSISTENCE +#define QBS_PERSISTENCE + +#include "persistentobject.h" +#include + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +class PersistentPool +{ +public: + PersistentPool(Logger &logger); + ~PersistentPool(); + + class HeadData + { + public: + QVariantMap projectConfig; + }; + + void load(const QString &filePath); + void setupWriteStream(const QString &filePath); + void finalizeWriteStream(); + void closeStream(); + void clear(); + QDataStream &stream(); + + template T *idLoad(); + template void loadContainer(T &container); + template QSharedPointer idLoadS(); + template void loadContainerS(T &container); + + void store(const QSharedPointer &ptr) { store(ptr.data()); } + void store(const PersistentObject *object); + template void storeContainer(const T &container); + + void store(const QVariantMap &map); + QVariantMap loadVariantMap(); + + void store(const QVariant &variant); + QVariant loadVariant(); + + void storeString(const QString &t); + QString loadString(int id); + QString idLoadString(); + + void storeStringSet(const QSet &t); + QSet loadStringSet(const QList &id); + QSet idLoadStringSet(); + + void storeStringList(const QStringList &t); + QStringList loadStringList(const QList &ids); + QStringList idLoadStringList(); + + const HeadData &headData() const { return m_headData; } + void setHeadData(const HeadData &hd) { m_headData = hd; } + +private: + typedef int PersistentObjectId; + + template struct RemovePointer { typedef T Type; }; + template struct RemovePointer { typedef T Type; }; + template struct RemoveConst { typedef T Type; }; + template struct RemoveConst { typedef T Type; }; + + template T *loadRaw(PersistentObjectId id); + template QSharedPointer load(PersistentObjectId id); + + QDataStream m_stream; + HeadData m_headData; + QVector m_loadedRaw; + QVector > m_loaded; + QHash m_storageIndices; + PersistentObjectId m_lastStoredObjectId; + + QVector m_stringStorage; + QHash m_inverseStringStorage; + PersistentObjectId m_lastStoredStringId; + Logger &m_logger; +}; + +template inline T *PersistentPool::idLoad() +{ + PersistentObjectId id; + stream() >> id; + return loadRaw(id); +} + +template inline void PersistentPool::loadContainer(T &container) +{ + int count; + stream() >> count; + container.clear(); + container.reserve(count); + for (int i = count; --i >= 0;) + container += idLoad::Type>(); +} + +template inline QSharedPointer PersistentPool::idLoadS() +{ + PersistentObjectId id; + m_stream >> id; + return load(id); +} + +template inline void PersistentPool::loadContainerS(T &container) +{ + int count; + stream() >> count; + container.clear(); + container.reserve(count); + for (int i = count; --i >= 0;) + container += idLoadS::Type>(); +} + +template inline void PersistentPool::storeContainer(const 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 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(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 inline QSharedPointer PersistentPool::load(PersistentObjectId id) +{ + if (id < 0) + return QSharedPointer(); + + if (id < m_loaded.count()) { + QSharedPointer obj = m_loaded.value(id); + return obj.dynamicCast(); + } + + m_loaded.resize(id + 1); + const QSharedPointer 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 00000000..0955e4da --- /dev/null +++ b/src/lib/corelib/tools/persistentobject.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#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 00000000..a21dbdaf --- /dev/null +++ b/src/lib/corelib/tools/preferences.cpp @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#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 default echo mode used by Qbs if none is specified. + */ +CommandEchoMode Preferences::defaultEchoMode() const +{ + return commandEchoModeFromName(getPreference(QLatin1String("defaultEchoMode")).toString()); +} + +/*! + * \brief Returns the list of paths where qbs looks for modules and imports. + * In addition to user-supplied locations, they will also be looked up at \c{baseDir}/share/qbs. + */ +QStringList Preferences::searchPaths(const QString &baseDir) const +{ + return pathList(QLatin1String("qbsSearchPaths"), baseDir + QLatin1String("/share/qbs")); +} + +/*! + * \brief Returns the list of paths where qbs looks for plugins. + * In addition to user-supplied locations, they will be looked up at \c{baseDir}/qbs/plugins. + */ +QStringList Preferences::pluginPaths(const QString &baseDir) const +{ + return pathList(QLatin1String("pluginsPath"), baseDir + QLatin1String("/qbs/plugins")); +} + +QVariant Preferences::getPreference(const QString &key, const QVariant &defaultValue) const +{ + const QString fullKey = QLatin1String("preferences.") + key; + if (!m_profile.isEmpty()) { + QVariant value = Profile(m_profile, m_settings).value(fullKey); + if (value.isValid()) { + if (key == QLatin1String("qbsSearchPaths")) // Merge with top-level value. + value = value.toStringList() + m_settings->value(fullKey).toStringList(); + return value; + } + } + + return m_settings->value(fullKey, defaultValue); +} + +QStringList Preferences::pathList(const QString &key, const QString &defaultValue) const +{ + QStringList paths = getPreference(key).toStringList(); + 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 00000000..031218da --- /dev/null +++ b/src/lib/corelib/tools/preferences.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PREFERENCES_H +#define QBS_PREFERENCES_H + +#include "qbs_export.h" + +#include "commandechomode.h" + +#include +#include + +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; + CommandEchoMode defaultEchoMode() const; + QStringList searchPaths(const QString &baseDir = QString()) const; + QStringList pluginPaths(const QString &baseDir = 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 00000000..12e45b25 --- /dev/null +++ b/src/lib/corelib/tools/processresult.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#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 error status of the process. If no error occurred, the value + * is \c Process::UnknownError. + */ +QProcess::ProcessError ProcessResult::error() const +{ + return d->error; +} + +/*! + * \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 00000000..3f7af423 --- /dev/null +++ b/src/lib/corelib/tools/processresult.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROCESSRESULT_H +#define QBS_PROCESSRESULT_H + +#include "qbs_export.h" + +#include +#include +#include +#include +#include + +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::ProcessError error() const; + int exitCode() const; + QStringList stdOut() const; + QStringList stdErr() const; + +private: + QExplicitlySharedDataPointer 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 00000000..37966029 --- /dev/null +++ b/src/lib/corelib/tools/processresult_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROCESSRESULT_P_H +#define QBS_PROCESSRESULT_P_H + +#include +#include +#include + +namespace qbs { +namespace Internal { +class ProcessResultPrivate : public QSharedData +{ +public: + bool success; + + QString executableFilePath; + QStringList arguments; + QString workingDirectory; + + QProcess::ProcessError error; + int exitCode; + QStringList stdOut; + QStringList stdErr; +}; + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/tools/processutils.cpp b/src/lib/corelib/tools/processutils.cpp new file mode 100644 index 00000000..2f8254ab --- /dev/null +++ b/src/lib/corelib/tools/processutils.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "processutils.h" + +#if defined(Q_OS_WIN) +# define PSAPI_VERSION 1 // To use GetModuleFileNameEx from Psapi.lib on all Win versions. +# include +# include +#elif defined(Q_OS_DARWIN) +# include +#elif defined(Q_OS_LINUX) +# include "fileinfo.h" +# include +# include +#elif defined(Q_OS_BSD4) +# include +# include +# include +# include +# if !defined(Q_OS_NETBSD) +# include +# endif +#else +# error Missing implementation of processNameByPid for this platform. +#endif + +namespace qbs { +namespace Internal { + +QString processNameByPid(qint64 pid) +{ +#if defined(Q_OS_WIN) + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, DWORD(pid)); + if (!hProcess) + return QString(); + wchar_t buf[UNICODE_STRING_MAX_CHARS]; + const DWORD length = GetModuleFileNameEx(hProcess, NULL, buf, sizeof(buf) / sizeof(wchar_t)); + CloseHandle(hProcess); + if (!length) + return QString(); + QString name = QString::fromWCharArray(buf, length); + int i = name.lastIndexOf(QLatin1Char('\\')); + if (i >= 0) + name.remove(0, i + 1); + i = name.lastIndexOf(QLatin1Char('.')); + if (i >= 0) + name.truncate(i); + return name; +#elif defined(Q_OS_DARWIN) + char name[1024]; + proc_name(pid, name, sizeof(name) / sizeof(char)); + return QString::fromUtf8(name); +#elif defined(Q_OS_LINUX) + char exePath[64]; + char buf[PATH_MAX]; + memset(buf, 0, sizeof(buf)); + sprintf(exePath, "/proc/%lld/exe", pid); + readlink(exePath, buf, sizeof(buf)); + return FileInfo::fileName(QString::fromUtf8(buf)); +#elif defined(Q_OS_BSD4) +# if defined(Q_OS_NETBSD) + struct kinfo_proc2 kp; + int mib[6] = { CTL_KERN, KERN_PROC2, KERN_PROC_PID, (int)pid, sizeof(struct kinfo_proc2), 1 }; +# elif defined(Q_OS_OPENBSD) + struct kinfo_proc kp; + int mib[6] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid, sizeof(struct kinfo_proc), 1 }; +# else + struct kinfo_proc kp; + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)pid }; +# endif + size_t len = sizeof(kp); + u_int mib_len = sizeof(mib)/sizeof(u_int); + + if (sysctl(mib, mib_len, &kp, &len, NULL, 0) < 0) + return QString(); + +# if defined(Q_OS_OPENBSD) || defined(Q_OS_NETBSD) + if (kp.p_pid != pid) + return QString(); + QString name = QFile::decodeName(kp.p_comm); +# else + if (kp.ki_pid != pid) + return QString(); + QString name = QFile::decodeName(kp.ki_comm); +# endif + return name; + +#else + Q_UNUSED(pid); + return QString(); +#endif +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/processutils.h b/src/lib/corelib/tools/processutils.h new file mode 100644 index 00000000..6911e804 --- /dev/null +++ b/src/lib/corelib/tools/processutils.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PROCESSUTILS_H +#define QBS_PROCESSUTILS_H + +#include +#include + +namespace qbs { +namespace Internal { + +QString processNameByPid(qint64 pid); + +} // namespace Internal +} // namespace qbs + +#endif // QBS_PROCESSUTILS_H + diff --git a/src/lib/corelib/tools/profile.cpp b/src/lib/corelib/tools/profile.cpp new file mode 100644 index 00000000..37c1dc64 --- /dev/null +++ b/src/lib/corelib/tools/profile.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "profile.h" +#include "qbsassert.h" +#include "settings.h" + +#include +#include + +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, ErrorInfo *error) const +{ + try { + return possiblyInheritedValue(key, defaultValue, QStringList()); + } catch (const ErrorInfo &e) { + if (error) + *error = e; + return QVariant(); + } +} + +/*! + * \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, ErrorInfo *error) const +{ + try { + return allKeysInternal(selection, QStringList()); + } catch (const ErrorInfo &e) { + if (error) + *error = e; + return 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 00000000..9a384d3a --- /dev/null +++ b/src/lib/corelib/tools/profile.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROFILE_H +#define QBS_PROFILE_H + +#include "qbs_export.h" + +#include +#include +#include + +namespace qbs { +class ErrorInfo; +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(), + ErrorInfo *error = 0) 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, ErrorInfo *error = 0) 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 Internal { +// Exported for autotests. +class QBS_EXPORT TemporaryProfile { +public: + TemporaryProfile(const QString &name, Settings *settings) : p(name, settings) {} + ~TemporaryProfile() { p.removeProfile(); } + + Profile p; +}; +} // namespace Internal + +} // namespace qbs + +#endif // Header guard diff --git a/src/lib/corelib/tools/profiling.cpp b/src/lib/corelib/tools/profiling.cpp new file mode 100644 index 00000000..5d9e66f7 --- /dev/null +++ b/src/lib/corelib/tools/profiling.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "profiling.h" + +#include +#include + +#include + +namespace qbs { +namespace Internal { + +class TimedActivityLogger::TimedActivityLoggerPrivate +{ +public: + Logger logger; + QString activity; + QElapsedTimer timer; +}; + +TimedActivityLogger::TimedActivityLogger(const Logger &logger, const QString &activity, + bool enabled) + : d(0) +{ + if (!enabled) + return; + d = new TimedActivityLoggerPrivate; + d->logger = logger; + d->activity = activity; + d->logger.qbsLog(LoggerInfo, true) << Tr::tr("Starting activity '%2'.").arg(activity); + d->timer.start(); +} + +void TimedActivityLogger::finishActivity() +{ + if (!d) + return; + const QString timeString = elapsedTimeString(d->timer.elapsed()); + d->logger.qbsLog(LoggerInfo, true) + << Tr::tr("Activity '%2' took %3.").arg(d->activity, timeString); + delete d; + d = 0; +} + +TimedActivityLogger::~TimedActivityLogger() +{ + finishActivity(); +} + +AccumulatingTimer::AccumulatingTimer(qint64 *elapsedTime) : m_elapsedTime(elapsedTime) +{ + if (elapsedTime) + m_timer.start(); +} + +AccumulatingTimer::~AccumulatingTimer() +{ + stop(); +} + +void AccumulatingTimer::stop() +{ + if (!m_timer.isValid()) + return; + *m_elapsedTime += m_timer.elapsed(); + m_timer.invalidate(); +} + +QString elapsedTimeString(qint64 elapsedTimeInMs) +{ + qint64 ms = elapsedTimeInMs; + 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::fromLatin1("%1ms").arg(ms); + if (h || m || s) + timeString.prepend(QString::fromLatin1("%1s, ").arg(s)); + if (h || m) + timeString.prepend(QString::fromLatin1("%1m, ").arg(m)); + if (h) + timeString.prepend(QString::fromLatin1("%1h, ").arg(h)); + return timeString; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/profiling.h b/src/lib/corelib/tools/profiling.h new file mode 100644 index 00000000..f532eac2 --- /dev/null +++ b/src/lib/corelib/tools/profiling.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PROFILING_H +#define QBS_PROFILING_H + +#include + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { +class Logger; + +QString elapsedTimeString(qint64 elapsedTimeInMs); + +class TimedActivityLogger +{ +public: + TimedActivityLogger(const Logger &logger, const QString &activity, bool enabled); + void finishActivity(); + ~TimedActivityLogger(); + +private: + class TimedActivityLoggerPrivate; + TimedActivityLoggerPrivate *d; +}; + +class AccumulatingTimer +{ +public: + AccumulatingTimer(qint64 *elapsedTime); + ~AccumulatingTimer(); + void stop(); + +private: + QElapsedTimer m_timer; + qint64 * const m_elapsedTime; +}; + +} // namespace Internal +} // 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 00000000..49787fd2 --- /dev/null +++ b/src/lib/corelib/tools/progressobserver.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#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 00000000..a4ef0902 --- /dev/null +++ b/src/lib/corelib/tools/progressobserver.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROGRESSOBSERVER_H +#define QBS_PROGRESSOBSERVER_H + +#include + +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/projectgeneratormanager.cpp b/src/lib/corelib/tools/projectgeneratormanager.cpp new file mode 100644 index 00000000..874659d3 --- /dev/null +++ b/src/lib/corelib/tools/projectgeneratormanager.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "projectgeneratormanager.h" + +#include +#include +#include + +#include +#include +#include + +#include "generators/clangcompilationdb/clangcompilationdbgenerator.h" +#include "generators/visualstudio/visualstudiogenerator.h" + +namespace qbs { + +using namespace Internal; + +ProjectGeneratorManager::~ProjectGeneratorManager() +{ + foreach (QLibrary * const lib, m_libs) { + lib->unload(); + delete lib; + } +} + +ProjectGeneratorManager *ProjectGeneratorManager::instance() +{ + static ProjectGeneratorManager generatorPlugin; + return &generatorPlugin; +} + +ProjectGeneratorManager::ProjectGeneratorManager() +{ + QVector > generators; + generators << QSharedPointer::create(); + generators << qbs::VisualStudioGenerator::createGeneratorList(); + foreach (QSharedPointer generator, generators) { + m_generators[generator->generatorName()] = generator; + } +} + +QStringList ProjectGeneratorManager::loadedGeneratorNames() +{ + return instance()->m_generators.keys(); +} + +QSharedPointer ProjectGeneratorManager::findGenerator(const QString &generatorName) +{ + return instance()->m_generators.value(generatorName); +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/projectgeneratormanager.h b/src/lib/corelib/tools/projectgeneratormanager.h new file mode 100644 index 00000000..b61160ca --- /dev/null +++ b/src/lib/corelib/tools/projectgeneratormanager.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PROJECTGENERATORMANAGER_H +#define QBS_PROJECTGENERATORMANAGER_H + +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QLibrary; +QT_END_NAMESPACE + +namespace qbs { +class ProjectGenerator; +namespace Internal { +class Logger; +} + +class QBS_EXPORT ProjectGeneratorManager +{ +public: + ~ProjectGeneratorManager(); + static ProjectGeneratorManager *instance(); + static QStringList loadedGeneratorNames(); + static QSharedPointer findGenerator(const QString &generatorName); + +private: + ProjectGeneratorManager(); + +private: + QList m_libs; + QMap > m_generators; +}; + +} // namespace qbs + +#endif diff --git a/src/lib/corelib/tools/propertyfinder.cpp b/src/lib/corelib/tools/propertyfinder.cpp new file mode 100644 index 00000000..d2c51aa3 --- /dev/null +++ b/src/lib/corelib/tools/propertyfinder.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "propertyfinder.h" + +#include "qbsassert.h" + +#include + +namespace qbs { +namespace Internal { + +QVariant PropertyFinder::propertyValue(const QVariantMap &properties, const QString &moduleName, + const QString &key) +{ + m_moduleName = moduleName; + m_key = key; + m_values.clear(); + findModuleValues(properties); + + return m_values.isEmpty() ? QVariant() : m_values.first(); +} + +void PropertyFinder::findModuleValues(const QVariantMap &properties) +{ + const QVariantMap moduleProperties = properties.value(QLatin1String("modules")).toMap(); + const QVariantMap::const_iterator modIt = moduleProperties.find(m_moduleName); + if (modIt != moduleProperties.constEnd()) { + const QVariantMap moduleMap = modIt->toMap(); + const QVariant property = moduleMap.value(m_key); + addToList(property); + } +} + +void PropertyFinder::addToList(const QVariant &value) +{ + if (!value.isNull() && !m_values.contains(value)) + m_values << value; +} + +} // 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 00000000..8491f97b --- /dev/null +++ b/src/lib/corelib/tools/propertyfinder.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_PROPERTY_FINDER_H +#define QBS_PROPERTY_FINDER_H + +#include +#include + +namespace qbs { +namespace Internal { + +class PropertyFinder +{ +public: + QVariant propertyValue(const QVariantMap &properties, const QString &moduleName, + const QString &key); + +private: + void findModuleValues(const QVariantMap &properties); + void addToList(const QVariant &value); + + 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 00000000..c249601a --- /dev/null +++ b/src/lib/corelib/tools/qbs_export.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_EXPORT_H +#define QBS_EXPORT_H + +#include + +#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 00000000..9bbdfb9a --- /dev/null +++ b/src/lib/corelib/tools/qbsassert.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbsassert.h" +#include "error.h" + +#include + +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::fromLatin1("ASSERT: %1").arg(QLatin1String(condition)), + CodeLocation(QString::fromLocal8Bit(file), line, -1, false), 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 00000000..2bbbd663 --- /dev/null +++ b/src/lib/corelib/tools/qbsassert.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_QBSASSERT_H +#define QBS_QBSASSERT_H + +#include "qbs_export.h" + +namespace qbs { +namespace Internal { + +QBS_EXPORT void writeAssertLocation(const char *condition, const char *file, int line); +QBS_EXPORT Q_NORETURN void 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 00000000..073cc787 --- /dev/null +++ b/src/lib/corelib/tools/qttools.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 00000000..bf7687fc --- /dev/null +++ b/src/lib/corelib/tools/qttools.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBSQTTOOLS_H +#define QBSQTTOOLS_H + +#include +#include + +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 00000000..83c1dfca --- /dev/null +++ b/src/lib/corelib/tools/scannerpluginmanager.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scannerpluginmanager.h" + +#include +#include +#include + +#include +#include +#include + +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 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 << QLatin1String("*.dll"); + else if (HostOsInfo::isMacosHost()) + filters << QLatin1String("*.dylib"); + else + filters << QLatin1String("*.so"); + + foreach (const QString &pluginPath, pluginPaths) { + logger.qbsTrace() << QString::fromLatin1("pluginmanager: loading plugins from '%1'.") + .arg(QDir::toNativeSeparators(pluginPath)); + QDirIterator it(pluginPath, filters, QDir::Files); + while (it.hasNext()) { + const QString fileName = it.next(); + QScopedPointer lib(new QLibrary(fileName)); + if (!lib->load()) { + logger.qbsWarning() << Tr::tr("Pluginmanager: Cannot load plugin '%1': %2") + .arg(QDir::toNativeSeparators(fileName), lib->errorString()); + continue; + } + + getScanners_f getScanners = reinterpret_cast(lib->resolve("getScanners")); + if (!getScanners) { + logger.qbsWarning() << Tr::tr("Pluginmanager: Cannot 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::fromLatin1("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 00000000..e377e656 --- /dev/null +++ b/src/lib/corelib/tools/scannerpluginmanager.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_PLUGINS_H +#define QBS_PLUGINS_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE +class QLibrary; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { +class Logger; + +class ScannerPluginManager +{ +public: + ~ScannerPluginManager(); + static ScannerPluginManager *instance(); + static QList scannersForFileTag(const FileTag &fileTag); + void loadPlugins(const QStringList &paths, const Logger &logger); + +private: + ScannerPluginManager(); + +private: + QList m_libs; + QHash > 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 00000000..16a5fea2 --- /dev/null +++ b/src/lib/corelib/tools/scripttools.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scripttools.h" + +#include +#include + +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)); +} + +TemporaryGlobalObjectSetter::TemporaryGlobalObjectSetter(const QScriptValue &object) +{ + QScriptEngine *engine = object.engine(); + m_oldGlobalObject = engine->globalObject(); + engine->setGlobalObject(object); +} + +TemporaryGlobalObjectSetter::~TemporaryGlobalObjectSetter() +{ + m_oldGlobalObject.engine()->setGlobalObject(m_oldGlobalObject); +} + +} // 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 00000000..77daa324 --- /dev/null +++ b/src/lib/corelib/tools/scripttools.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SCRIPTTOOLS_H +#define QBS_SCRIPTTOOLS_H + +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDataStream &operator<< (QDataStream &s, const QScriptProgram &script); +QDataStream &operator>> (QDataStream &s, QScriptProgram &script); + +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { + +template +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); + +template +void attachPointerTo(QScriptValue &scriptValue, T *ptr) +{ + QVariant v; + v.setValue(reinterpret_cast(ptr)); + scriptValue.setData(scriptValue.engine()->newVariant(v)); +} + +template +T *attachedPointer(const QScriptValue &scriptValue) +{ + const quintptr ptr = scriptValue.data().toVariant().value(); + return reinterpret_cast(ptr); +} + +class TemporaryGlobalObjectSetter +{ +public: + TemporaryGlobalObjectSetter(const QScriptValue &object); + ~TemporaryGlobalObjectSetter(); + +private: + QScriptValue m_oldGlobalObject; +}; + +} // 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 00000000..bb4f71b9 --- /dev/null +++ b/src/lib/corelib/tools/settings.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "settings.h" + +#include "error.h" +#include "settingscreator.h" + +#include + +#include + +#include + +namespace qbs { +using namespace Internal; + +Settings::Settings(const QString &baseDir) + : m_settings(SettingsCreator(baseDir).getQSettings()), m_baseDir(baseDir) +{ + // Actual qbs settings are stored transparently within a group, because QSettings + // can see non-qbs fallback settings e.g. from QtProject that we're not interested in. + m_settings->beginGroup(QLatin1String("org/qt-project/qbs")); +} + +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); +} + +void Settings::remove(const QString &key) +{ + m_settings->remove(internalRepresentation(key)); +} + +void Settings::clear() +{ + m_settings->clear(); +} + +void Settings::sync() +{ + m_settings->sync(); +} + +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::fileName() const +{ + return m_settings->fileName(); +} + +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); +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/settings.h b/src/lib/corelib/tools/settings.h new file mode 100644 index 00000000..ad0f5123 --- /dev/null +++ b/src/lib/corelib/tools/settings.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SETTINGS_H +#define QBS_SETTINGS_H + +#include "qbs_export.h" + +#include +#include + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace qbs { + +class QBS_EXPORT Settings +{ +public: + // The "pure" base directory without any version scope. Empty string means "system default". + Settings(const QString &baseDir); + + ~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(); + void sync(); + + QString defaultProfile() const; + QStringList profiles() const; + + QString fileName() const; + QString baseDirectory() const { return m_baseDir; } // As passed into the constructor. + +private: + QString internalRepresentation(const QString &externalKey) const; + QString externalRepresentation(const QString &internalKey) const; + void fixupKeys(QStringList &keys) const; + + QSettings * const m_settings; + const QString m_baseDir; +}; + +} // namespace qbs + +#endif // QBS_SETTINGS_H diff --git a/src/lib/corelib/tools/settingscreator.cpp b/src/lib/corelib/tools/settingscreator.cpp new file mode 100644 index 00000000..efa8d8de --- /dev/null +++ b/src/lib/corelib/tools/settingscreator.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "settingscreator.h" + +#include "fileinfo.h" +#include "hostosinfo.h" + +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +static QSettings::Format format() +{ + return HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat; +} + + +SettingsCreator::SettingsCreator(const QString &baseDir) + : m_settingsBaseDir(baseDir), m_qbsVersion(Version::fromString(QLatin1String(QBS_VERSION))) +{ +} + +QSettings *SettingsCreator::getQSettings() +{ + createQSettings(); + migrate(); + return m_settings.release(); +} + +void SettingsCreator::migrate() +{ + if (!m_settings->allKeys().isEmpty()) // We already have settings for this qbs version. + return; + + m_settings.reset(); + + // Find settings from highest qbs version lower than this one and copy all settings data. + const Version thePredecessor = predecessor(); + QString oldSettingsDir = m_settingsBaseDir; + if (thePredecessor.isValid()) + oldSettingsDir.append(QLatin1String("/qbs/")).append(thePredecessor.toString()); + QString oldProfilesDir = oldSettingsDir; + if (!thePredecessor.isValid()) + oldProfilesDir += QLatin1String("/qbs"); + oldProfilesDir += QLatin1String("/profiles"); + const QString newProfilesDir = m_newSettingsDir + QLatin1String("/profiles"); + QString errorMessage; + if (QFileInfo(oldProfilesDir).exists() + && !copyFileRecursion(oldProfilesDir, newProfilesDir, false, true, &errorMessage)) { + qWarning() << "Error in settings migration: " << errorMessage; + } + const QString oldSettingsFilePath = oldSettingsDir + QLatin1Char('/') + m_settingsFileName; + if (QFileInfo(oldSettingsFilePath).exists() + && (!QDir::root().mkpath(m_newSettingsDir) + || !QFile::copy(oldSettingsFilePath, m_newSettingsFilePath))) { + qWarning() << "Error in settings migration: Could not copy" << oldSettingsFilePath + << "to" << m_newSettingsFilePath; + } + + // Adapt all paths in settings that point to the old location. At the time of this writing, + // that's only preferences.qbsSearchPaths as written by libqtprofilesetup, but we don't want + // to hardcode that here. + m_settings.reset(new QSettings(m_newSettingsFilePath, format())); + foreach (const QString &key, m_settings->allKeys()) { + QVariant v = m_settings->value(key); + if (v.type() == QVariant::String) { + QString s = v.toString(); + if (s.contains(oldProfilesDir)) + m_settings->setValue(key, s.replace(oldProfilesDir, newProfilesDir)); + } else if (v.type() == QVariant::StringList) { + const QStringList oldList = v.toStringList(); + QStringList newList; + foreach (const QString &oldString, oldList) { + QString newString = oldString; + newList << newString.replace(oldProfilesDir, newProfilesDir); + } + if (newList != oldList) + m_settings->setValue(key, newList); + } + } +} + +void SettingsCreator::createQSettings() +{ + std::unique_ptr tmp(m_settingsBaseDir.isEmpty() + ? new QSettings(format(), QSettings::UserScope, QLatin1String("QtProject"), + QLatin1String("qbs")) + : new QSettings(m_settingsBaseDir + QLatin1String("/qbs.conf"), format())); + const QFileInfo fi(tmp->fileName()); + m_settingsBaseDir = fi.path(); + m_newSettingsDir = m_settingsBaseDir + QLatin1String("/qbs/") + m_qbsVersion.toString(); + m_settingsFileName = fi.fileName(); + m_newSettingsFilePath = m_newSettingsDir + QLatin1Char('/') + m_settingsFileName; + m_settings.reset(new QSettings(m_newSettingsFilePath, tmp->format())); +} + +Version SettingsCreator::predecessor() const +{ + QDirIterator dit(m_settingsBaseDir + QLatin1String("/qbs")); + Version thePredecessor; + while (dit.hasNext()) { + dit.next(); + const auto currentVersion = Version::fromString(dit.fileName()); + if (currentVersion > thePredecessor && currentVersion < m_qbsVersion) + thePredecessor = currentVersion; + } + return thePredecessor; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/settingscreator.h b/src/lib/corelib/tools/settingscreator.h new file mode 100644 index 00000000..ad0f05f5 --- /dev/null +++ b/src/lib/corelib/tools/settingscreator.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SETTINGSCREATOR_H +#define QBS_SETTINGSCREATOR_H + +#include "version.h" + +#include + +#include + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { + +class SettingsCreator +{ +public: + SettingsCreator(const QString &baseDir); + + QSettings *getQSettings(); + +private: + void migrate(); + void createQSettings(); + Version predecessor() const; + + QString m_settingsBaseDir; + QString m_newSettingsDir; + QString m_settingsFileName; + QString m_newSettingsFilePath; + std::unique_ptr m_settings; + const Version m_qbsVersion; +}; + + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/corelib/tools/settingsmodel.cpp b/src/lib/corelib/tools/settingsmodel.cpp new file mode 100644 index 00000000..758a9282 --- /dev/null +++ b/src/lib/corelib/tools/settingsmodel.cpp @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "settingsmodel.h" + +#include +#include + +#ifdef QT_GUI_LIB +#include +#endif + +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +struct Node +{ + Node() : parent(0), isFromSettings(true) {} + ~Node() { qDeleteAll(children); } + + QString uniqueChildName() const; + bool hasDirectChildWithName(const QString &name) const; + + QString name; + QString value; + Node *parent; + QList children; + bool isFromSettings; +}; + +QString Node::uniqueChildName() const +{ + QString newName = QLatin1String("newkey"); + bool unique; + do { + unique = true; + foreach (const Node *childNode, children) { + if (childNode->name == newName) { + unique = false; + newName += QLatin1Char('_'); + break; + } + } + } while (!unique); + return newName; +} + +bool Node::hasDirectChildWithName(const QString &name) const +{ + foreach (const Node * const child, children) { + if (child->name == name) + return true; + } + return false; +} + +} // namespace Internal + +using Internal::Node; + +class SettingsModel::SettingsModelPrivate +{ +public: + SettingsModelPrivate() : dirty(false), editable(true) {} + + void readSettings(); + void addNodeFromSettings(Node *parentNode, const QString &fullyQualifiedName); + void addNode(Node *parentNode, const QString ¤tNamePart, + const QStringList &restOfName, const QVariant &value); + void doSave(const Node *node, const QString &prefix); + Node *indexToNode(const QModelIndex &index); + + Node rootNode; + QScopedPointer settings; + QVariantMap additionalProperties; + bool dirty; + bool editable; +}; + +SettingsModel::SettingsModel(const QString &settingsDir, QObject *parent) + : QAbstractItemModel(parent), d(new SettingsModelPrivate) +{ + d->settings.reset(new qbs::Settings(settingsDir)); + d->readSettings(); +} + +SettingsModel::~SettingsModel() +{ + delete d; +} + +void SettingsModel::reload() +{ + beginResetModel(); + d->readSettings(); + endResetModel(); +} + +void SettingsModel::save() +{ + if (!d->dirty) + return; + d->settings->clear(); + d->doSave(&d->rootNode, QString()); + d->dirty = false; +} + +void SettingsModel::updateSettingsDir(const QString &settingsDir) +{ + beginResetModel(); + d->settings.reset(new qbs::Settings(settingsDir)); + d->readSettings(); + endResetModel(); +} + +void SettingsModel::addNewKey(const QModelIndex &parent) +{ + Node *parentNode = d->indexToNode(parent); + if (!parentNode) + return; + Node * const newNode = new Node; + newNode->parent = parentNode; + newNode->name = parentNode->uniqueChildName(); + beginInsertRows(parent, parentNode->children.count(), parentNode->children.count()); + parentNode->children << newNode; + endInsertRows(); + d->dirty = true; +} + +void SettingsModel::removeKey(const QModelIndex &index) +{ + Node * const node = d->indexToNode(index); + if (!node || node == &d->rootNode) + return; + const int positionInParent = node->parent->children.indexOf(node); + beginRemoveRows(parent(index), positionInParent, positionInParent); + node->parent->children.removeAt(positionInParent); + delete node; + endRemoveRows(); + d->dirty = true; +} + +bool SettingsModel::hasUnsavedChanges() const +{ + return d->dirty; +} + +void SettingsModel::setEditable(bool isEditable) +{ + d->editable = isEditable; +} + +void SettingsModel::setAdditionalProperties(const QVariantMap &properties) +{ + d->additionalProperties = properties; + reload(); +} + +Qt::ItemFlags SettingsModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemFlags(); + const Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; + if (index.column() == keyColumn()) { + if (d->editable) + return flags | Qt::ItemIsEditable; + return flags; + } + if (index.column() == valueColumn()) { + const Node * const node = d->indexToNode(index); + if (!node) + return Qt::ItemFlags(); + + // Only leaf nodes have values. + return d->editable && node->children.isEmpty() ? flags | Qt::ItemIsEditable : flags; + } + return Qt::ItemFlags(); +} + +QVariant SettingsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal) + return QVariant(); + if (role != Qt::DisplayRole) + return QVariant(); + if (section == keyColumn()) + return tr("Key"); + if (section == valueColumn()) + return tr("Value"); + return QVariant(); +} + +int SettingsModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 2; +} + +int SettingsModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + const Node * const node = d->indexToNode(parent); + Q_ASSERT(node); + return node->children.count(); +} + +QVariant SettingsModel::data(const QModelIndex &index, int role) const +{ + if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::ForegroundRole) + return QVariant(); + const Node * const node = d->indexToNode(index); + if (!node) + return QVariant(); + if (role == Qt::ForegroundRole) { +#ifdef QT_GUI_LIB + if (index.column() == valueColumn() && !node->isFromSettings) + return QBrush(Qt::red); +#endif + return QVariant(); + } + if (index.column() == keyColumn()) + return node->name; + if (index.column() == valueColumn() && node->children.isEmpty()) + return node->value; + return QVariant(); +} + +bool SettingsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || role != Qt::EditRole) + return false; + Node * const node = d->indexToNode(index); + if (!node) + return false; + const QString valueString = value.toString(); + QString *toChange = 0; + if (index.column() == keyColumn() && !valueString.isEmpty() + && !node->parent->hasDirectChildWithName(valueString)) { + toChange = &node->name; + } else if (index.column() == valueColumn() && valueString != node->value) { + toChange = &node->value; + } + + if (toChange) { + *toChange = valueString; + emit dataChanged(index, index); + d->dirty = true; + } + return toChange; +} + +QModelIndex SettingsModel::index(int row, int column, const QModelIndex &parent) const +{ + const Node * const parentNode = d->indexToNode(parent); + Q_ASSERT(parentNode); + if (parentNode->children.count() <= row) + return QModelIndex(); + return createIndex(row, column, parentNode->children.at(row)); +} + +QModelIndex SettingsModel::parent(const QModelIndex &child) const +{ + Node * const childNode = static_cast(child.internalPointer()); + Q_ASSERT(childNode); + Node * const parentNode = childNode->parent; + if (parentNode == &d->rootNode) + return QModelIndex(); + const Node * const grandParentNode = parentNode->parent; + Q_ASSERT(grandParentNode); + return createIndex(grandParentNode->children.indexOf(parentNode), 0, parentNode); +} + + +void SettingsModel::SettingsModelPrivate::readSettings() +{ + qDeleteAll(rootNode.children); + rootNode.children.clear(); + foreach (const QString &topLevelKey, settings->directChildren(QString())) + addNodeFromSettings(&rootNode, topLevelKey); + for (QVariantMap::ConstIterator it = additionalProperties.constBegin(); + it != additionalProperties.constEnd(); ++it) { + const QStringList nameAsList = it.key().split(QLatin1Char('.'), QString::SkipEmptyParts); + addNode(&rootNode, nameAsList.first(), nameAsList.mid(1), it.value()); + } + dirty = false; +} + +static Node *createNode(Node *parentNode, const QString &name) +{ + Node * const node = new Node; + node->name = name; + node->parent = parentNode; + parentNode->children << node; + return node; +} + +void SettingsModel::SettingsModelPrivate::addNodeFromSettings(Node *parentNode, + const QString &fullyQualifiedName) +{ + const QString &nodeName + = fullyQualifiedName.mid(fullyQualifiedName.lastIndexOf(QLatin1Char('.')) + 1); + Node * const node = createNode(parentNode, nodeName); + node->value = settingsValueToRepresentation(settings->value(fullyQualifiedName)); + foreach (const QString &childKey, settings->directChildren(fullyQualifiedName)) + addNodeFromSettings(node, fullyQualifiedName + QLatin1Char('.') + childKey); + dirty = true; +} + +void SettingsModel::SettingsModelPrivate::addNode(qbs::Internal::Node *parentNode, + const QString ¤tNamePart, const QStringList &restOfName, const QVariant &value) +{ + Node *currentNode = 0; + foreach (Node * const n, parentNode->children) { + if (n->name == currentNamePart) { + currentNode = n; + break; + } + } + if (!currentNode) + currentNode = createNode(parentNode, currentNamePart); + if (restOfName.isEmpty()) { + currentNode->value = settingsValueToRepresentation(value); + currentNode->isFromSettings = false; + } else { + addNode(currentNode, restOfName.first(), restOfName.mid(1), value); + } +} + +void SettingsModel::SettingsModelPrivate::doSave(const Node *node, const QString &prefix) +{ + if (node->children.isEmpty()) { + settings->setValue(prefix + node->name, representationToSettingsValue(node->value)); + return; + } + + const QString newPrefix = prefix + node->name + QLatin1Char('.'); + foreach (const Node * const child, node->children) + doSave(child, newPrefix); +} + +Node *SettingsModel::SettingsModelPrivate::indexToNode(const QModelIndex &index) +{ + return index.isValid() ? static_cast(index.internalPointer()) : &rootNode; +} + + +QString settingsValueToRepresentation(const QVariant &value) +{ + return toJSLiteral(value); +} + +static QVariant variantFromString(const QString &str) +{ + // ### use Qt5's JSON reader at some point. + QScriptEngine engine; + QScriptValue sv = engine.evaluate(QLatin1String("(function(){return ") + + str + QLatin1String(";})()")); + if (sv.isError()) + return QVariant(); + return sv.toVariant(); +} + +QVariant representationToSettingsValue(const QString &representation) +{ + const QVariant variant = variantFromString(representation); + if (variant.isValid()) + return variant; + + // If it's not valid JavaScript, interpret the value as a string. + return representation; +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/settingsmodel.h b/src/lib/corelib/tools/settingsmodel.h new file mode 100644 index 00000000..4ac8911c --- /dev/null +++ b/src/lib/corelib/tools/settingsmodel.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SETTINGSMODEL_H +#define QBS_SETTINGSMODEL_H + +#include + +#include +#include + +namespace qbs { + +class QBS_EXPORT SettingsModel : public QAbstractItemModel +{ + Q_OBJECT +public: + SettingsModel(const QString &settingsDir, QObject *parent = 0); + ~SettingsModel(); + + int keyColumn() const { return 0; } + int valueColumn() const { return 1; } + bool hasUnsavedChanges() const; + + void setEditable(bool isEditable); + void setAdditionalProperties(const QVariantMap &properties); // Flat map. + void reload(); + void save(); + void updateSettingsDir(const QString &settingsDir); + + void addNewKey(const QModelIndex &parent); + void removeKey(const QModelIndex &index); + + Qt::ItemFlags flags(const QModelIndex &index) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + +private: + class SettingsModelPrivate; + SettingsModelPrivate * const d; +}; + +QBS_EXPORT QString settingsValueToRepresentation(const QVariant &value); +QBS_EXPORT QVariant representationToSettingsValue(const QString &representation); + +} // namespace qbs + +#endif // QBS_SETTINGSMODEL_H diff --git a/src/lib/corelib/tools/setupprojectparameters.cpp b/src/lib/corelib/tools/setupprojectparameters.cpp new file mode 100644 index 00000000..33918e16 --- /dev/null +++ b/src/lib/corelib/tools/setupprojectparameters.cpp @@ -0,0 +1,633 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "setupprojectparameters.h" + +#include +#include +#include +#include +#include +#include + +#include + +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) + , forceProbeExecution(false) + , waitLockBuildGraph(false) + , restoreBehavior(SetupProjectParameters::RestoreAndTrackChanges) + , propertyCheckingMode(ErrorHandlingMode::Relaxed) + , productErrorMode(ErrorHandlingMode::Strict) + , environment(QProcessEnvironment::systemEnvironment()) + { + } + + QString projectFilePath; + QString topLevelProfile; + QString configurationName; + QString buildRoot; + QStringList searchPaths; + QStringList pluginPaths; + QString libexecPath; + QString settingsBaseDir; + QVariantMap overriddenValues; + QVariantMap buildConfiguration; + mutable QVariantMap buildConfigurationTree; + mutable QVariantMap overriddenValuesTree; + mutable QVariantMap finalBuildConfigTree; + bool ignoreDifferentProjectFilePath; + bool dryRun; + bool logElapsedTime; + bool forceProbeExecution; + bool waitLockBuildGraph; + SetupProjectParameters::RestoreBehavior restoreBehavior; + ErrorHandlingMode propertyCheckingMode; + ErrorHandlingMode productErrorMode; + 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 name of the top-level profile for building the project. + */ +QString SetupProjectParameters::topLevelProfile() const +{ + return d->topLevelProfile; +} + +/*! + * \brief Sets the top-level profile for building the project. + */ +void SetupProjectParameters::setTopLevelProfile(const QString &profile) +{ + d->buildConfigurationTree.clear(); + d->finalBuildConfigTree.clear(); + d->topLevelProfile = profile; +} + +/*! + * Returns the name of the current project build configuration. + */ +QString SetupProjectParameters::configurationName() const +{ + return d->configurationName; +} + +/*! + * Sets the name of the current project build configuration to an arbitrary user-specified name, + * \a configurationName. + */ +void SetupProjectParameters::setConfigurationName(const QString &configurationName) +{ + d->buildConfigurationTree.clear(); + d->finalBuildConfigTree.clear(); + d->configurationName = configurationName; +} + +/*! + * \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; + + const QString canonicalProjectFilePath = QFileInfo(d->projectFilePath).canonicalFilePath(); + if (!canonicalProjectFilePath.isEmpty()) + 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; + + const QString canonicalBuildRoot = QFileInfo(d->buildRoot).canonicalFilePath(); + if (!canonicalBuildRoot.isEmpty()) + d->buildRoot = canonicalBuildRoot; +} + +/*! + * \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; +} + +/*! + * \brief Where to look for internal binaries. + */ +QString SetupProjectParameters::libexecPath() const +{ + return d->libexecPath; +} + +/*! + * \brief Sets the information about where to look for internal binaries. + * \note \p libexecPath must be an absolute path. + */ +void SetupProjectParameters::setLibexecPath(const QString &libexecPath) +{ + d->libexecPath = libexecPath; +} + +/*! + * \brief The base directory for qbs settings. + * This value is used to locate profiles and preferences. + */ +QString SetupProjectParameters::settingsDirectory() const +{ + return d->settingsBaseDir; +} + +/*! + * \brief Sets the base directory for qbs settings. + * \param settingsBaseDir Will be used to locate profiles and preferences. + */ +void SetupProjectParameters::setSettingsDirectory(const QString &settingsBaseDir) +{ + d->settingsBaseDir = settingsBaseDir; +} + +/*! + * 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) { + const QString name = it.key(); + int idx = name.lastIndexOf(QLatin1Char('.')); + const QStringList nameElements = (idx == -1) + ? QStringList() << name + : QStringList() << name.left(idx) << name.mid(idx + 1); + Internal::setConfigProperty(*valueTree, nameElements, it.value()); + } +} + +QVariantMap SetupProjectParameters::overriddenValuesTree() const +{ + provideValuesTree(d->overriddenValues, &d->overriddenValuesTree); + return d->overriddenValuesTree; +} + +/*! + * \brief Returns the build configuration. + * Overridden values are not taken into account. + */ +QVariantMap SetupProjectParameters::buildConfiguration() const +{ + return d->buildConfiguration; +} + +/*! + * \brief Returns the build configuration in tree form. + * Overridden values are not taken into account. + */ +QVariantMap SetupProjectParameters::buildConfigurationTree() const +{ + provideValuesTree(d->buildConfiguration, &d->buildConfigurationTree); + return d->buildConfigurationTree; +} + +static QVariantMap expandedBuildConfigurationInternal(const QString &settingsBaseDir, + const QString &profileName, const QString &configurationName) +{ + Settings settings(settingsBaseDir); + QVariantMap buildConfig; + + // (1) Values from profile, if given. + if (!profileName.isEmpty()) { + ErrorInfo err; + const Profile profile(profileName, &settings); + const QStringList profileKeys = profile.allKeys(Profile::KeySelectionRecursive, &err); + if (err.hasError()) + throw err; + if (profileKeys.isEmpty()) + throw ErrorInfo(Internal::Tr::tr("Unknown or empty profile '%1'.").arg(profileName)); + foreach (const QString &profileKey, profileKeys) { + buildConfig.insert(profileKey, profile.value(profileKey, QVariant(), &err)); + if (err.hasError()) + throw err; + } + } + + // (2) Build configuration name. + if (configurationName.isEmpty()) + throw ErrorInfo(Internal::Tr::tr("No build configuration name set.")); + buildConfig.insert(QLatin1String("qbs.configurationName"), configurationName); + return buildConfig; +} + + +QVariantMap SetupProjectParameters::expandedBuildConfiguration(const QString &settingsBaseDir, + const QString &profileName, const QString &configurationName, ErrorInfo *errorInfo) +{ + try { + return expandedBuildConfigurationInternal(settingsBaseDir, profileName, configurationName); + } catch (const ErrorInfo &err) { + if (errorInfo) + *errorInfo = err; + return QVariantMap(); + } +} + + +/*! + * \brief Expands the build configuration. + * + * Expansion is the process by which the build configuration is completed based on the settings + * in \c settingsDirectory(). 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() +{ + ErrorInfo err; + QVariantMap expandedConfig = expandedBuildConfiguration(d->settingsBaseDir, topLevelProfile(), + configurationName(), &err); + if (err.hasError()) + return err; + if (d->buildConfiguration != expandedConfig) { + d->buildConfigurationTree.clear(); + d->buildConfiguration = expandedConfig; + } + return err; +} + +QVariantMap SetupProjectParameters::finalBuildConfigurationTree(const QVariantMap &buildConfig, + const QVariantMap &overriddenValues, const QString &buildRoot) +{ + QVariantMap flatBuildConfig = buildConfig; + for (QVariantMap::ConstIterator it = overriddenValues.constBegin(); + it != overriddenValues.constEnd(); ++it) { + flatBuildConfig.insert(it.key(), it.value()); + } + + const QString installRootKey = QLatin1String("qbs.installRoot"); + QString installRoot = flatBuildConfig.value(installRootKey).toString(); + if (installRoot.isEmpty()) { + installRoot = buildRoot + QLatin1Char('/') + + flatBuildConfig.value(QLatin1String("qbs.configurationName")).toString() + + QLatin1Char('/') + InstallOptions::defaultInstallRoot(); + flatBuildConfig.insert(installRootKey, installRoot); + } + + QVariantMap buildConfigTree; + provideValuesTree(flatBuildConfig, &buildConfigTree); + return buildConfigTree; +} + +/*! + * \brief Returns the build configuration in tree form, with overridden values taken into account. + */ +QVariantMap SetupProjectParameters::finalBuildConfigurationTree() const +{ + if (d->finalBuildConfigTree.isEmpty()) { + d->finalBuildConfigTree = finalBuildConfigurationTree(buildConfiguration(), + overriddenValues(), buildRoot()); + } + 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 Returns true iff probes should be re-run. + */ +bool SetupProjectParameters::forceProbeExecution() const +{ + return d->forceProbeExecution; +} + +/*! + * Controls whether to re-run probes even if they do not appear to be outdated. + * This option only has an effect if \c restoreBehavior() is \c RestoreAndTrackChanges. + */ +void SetupProjectParameters::setForceProbeExecution(bool force) +{ + d->forceProbeExecution = force; +} + +/*! + * \brief Returns true if qbs should wait for the build graph lock to become available, + * otherwise qbs will exit immediately if the lock cannot be acquired. + */ +bool SetupProjectParameters::waitLockBuildGraph() const +{ + return d->waitLockBuildGraph; +} + +/*! + * Controls whether to wait indefinitely for the build graph lock to be released. + * This allows multiple conflicting qbs processes to be spawned simultaneously. + */ +void SetupProjectParameters::setWaitLockBuildGraph(bool wait) +{ + d->waitLockBuildGraph = wait; +} + +/*! + * \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; +} + +QProcessEnvironment SetupProjectParameters::adjustedEnvironment() const +{ + QProcessEnvironment result = environment(); + const QVariantMap environmentFromProfile + = buildConfigurationTree().value(QLatin1String("buildEnvironment")).toMap(); + for (QVariantMap::const_iterator it = environmentFromProfile.begin(); + it != environmentFromProfile.end(); ++it) { + result.insert(it.key(), it.value().toString()); + } + return result; +} + + +/*! + * \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; +} + +/*! + * \enum ErrorHandlingMode + * This enum type specifies how \QBS should behave if errors occur during project resolving. + * \value ErrorHandlingMode::Strict Project resolving will stop with an error message. + * \value ErrorHandlingMode::Relaxed Project resolving will continue (if possible), and a warning + * will be printed. + */ + +/*! + * Indicates how to handle unknown properties. + */ +ErrorHandlingMode SetupProjectParameters::propertyCheckingMode() const +{ + return d->propertyCheckingMode; +} + +/*! + * Controls how to handle unknown properties. + * The default is \c PropertyCheckingRelaxed. + */ +void SetupProjectParameters::setPropertyCheckingMode(ErrorHandlingMode mode) +{ + d->propertyCheckingMode = mode; +} + +/*! + * \brief Indicates how errors occurring during product resolving are handled. + */ +ErrorHandlingMode SetupProjectParameters::productErrorMode() const +{ + return d->productErrorMode; +} + +/*! + * \brief Specifies whether an error occurring during product resolving should be fatal or not. + * \note Not all errors can be ignored; this setting is mainly intended for things such as + * missing dependencies or references to non-existing source files. + */ +void SetupProjectParameters::setProductErrorMode(ErrorHandlingMode mode) +{ + d->productErrorMode = mode; +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/setupprojectparameters.h b/src/lib/corelib/tools/setupprojectparameters.h new file mode 100644 index 00000000..2682a1e7 --- /dev/null +++ b/src/lib/corelib/tools/setupprojectparameters.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_SETUPPROJECTPARAMETERS_H +#define QBS_SETUPPROJECTPARAMETERS_H + +#include "qbs_export.h" + +#include + +#include +#include +#include +#include + +namespace qbs { + +class Settings; + +namespace Internal { class SetupProjectParametersPrivate; } + +enum class ErrorHandlingMode { Strict, Relaxed }; + +class QBS_EXPORT SetupProjectParameters +{ +public: + SetupProjectParameters(); + SetupProjectParameters(const SetupProjectParameters &other); + ~SetupProjectParameters(); + + SetupProjectParameters &operator=(const SetupProjectParameters &other); + + QString topLevelProfile() const; + void setTopLevelProfile(const QString &profile); + + QString configurationName() const; + void setConfigurationName(const QString &configurationName); + + 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); + + QString libexecPath() const; + void setLibexecPath(const QString &libexecPath); + + QString settingsDirectory() const; + void setSettingsDirectory(const QString &settingsBaseDir); + + QVariantMap overriddenValues() const; + void setOverriddenValues(const QVariantMap &values); + QVariantMap overriddenValuesTree() const; + + static QVariantMap expandedBuildConfiguration(const QString &settingsBaseDir, + const QString &profileName, const QString &configurationName, ErrorInfo *errorInfo = 0); + ErrorInfo expandBuildConfiguration(); + QVariantMap buildConfiguration() const; + QVariantMap buildConfigurationTree() const; + + static QVariantMap finalBuildConfigurationTree(const QVariantMap &buildConfig, + const QVariantMap &overriddenValues, const QString &buildRoot); + QVariantMap finalBuildConfigurationTree() const; + + bool ignoreDifferentProjectFilePath() const; + void setIgnoreDifferentProjectFilePath(bool doIgnore); + + bool dryRun() const; + void setDryRun(bool dryRun); + + bool logElapsedTime() const; + void setLogElapsedTime(bool logElapsedTime); + + bool forceProbeExecution() const; + void setForceProbeExecution(bool force); + + bool waitLockBuildGraph() const; + void setWaitLockBuildGraph(bool wait); + + QProcessEnvironment environment() const; + void setEnvironment(const QProcessEnvironment &env); + QProcessEnvironment adjustedEnvironment() const; + + enum RestoreBehavior { RestoreOnly, ResolveOnly, RestoreAndTrackChanges }; + RestoreBehavior restoreBehavior() const; + void setRestoreBehavior(RestoreBehavior behavior); + + ErrorHandlingMode propertyCheckingMode() const; + void setPropertyCheckingMode(ErrorHandlingMode mode); + + ErrorHandlingMode productErrorMode() const; + void setProductErrorMode(ErrorHandlingMode mode); + +private: + QSharedDataPointer d; +}; + +} // namespace qbs + +#endif // Include guard diff --git a/src/lib/corelib/tools/shellutils.cpp b/src/lib/corelib/tools/shellutils.cpp new file mode 100644 index 00000000..8c91638d --- /dev/null +++ b/src/lib/corelib/tools/shellutils.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shellutils.h" +#include "pathutils.h" +#include +#include +#include + +namespace qbs { +namespace Internal { + +QString shellInterpreter(const QString &filePath) { + QFile file(filePath); + if (file.open(QIODevice::ReadOnly)) { + QTextStream ts(&file); + const QString shebang = ts.readLine(); + if (shebang.startsWith(QLatin1String("#!"))) { + return (shebang.mid(2).split(QRegExp(QLatin1String("\\s")), + QString::SkipEmptyParts) << QString()).first(); + } + } + + return QString(); +} + +// isSpecialChar, hasSpecialChars, shellQuoteUnix, shellQuoteWin: +// all from qtbase/qmake/library/ioutils.cpp + +inline static bool isSpecialChar(ushort c, const uchar (&iqm)[16]) +{ + if ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)))) + return true; + return false; +} + +inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16]) +{ + for (int x = arg.length() - 1; x >= 0; --x) { + if (isSpecialChar(arg.unicode()[x].unicode(), iqm)) + return true; + } + return false; +} + +static QString shellQuoteUnix(const QString &arg) +{ + // Chars that should be quoted (TM). This includes: + static const uchar iqm[] = { + 0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8, + 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78 + }; // 0-32 \'"$`<>|;&(){}*?#!~[] + + if (!arg.length()) + return QString::fromLatin1("''"); + + QString ret(arg); + if (hasSpecialChars(ret, iqm)) { + ret.replace(QLatin1Char('\''), QLatin1String("'\\''")); + ret.prepend(QLatin1Char('\'')); + ret.append(QLatin1Char('\'')); + } + return ret; +} + +static QString shellQuoteWin(const QString &arg) +{ + // Chars that should be quoted (TM). This includes: + // - control chars & space + // - the shell meta chars "&()<>^| + // - the potential separators ,;= + static const uchar iqm[] = { + 0xff, 0xff, 0xff, 0xff, 0x45, 0x13, 0x00, 0x78, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10 + }; + // Shell meta chars that need escaping. + static const uchar ism[] = { + 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x10 + }; // &()<>^| + + if (!arg.length()) + return QString::fromLatin1("\"\""); + + QString ret(arg); + if (hasSpecialChars(ret, iqm)) { + // The process-level standard quoting allows escaping quotes with backslashes (note + // that backslashes don't escape themselves, unless they are followed by a quote). + // Consequently, quotes are escaped and their preceding backslashes are doubled. + ret.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\"")); + // Trailing backslashes must be doubled as well, as they are followed by a quote. + ret.replace(QRegExp(QLatin1String("(\\\\+)$")), QLatin1String("\\1\\1")); + // However, the shell also interprets the command, and no backslash-escaping exists + // there - a quote always toggles the quoting state, but is nonetheless passed down + // to the called process verbatim. In the unquoted state, the circumflex escapes + // meta chars (including itself and quotes), and is removed from the command. + bool quoted = true; + for (int i = 0; i < ret.length(); i++) { + QChar c = ret.unicode()[i]; + if (c.unicode() == '"') + quoted = !quoted; + else if (!quoted && isSpecialChar(c.unicode(), ism)) + ret.insert(i++, QLatin1Char('^')); + } + if (!quoted) + ret.append(QLatin1Char('^')); + ret.append(QLatin1Char('"')); + ret.prepend(QLatin1Char('"')); + } + return ret; +} + +QString shellQuote(const QString &arg, HostOsInfo::HostOs os) +{ + return os == HostOsInfo::HostOsWindows ? shellQuoteWin(arg) : shellQuoteUnix(arg); +} + +QString shellQuote(const QStringList &args, HostOsInfo::HostOs os) +{ + QString result; + if (!args.isEmpty()) { + result += shellQuote(args.at(0), os); + for (int i = 1; i < args.size(); ++i) + result += QLatin1Char(' ') + shellQuote(args.at(i), os); + } + return result; +} + +QString shellQuote(const QString &program, const QStringList &args, HostOsInfo::HostOs os) +{ + QString result = shellQuote(program, os); + if (!args.isEmpty()) + result += QLatin1Char(' ') + shellQuote(args, os); + return result; +} + +void CommandLine::setProgram(const QString &program, bool raw) +{ + m_program = program; + m_isRawProgram = raw; +} + +void CommandLine::appendArgument(const QString &value) +{ + m_arguments.append(value); +} + +void CommandLine::appendArguments(const QList &args) +{ + for (const QString &arg : args) + appendArgument(arg); +} + +void CommandLine::appendRawArgument(const QString &value) +{ + Argument arg(value); + arg.shouldQuote = false; + m_arguments.append(arg); +} + +void CommandLine::appendPathArgument(const QString &value) +{ + Argument arg(value); + arg.isFilePath = true; + m_arguments.append(arg); +} + +void CommandLine::clearArguments() +{ + m_arguments.clear(); +} + +QString CommandLine::toCommandLine(HostOsInfo::HostOs os) const +{ + QString result = PathUtils::toNativeSeparators(m_program, os); + if (!m_isRawProgram) + result = shellQuote(result, os); + for (const Argument &arg : m_arguments) { + const QString value = arg.isFilePath + ? PathUtils::toNativeSeparators(arg.value, os) + : arg.value; + result += QLatin1Char(' ') + (arg.shouldQuote ? shellQuote(value, os) : value); + } + return result; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/shellutils.h b/src/lib/corelib/tools/shellutils.h new file mode 100644 index 00000000..5339405c --- /dev/null +++ b/src/lib/corelib/tools/shellutils.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_SHELLUTILS_H +#define QBS_SHELLUTILS_H + +#include "qbs_export.h" +#include "hostosinfo.h" +#include +#include + +namespace qbs { +namespace Internal { + +QBS_EXPORT QString shellInterpreter(const QString &filePath); +QBS_EXPORT QString shellQuote(const QString &arg, HostOsInfo::HostOs os = HostOsInfo::hostOs()); +QBS_EXPORT QString shellQuote(const QStringList &args, + HostOsInfo::HostOs os = HostOsInfo::hostOs()); +QBS_EXPORT QString shellQuote(const QString &program, const QStringList &args, + HostOsInfo::HostOs os = HostOsInfo::hostOs()); + +class QBS_EXPORT CommandLine +{ +public: + void setProgram(const QString &program, bool raw = false); + void appendArgument(const QString &value); + void appendArguments(const QList &args); + void appendRawArgument(const QString &value); + void appendPathArgument(const QString &value); + void clearArguments(); + QString toCommandLine(HostOsInfo::HostOs os = HostOsInfo::hostOs()) const; + +private: + struct Argument + { + Argument(const QString &value = QString()) : value(value) { } + QString value; + bool isFilePath = false; + bool shouldQuote = true; + }; + + bool m_isRawProgram; + QString m_program; + QVector m_arguments; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_SHELLUTILS_H diff --git a/src/lib/corelib/tools/toolchains.cpp b/src/lib/corelib/tools/toolchains.cpp new file mode 100644 index 00000000..eae6bca0 --- /dev/null +++ b/src/lib/corelib/tools/toolchains.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "toolchains.h" +#include +#include + +namespace qbs { + +QStringList canonicalToolchain(const QStringList &toolchain) +{ + static const QStringList knownToolchains { + QStringLiteral("xcode"), + QStringLiteral("clang"), + QStringLiteral("llvm"), + QStringLiteral("mingw"), + QStringLiteral("gcc"), + QStringLiteral("msvc") + }; + + // Canonicalize each toolchain in the toolchain list, + // which gets us the aggregate canonicalized (unsorted) list + QStringList toolchains; + for (const QString &toolchainName : toolchain) + toolchains << canonicalToolchain(toolchainName); + toolchains.removeDuplicates(); + + // Find all known toolchains in the canonicalized list, + // removing them from the main list as we go. + QStringList usedKnownToolchains; + for (int i = 0; i < toolchains.size(); ++i) { + if (knownToolchains.contains(toolchains[i])) { + usedKnownToolchains << toolchains[i]; + toolchains.removeAt(i--); + } + } + + // Sort the list of known toolchains into their canonical order. + std::sort(usedKnownToolchains.begin(), usedKnownToolchains.end(), []( + const QString &a, + const QString &b) { + return knownToolchains.indexOf(a) < knownToolchains.indexOf(b); + }); + + // Re-add the known toolchains to the main list (the custom ones go first). + toolchains << usedKnownToolchains; + + // The toolchain list still needs further validation as it may contain mututally exclusive + // toolchain types (for example, llvm and msvc). + return toolchains; +} + +QStringList canonicalToolchain(const QString &name) +{ + const QString &toolchainName = name.toLower(); + QStringList toolchains(toolchainName); + if (toolchainName == QLatin1String("xcode")) + toolchains << canonicalToolchain(QLatin1String("clang")); + else if (toolchainName == QLatin1String("clang")) + toolchains << canonicalToolchain(QLatin1String("llvm")); + else if (toolchainName == QLatin1String("llvm") || + toolchainName == QLatin1String("mingw")) { + toolchains << canonicalToolchain(QLatin1String("gcc")); + } + return toolchains; +} + +} // namespace qbs diff --git a/src/lib/corelib/tools/toolchains.h b/src/lib/corelib/tools/toolchains.h new file mode 100644 index 00000000..1bb52ca8 --- /dev/null +++ b/src/lib/corelib/tools/toolchains.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_TOOLCHAINS_H +#define QBS_TOOLCHAINS_H + +#include "qbs_export.h" +#include + +namespace qbs { + +QBS_EXPORT QStringList canonicalToolchain(const QStringList &toolchain); +QBS_EXPORT QStringList canonicalToolchain(const QString &toolchainName); + +} // 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 00000000..344f9924 --- /dev/null +++ b/src/lib/corelib/tools/tools.pri @@ -0,0 +1,130 @@ +include(../../../install_prefix.pri) + +INCLUDEPATH += $$PWD/../.. # for plugins + +HEADERS += \ + $$PWD/architectures.h \ + $$PWD/buildgraphlocker.h \ + $$PWD/codelocation.h \ + $$PWD/commandechomode.h \ + $$PWD/error.h \ + $$PWD/executablefinder.h \ + $$PWD/fileinfo.h \ + $$PWD/filesaver.h \ + $$PWD/filetime.h \ + $$PWD/generateoptions.h \ + $$PWD/id.h \ + $$PWD/jsliterals.h \ + $$PWD/msvcinfo.h \ + $$PWD/persistence.h \ + $$PWD/scannerpluginmanager.h \ + $$PWD/scripttools.h \ + $$PWD/settings.h \ + $$PWD/settingsmodel.h \ + $$PWD/pathutils.h \ + $$PWD/preferences.h \ + $$PWD/profile.h \ + $$PWD/profiling.h \ + $$PWD/processresult.h \ + $$PWD/processresult_p.h \ + $$PWD/processutils.h \ + $$PWD/progressobserver.h \ + $$PWD/projectgeneratormanager.h \ + $$PWD/propertyfinder.h \ + $$PWD/shellutils.h \ + $$PWD/toolchains.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 \ + $$PWD/settingscreator.h \ + $$PWD/version.h \ + $$PWD/visualstudioversioninfo.h \ + $$PWD/vsenvironmentdetector.h + +SOURCES += \ + $$PWD/architectures.cpp \ + $$PWD/buildgraphlocker.cpp \ + $$PWD/codelocation.cpp \ + $$PWD/commandechomode.cpp \ + $$PWD/error.cpp \ + $$PWD/executablefinder.cpp \ + $$PWD/fileinfo.cpp \ + $$PWD/filesaver.cpp \ + $$PWD/generateoptions.cpp \ + $$PWD/id.cpp \ + $$PWD/jsliterals.cpp \ + $$PWD/msvcinfo.cpp \ + $$PWD/persistence.cpp \ + $$PWD/scannerpluginmanager.cpp \ + $$PWD/scripttools.cpp \ + $$PWD/settings.cpp \ + $$PWD/settingsmodel.cpp \ + $$PWD/preferences.cpp \ + $$PWD/processresult.cpp \ + $$PWD/processutils.cpp \ + $$PWD/profile.cpp \ + $$PWD/profiling.cpp \ + $$PWD/progressobserver.cpp \ + $$PWD/projectgeneratormanager.cpp \ + $$PWD/propertyfinder.cpp \ + $$PWD/shellutils.cpp \ + $$PWD/buildoptions.cpp \ + $$PWD/installoptions.cpp \ + $$PWD/cleanoptions.cpp \ + $$PWD/setupprojectparameters.cpp \ + $$PWD/qbsassert.cpp \ + $$PWD/qttools.cpp \ + $$PWD/settingscreator.cpp \ + $$PWD/toolchains.cpp \ + $$PWD/version.cpp \ + $$PWD/visualstudioversioninfo.cpp \ + $$PWD/vsenvironmentdetector.cpp + +osx { + HEADERS += $$PWD/applecodesignutils.h + SOURCES += $$PWD/applecodesignutils.cpp + LIBS += -framework Security +} + +win32 { + SOURCES += $$PWD/filetime_win.cpp +} + +unix { + SOURCES += $$PWD/filetime_unix.cpp +} + +qbs_enable_unit_tests { + HEADERS += $$PWD/tst_tools.h + SOURCES += $$PWD/tst_tools.cpp +} + +!qbs_no_dev_install { + tools_headers.files = \ + $$PWD/architectures.h \ + $$PWD/cleanoptions.h \ + $$PWD/codelocation.h \ + $$PWD/commandechomode.h \ + $$PWD/error.h \ + $$PWD/settings.h \ + $$PWD/settingsmodel.h \ + $$PWD/preferences.h \ + $$PWD/profile.h \ + $$PWD/processresult.h \ + $$PWD/qbs_export.h \ + $$PWD/buildoptions.h \ + $$PWD/generateoptions.h \ + $$PWD/generatorpluginmanager.h \ + $$PWD/installoptions.h \ + $$PWD/setupprojectparameters.h \ + $$PWD/toolchains.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 00000000..f3d213e2 --- /dev/null +++ b/src/lib/corelib/tools/tst_tools.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#undef QT_NO_CAST_FROM_ASCII // I am qmake, and I approve this hack. + +#include "tst_tools.h" + +#include "buildoptions.h" +#include "error.h" +#include "fileinfo.h" +#include "hostosinfo.h" +#include "processutils.h" +#include "profile.h" +#include "settings.h" +#include "setupprojectparameters.h" +#include "version.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace qbs { +namespace Internal { + +TestTools::TestTools(Settings *settings) : m_settings(settings) +{ +} + +TestTools::~TestTools() +{ + qDeleteAll(m_tmpDirs); +} + +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")); + QCOMPARE(FileInfo::path("/fileInRoot"), QString(QLatin1Char('/'))); + if (HostOsInfo::isWindowsHost()) + QCOMPARE(FileInfo::path("C:/fileInDriveRoot"), QString("C:/")); + 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(QDir::tempPath() + QLatin1String("/CamelCase")); + QVERIFY2(tempFile.open(), qPrintable(tempFile.errorString())); + 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() +{ + TemporaryProfile tpp("parent", m_settings); + Profile parentProfile = tpp.p; + TemporaryProfile tpc("child", m_settings); + Profile childProfile = tpc.p; + 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. + TemporaryProfile tpf("foo", m_settings); + Profile fooProfile = tpf.p; + fooProfile.setValue("testKey", "gnampf"); + childProfile.setBaseProfile("foo"); + QCOMPARE(childProfile.value("testKey", "none").toString(), QLatin1String("gnampf")); + + ErrorInfo errorInfo; + childProfile.setBaseProfile("SmurfAlongWithMe"); + childProfile.value("blubb", QString(), &errorInfo); + QVERIFY(errorInfo.hasError()); + + errorInfo.clear(); + childProfile.setBaseProfile("parent"); + parentProfile.setBaseProfile("child"); + QVERIFY(!childProfile.value("blubb", QString(), &errorInfo).isValid()); + QVERIFY(errorInfo.hasError()); + + QVERIFY(!childProfile.allKeys(Profile::KeySelectionNonRecursive).isEmpty()); + + errorInfo.clear(); + QVERIFY(childProfile.allKeys(Profile::KeySelectionRecursive, &errorInfo).isEmpty()); + QVERIFY(errorInfo.hasError()); +} + +void TestTools::testSettingsMigration() +{ + QFETCH(QString, baseDir); + QFETCH(bool, hasOldSettings); + Settings settings(baseDir); + if (hasOldSettings) { + QVERIFY(QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles/right.txt") + .exists()); + QCOMPARE(settings.value("key").toString(), + settings.baseDirectory() + "/qbs/" QBS_VERSION "/profilesright"); + } else { + QVERIFY(!QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles").exists()); + QVERIFY(settings.allKeys().isEmpty()); + } +} + +void TestTools::testSettingsMigration_data() +{ + QTest::addColumn("baseDir"); + QTest::addColumn("hasOldSettings"); + QTest::newRow("settings dir with lots of versions") << setupSettingsDir1() << true; + QTest::newRow("settings dir with only a fallback") << setupSettingsDir2() << true; + QTest::newRow("no previous settings") << setupSettingsDir3() << false; +} + +QString TestTools::setupSettingsDir1() +{ + QTemporaryDir * const baseDir = new QTemporaryDir; + m_tmpDirs << baseDir; + + const Version thisVersion = Version::fromString(QBS_VERSION); + Version predecessor; + if (thisVersion.patchLevel() > 0) { + predecessor.setMajorVersion(thisVersion.majorVersion()); + predecessor.setMinorVersion(thisVersion.minorVersion()); + predecessor.setPatchLevel(thisVersion.patchLevel() - 1); + } else if (thisVersion.minorVersion() > 0) { + predecessor.setMajorVersion(thisVersion.majorVersion()); + predecessor.setMinorVersion(thisVersion.minorVersion() - 1); + predecessor.setPatchLevel(99); + } else { + predecessor.setMajorVersion(thisVersion.majorVersion() - 1); + predecessor.setMajorVersion(99); + predecessor.setPatchLevel(99); + } + const auto versions = QList() << Version(0, 1, 0) << Version(1, 0, 5) << predecessor + << Version(thisVersion.majorVersion() + 1, thisVersion.minorVersion(), + thisVersion.patchLevel()) + << Version(thisVersion.majorVersion(), thisVersion.minorVersion() + 1, + thisVersion.patchLevel()) + << Version(thisVersion.majorVersion(), thisVersion.minorVersion(), + thisVersion.patchLevel() + 1) + << Version(99, 99, 99); + foreach (const Version &v, versions) { + const QString settingsDir = baseDir->path() + "/qbs/" + v.toString(); + QSettings s(settingsDir + "/qbs.conf", + HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat); + const QString profilesDir = settingsDir + "/profiles"; + QDir::root().mkpath(profilesDir); + const QString magicString = v == predecessor ? "right" : "wrong"; + QFile f(profilesDir + '/' + magicString + ".txt"); + f.open(QIODevice::WriteOnly); + s.setValue("org/qt-project/qbs/key", profilesDir + magicString); + } + + return baseDir->path(); +} + +QString TestTools::setupSettingsDir2() +{ + QTemporaryDir * const baseDir = new QTemporaryDir; + m_tmpDirs << baseDir; + const QString settingsDir = baseDir->path(); + QSettings s(settingsDir + QLatin1String("/qbs.conf"), + HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat); + const QString profilesDir = settingsDir + QLatin1String("/qbs/profiles"); + QDir::root().mkpath(profilesDir); + QFile f(profilesDir + "/right.txt"); + f.open(QIODevice::WriteOnly); + s.setValue("org/qt-project/qbs/key", profilesDir + "right"); + + return baseDir->path(); +} + +QString TestTools::setupSettingsDir3() +{ + auto * const baseDir = new QTemporaryDir; + m_tmpDirs << baseDir; + return baseDir->path(); +} + +void TestTools::testBuildConfigMerging() +{ + Settings settings((QString())); + TemporaryProfile tp(QLatin1String("tst_tools_profile"), &settings); + Profile profile = tp.p; + profile.setValue(QLatin1String("topLevelKey"), QLatin1String("topLevelValue")); + profile.setValue(QLatin1String("qbs.toolchain"), QLatin1String("gcc")); + profile.setValue(QLatin1String("qbs.architecture"), + QLatin1String("Jean-Claude Pillemann")); + profile.setValue(QLatin1String("cpp.treatWarningsAsErrors"), true); + QVariantMap overrideMap; + overrideMap.insert(QLatin1String("qbs.toolchain"), QLatin1String("clang")); + overrideMap.insert(QLatin1String("qbs.installRoot"), QLatin1String("/blubb")); + SetupProjectParameters params; + params.setTopLevelProfile(profile.name()); + params.setConfigurationName(QLatin1String("debug")); + params.setOverriddenValues(overrideMap); + const ErrorInfo error = params.expandBuildConfiguration(); + QVERIFY2(!error.hasError(), qPrintable(error.toString())); + 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(), 4); + QCOMPARE(finalQbsMap.value(QLatin1String("toolchain")).toString(), + QString::fromLatin1("clang")); + QCOMPARE(finalQbsMap.value(QLatin1String("configurationName")).toString(), + QString::fromLatin1("debug")); + QCOMPARE(finalQbsMap.value(QLatin1String("architecture")).toString(), + QString::fromLatin1("Jean-Claude Pillemann")); + QCOMPARE(finalQbsMap.value(QLatin1String("installRoot")).toString(), QLatin1String("/blubb")); + const QVariantMap finalCppMap = finalMap.value(QLatin1String("cpp")).toMap(); + QCOMPARE(finalCppMap.count(), 1); + QCOMPARE(finalCppMap.value(QLatin1String("treatWarningsAsErrors")).toBool(), true); +} + +void TestTools::testProcessNameByPid() +{ + QCOMPARE(qAppName(), processNameByPid(QCoreApplication::applicationPid())); +} + +} // 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 00000000..e71ccd0c --- /dev/null +++ b/src/lib/corelib/tools/tst_tools.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qbs_export.h" + +#include +#include + +QT_BEGIN_NAMESPACE +class QTemporaryDir; +QT_END_NAMESPACE + +namespace qbs { +class Settings; + +namespace Internal { + +class QBS_EXPORT TestTools : public QObject +{ + Q_OBJECT + +public: + TestTools(Settings *settings); + ~TestTools(); + +private slots: + void fileCaseCheck(); + void testBuildConfigMerging(); + void testFileInfo(); + void testProcessNameByPid(); + void testProfiles(); + void testSettingsMigration(); + void testSettingsMigration_data(); + +private: + QString setupSettingsDir1(); + QString setupSettingsDir2(); + QString setupSettingsDir3(); + + Settings * const m_settings; + QList m_tmpDirs; +}; + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/version.cpp b/src/lib/corelib/tools/version.cpp new file mode 100644 index 00000000..9e1cbfed --- /dev/null +++ b/src/lib/corelib/tools/version.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "version.h" + +#include +#include + +namespace qbs { +namespace Internal { + +Version::Version(int major, int minor, int patch, int buildNr) + : m_major(major), m_minor(minor), m_patch(patch), m_build(buildNr) +{ +} + +int Version::majorVersion() const +{ + return m_major; +} + +void Version::setMajorVersion(int major) +{ + m_major = major; +} + +int Version::minorVersion() const +{ + return m_minor; +} + +void Version::setMinorVersion(int minor) +{ + m_minor = minor; +} +int Version::patchLevel() const +{ + return m_patch; +} + +void Version::setPatchLevel(int patch) +{ + m_patch = patch; +} + +int Version::buildNumber() const +{ + return m_build; +} + +void Version::setBuildNumber(int nr) +{ + m_build = nr; +} + +Version Version::fromString(const QString &versionString, bool buildNumberAllowed) +{ + QString pattern = QLatin1String("(\\d+)"); // At least one number. + for (int i = 0; i < 2; ++i) + pattern += QLatin1String("(?:\\.(\\d+))?"); // Followed by a dot and a number up to two times. + if (buildNumberAllowed) + pattern += QLatin1String("(?:-(\\d+))?"); // And possibly a dash followed by the build number. + QRegExp rex(pattern); + if (!rex.exactMatch(versionString)) + return Version(); + const int majorNr = rex.cap(1).toInt(); + const int minorNr = rex.captureCount() >= 2 ? rex.cap(2).toInt() : 0; + const int patchNr = rex.captureCount() >= 3 ? rex.cap(3).toInt() : 0; + const int buildNr = rex.captureCount() >= 4 ? rex.cap(4).toInt() : 0; + return Version(majorNr, minorNr, patchNr, buildNr); +} + +QString Version::toString() const +{ + QString s; + if (m_build) + s.sprintf("%d.%d.%d-%d", m_major, m_minor, m_patch, m_build); + else + s.sprintf("%d.%d.%d", m_major, m_minor, m_patch); + return s; +} + +const Version &Version::qbsVersion() +{ + static const Version v = fromString(QLatin1String(QBS_VERSION)); + return v; +} + +int compare(const Version &lhs, const Version &rhs) +{ + if (lhs.majorVersion() < rhs.majorVersion()) + return -1; + if (lhs.majorVersion() > rhs.majorVersion()) + return 1; + if (lhs.minorVersion() < rhs.minorVersion()) + return -1; + if (lhs.minorVersion() > rhs.minorVersion()) + return 1; + if (lhs.patchLevel() < rhs.patchLevel()) + return -1; + if (lhs.patchLevel() > rhs.patchLevel()) + return 1; + if (lhs.buildNumber() < rhs.buildNumber()) + return -1; + if (lhs.buildNumber() > rhs.buildNumber()) + return 1; + return 0; +} + +VersionRange::VersionRange(const Version &minVersion, const Version &maxVersion) + : minimum(minVersion), maximum(maxVersion) +{ +} + +VersionRange &VersionRange::narrowDown(const VersionRange &other) +{ + if (other.minimum > minimum) + minimum = other.minimum; + if (other.maximum.isValid() && other.maximum < maximum) + maximum = other.maximum; + return *this; +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/version.h b/src/lib/corelib/tools/version.h new file mode 100644 index 00000000..f87d5c02 --- /dev/null +++ b/src/lib/corelib/tools/version.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_VERSION_H +#define QBS_VERSION_H + +#include "qbs_export.h" + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { + +class QBS_EXPORT Version +{ +public: + explicit Version(int majorVersion = 0, int minorVersion = 0, int patchLevel = 0, + int buildNr = 0); + + bool isValid() const { return m_major || m_minor || m_patch || m_build; } + + int majorVersion() const; + void setMajorVersion(int majorVersion); + + int minorVersion() const; + void setMinorVersion(int minorVersion); + + int patchLevel() const; + void setPatchLevel(int patchLevel); + + int buildNumber() const; + void setBuildNumber(int nr); + + static Version fromString(const QString &versionString, bool buildNumberAllowed = false); + QString toString() const; + + static const Version &qbsVersion(); + +private: + int m_major; + int m_minor; + int m_patch; + int m_build; +}; + +class VersionRange +{ +public: + VersionRange() = default; + VersionRange(const Version &minVersion, const Version &maxVersion); + + Version minimum; + Version maximum; // exclusive + + VersionRange &narrowDown(const VersionRange &other); +}; + +QBS_EXPORT int compare(const Version &lhs, const Version &rhs); +inline bool operator==(const Version &lhs, const Version &rhs) { return compare(lhs, rhs) == 0; } +inline bool operator!=(const Version &lhs, const Version &rhs) { return !operator==(lhs, rhs); } +inline bool operator<(const Version &lhs, const Version &rhs) { return compare(lhs, rhs) < 0; } +inline bool operator>(const Version &lhs, const Version &rhs) { return compare(lhs, rhs) > 0; } +inline bool operator<=(const Version &lhs, const Version &rhs) { return !operator>(lhs, rhs); } +inline bool operator>=(const Version &lhs, const Version &rhs) { return !operator<(lhs, rhs); } + +} // namespace Internal +} // namespace qbs + +#endif // QBS_VERSION_H diff --git a/src/lib/corelib/tools/visualstudioversioninfo.cpp b/src/lib/corelib/tools/visualstudioversioninfo.cpp new file mode 100644 index 00000000..1022c250 --- /dev/null +++ b/src/lib/corelib/tools/visualstudioversioninfo.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "visualstudioversioninfo.h" +#include +#include +#include + +namespace qbs { +namespace Internal { + +VisualStudioVersionInfo::VisualStudioVersionInfo(const Version &version) + : m_version(version) +{ + QBS_CHECK(version.minorVersion() == 0 || version == Version(7, 1)); +} + +QSet VisualStudioVersionInfo::knownVersions() +{ + QSet known; + known << Version(14) << Version(12) << Version(11) + << Version(10) << Version(9) << Version(8) + << Version(7, 1) << Version(7) << Version(6); + return known; +} + +bool VisualStudioVersionInfo::operator<(const VisualStudioVersionInfo &other) const +{ + return m_version < other.m_version; +} + +bool VisualStudioVersionInfo::operator==(const VisualStudioVersionInfo &other) const +{ + return m_version == other.m_version; +} + +bool VisualStudioVersionInfo::usesMsBuild() const +{ + return m_version.majorVersion() >= 10; +} + +bool VisualStudioVersionInfo::usesVcBuild() const +{ + return m_version.majorVersion() <= 9; +} + +bool VisualStudioVersionInfo::usesSolutions() const +{ + return m_version.majorVersion() >= 7; +} + +Version VisualStudioVersionInfo::version() const +{ + return m_version; +} + +int VisualStudioVersionInfo::marketingVersion() const +{ + switch (m_version.majorVersion()) { + case 6: + return 6; + case 7: + switch (m_version.minorVersion()) { + case 0: + return 2002; + case 1: + return 2003; + default: + Q_UNREACHABLE(); + } + break; + case 8: + return 2005; + case 9: + return 2008; + case 10: + return 2010; + case 11: + return 2012; + case 12: + return 2013; + case 14: + return 2015; + case 15: + return 2017; + default: + qWarning() << QStringLiteral("unrecognized Visual Studio version: ") + << m_version.toString(); + return 0; + } +} + +Version VisualStudioVersionInfo::clCompilerVersion() const +{ + return Version(m_version.majorVersion() + (m_version.majorVersion() >= 14 ? 5 : 6), + m_version.minorVersion() * 10); +} + +QString VisualStudioVersionInfo::solutionVersion() const +{ + // Visual Studio 2012 finally stabilized the solution version + if (m_version >= Version(11)) + return QStringLiteral("12.00"); + + if (m_version >= Version(8)) + return QStringLiteral("%1.00").arg(m_version.majorVersion() + 1); + + if (m_version >= Version(7, 1)) + return QStringLiteral("8.00"); + + if (m_version >= Version(7)) + return QStringLiteral("7.00"); + + // these versions do not use solution files + // Visual Studio 6 uses .dsw files which are format version 6.00 but these are different + Q_ASSERT(!usesSolutions()); + Q_UNREACHABLE(); +} + +QString VisualStudioVersionInfo::toolsVersion() const +{ + // "https://msdn.microsoft.com/en-us/library/bb383796.aspx" + // Starting in Visual Studio 2013, the MSBuild Toolset version is the same as the Visual Studio + // version number"... again + if (m_version >= Version(12)) + return QStringLiteral("%1.0").arg(m_version.majorVersion()); + + if (m_version >= Version(10)) + return QStringLiteral("4.0"); + + // pre-MSBuild + return QStringLiteral("%1,00").arg(m_version.majorVersion()); +} + +QString VisualStudioVersionInfo::platformToolsetVersion() const +{ + return QStringLiteral("v%1").arg(m_version.majorVersion() * 10); +} + +quint32 qHash(const VisualStudioVersionInfo &info) +{ + return qHash(info.version().toString()); +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/visualstudioversioninfo.h b/src/lib/corelib/tools/visualstudioversioninfo.h new file mode 100644 index 00000000..2629e349 --- /dev/null +++ b/src/lib/corelib/tools/visualstudioversioninfo.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_VISUALSTUDIOVERSIONINFO_H +#define QBS_VISUALSTUDIOVERSIONINFO_H + +#include "qbs_export.h" + +#include +#include +#include + +namespace qbs { +namespace Internal { + +class QBS_EXPORT VisualStudioVersionInfo +{ +public: + VisualStudioVersionInfo(const Version &version); + + static QSet knownVersions(); + + bool operator<(const VisualStudioVersionInfo &other) const; + bool operator==(const VisualStudioVersionInfo &other) const; + + bool usesMsBuild() const; + bool usesVcBuild() const; + bool usesSolutions() const; + + Version version() const; + int marketingVersion() const; + Version clCompilerVersion() const; + + QString solutionVersion() const; + QString toolsVersion() const; + QString platformToolsetVersion() const; + +private: + Version m_version; +}; + +quint32 qHash(const VisualStudioVersionInfo &info); + +} // namespace Internal +} // namespace qbs + +#endif // QBS_VISUALSTUDIOVERSIONINFO_H diff --git a/src/lib/corelib/tools/vsenvironmentdetector.cpp b/src/lib/corelib/tools/vsenvironmentdetector.cpp new file mode 100644 index 00000000..4d359f85 --- /dev/null +++ b/src/lib/corelib/tools/vsenvironmentdetector.cpp @@ -0,0 +1,247 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "vsenvironmentdetector.h" + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#include +#endif + +namespace qbs { +namespace Internal { + +static QString windowsSystem32Path() +{ +#ifdef Q_OS_WIN + wchar_t str[UNICODE_STRING_MAX_CHARS]; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, 0, str))) + return QString::fromUtf16(reinterpret_cast(str)); +#endif + return QString(); +} + +VsEnvironmentDetector::VsEnvironmentDetector() + : m_windowsSystemDirPath(windowsSystem32Path()) +{ +} + +bool VsEnvironmentDetector::start(MSVC *msvc) +{ + return start(QVector() << msvc); +} + +bool VsEnvironmentDetector::start(QVector msvcs) +{ + std::sort(msvcs.begin(), msvcs.end(), [] (const MSVC *a, const MSVC *b) -> bool { + return a->vcInstallPath < b->vcInstallPath; + }); + + QVector compatibleMSVCs; + QString lastVcInstallPath; + foreach (MSVC *msvc, msvcs) { + if (lastVcInstallPath != msvc->vcInstallPath) { + lastVcInstallPath = msvc->vcInstallPath; + if (!compatibleMSVCs.isEmpty()) { + startDetection(compatibleMSVCs); + compatibleMSVCs.clear(); + } + } + compatibleMSVCs.append(msvc); + } + startDetection(compatibleMSVCs); + return true; +} + +QString VsEnvironmentDetector::findVcVarsAllBat(const MSVC &msvc) const +{ + // ### We can only rely on MSVC.vcInstallPath being set + // when this is called from utilitiesextension.cpp :-( + // If we knew the vsInstallPath at this point we could just use that + // instead of searching for vcvarsall.bat candidates. + QDir dir(msvc.vcInstallPath); + for (;;) { + if (!dir.cdUp()) + return QString(); + if (dir.dirName() == QLatin1String("VC")) + break; + } + const QString vcvarsallbat = QStringLiteral("vcvarsall.bat"); + QString path = vcvarsallbat; + if (dir.exists(path)) + return dir.absoluteFilePath(path); + path = QLatin1String("Auxiliary/Build/") + vcvarsallbat; + if (dir.exists(path)) + return dir.absoluteFilePath(path); + return QString(); +} + +bool VsEnvironmentDetector::startDetection(const QVector &compatibleMSVCs) +{ + const QString vcvarsallbat = findVcVarsAllBat(*compatibleMSVCs.first()); + if (vcvarsallbat.isEmpty()) { + m_errorString = Tr::tr("Cannot find 'vcvarsall.bat'."); + return false; + } + + QTemporaryFile tmpFile(QDir::tempPath() + QLatin1Char('/') + QLatin1String("XXXXXX.bat")); + if (!tmpFile.open()) { + m_errorString = Tr::tr("Cannot open temporary file '%1' for writing.").arg( + tmpFile.fileName()); + return false; + } + + writeBatchFile(&tmpFile, vcvarsallbat, compatibleMSVCs); + tmpFile.flush(); + + QProcess process; + const QString shellFilePath = QLatin1String("cmd.exe"); + process.start(shellFilePath, QStringList() + << QLatin1String("/C") << tmpFile.fileName()); + if (!process.waitForStarted()) { + m_errorString = Tr::tr("Failed to start '%1'.").arg(shellFilePath); + return false; + } + process.waitForFinished(-1); + if (process.exitStatus() != QProcess::NormalExit) { + m_errorString = Tr::tr("Process '%1' did not exit normally.").arg(shellFilePath); + return false; + } + if (process.exitCode() != 0) { + m_errorString = Tr::tr("Failed to detect Visual Studio environment."); + return false; + } + parseBatOutput(process.readAllStandardOutput(), compatibleMSVCs); + return true; + +} + +static void batClearVars(QTextStream &s, const QStringList &varnames) +{ + foreach (const QString &varname, varnames) + s << "set " << varname << '=' << endl; +} + +static void batPrintVars(QTextStream &s, const QStringList &varnames) +{ + foreach (const QString &varname, varnames) + s << "echo " << varname << "=%" << varname << '%' << endl; +} + +static QString vcArchitecture(const MSVC *msvc) +{ + QString vcArch = msvc->architecture; + if (msvc->architecture == QLatin1String("armv7")) + vcArch = QLatin1String("arm"); + if (msvc->architecture == QLatin1String("x86_64")) + vcArch = QLatin1String("amd64"); + + for (const QString &hostPrefix : + QStringList({QStringLiteral("x86"), QStringLiteral("amd64_"), QStringLiteral("x86_")})) { + if (QFile::exists(msvc->clPathForArchitecture(hostPrefix + vcArch))) { + vcArch.prepend(hostPrefix); + break; + } + } + + return vcArch; +} + +void VsEnvironmentDetector::writeBatchFile(QIODevice *device, const QString &vcvarsallbat, + const QVector &msvcs) const +{ + const QStringList varnames = QStringList() << QLatin1String("PATH") + << QLatin1String("INCLUDE") << QLatin1String("LIB"); + QTextStream s(device); + s << "@echo off" << endl; + for (const MSVC *msvc : msvcs) { + s << "echo --" << msvc->architecture << "--" << endl + << "setlocal" << endl; + batClearVars(s, varnames); + s << "set PATH=" << m_windowsSystemDirPath << endl; // vcvarsall.bat needs tools from here + s << "call \"" << vcvarsallbat << "\" " << vcArchitecture(msvc) + << " || exit /b 1" << endl; + batPrintVars(s, varnames); + s << "endlocal" << endl; + } +} + +void VsEnvironmentDetector::parseBatOutput(const QByteArray &output, QVector msvcs) +{ + QString arch; + QProcessEnvironment *targetEnv = 0; + foreach (QByteArray line, output.split('\n')) { + line = line.trimmed(); + if (line.isEmpty()) + continue; + + if (line.startsWith("--") && line.endsWith("--")) { + line.remove(0, 2); + line.chop(2); + arch = QString::fromLocal8Bit(line); + targetEnv = &msvcs.takeFirst()->environment; + } else { + int idx = line.indexOf('='); + if (idx < 0) + continue; + QBS_CHECK(targetEnv); + const QString name = QString::fromLocal8Bit(line.left(idx)); + QString value = QString::fromLocal8Bit(line.mid(idx + 1)); + if (name.compare(QLatin1String("PATH"), Qt::CaseInsensitive) == 0) + value.remove(m_windowsSystemDirPath); + if (value.endsWith(QLatin1Char(';'))) + value.chop(1); + if (value.endsWith(QLatin1Char('\\'))) + value.chop(1); + targetEnv->insert(name, value); + } + } +} + +} // namespace Internal +} // namespace qbs diff --git a/src/lib/corelib/tools/vsenvironmentdetector.h b/src/lib/corelib/tools/vsenvironmentdetector.h new file mode 100644 index 00000000..bf715b28 --- /dev/null +++ b/src/lib/corelib/tools/vsenvironmentdetector.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_VSENVIRONMENTDETECTOR_H +#define QBS_VSENVIRONMENTDETECTOR_H + +#include "qbs_export.h" +#include "msvcinfo.h" + +#include + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +namespace qbs { +namespace Internal { + +class MSVC; + +class QBS_EXPORT VsEnvironmentDetector +{ +public: + VsEnvironmentDetector(); + + bool start(MSVC *msvc); + bool start(QVector msvcs); + QString errorString() const { return m_errorString; } + +private: + QString findVcVarsAllBat(const MSVC &msvc) const; + bool startDetection(const QVector &compatibleMSVCs); + void writeBatchFile(QIODevice *device, const QString &vcvarsallbat, const QVector &msvcs) const; + void parseBatOutput(const QByteArray &output, QVector msvcs); + + const QString m_windowsSystemDirPath; + QString m_errorString; +}; + +} // namespace Internal +} // namespace qbs + +#endif // QBS_VSENVIRONMENTDETECTOR_H diff --git a/src/lib/corelib/tools/weakpointer.h b/src/lib/corelib/tools/weakpointer.h new file mode 100644 index 00000000..edf91336 --- /dev/null +++ b/src/lib/corelib/tools/weakpointer.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_WEAKPOINTER_H +#define QBS_WEAKPOINTER_H + +#include + +namespace qbs { +namespace Internal { + +template class WeakPointer : public QWeakPointer +{ +public: + WeakPointer() : QWeakPointer() {} + WeakPointer(const QSharedPointer &sharedPointer) : QWeakPointer(sharedPointer) {} + template WeakPointer(const QSharedPointer &sp) : QWeakPointer(sp) { } + + + operator T*() const { return checkedData(); } + T *operator->() const { return checkedData(); } + T operator*() const { return *checkedData(); } + +private: + T *checkedData() const { + T * const d = QWeakPointer::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 00000000..4b075147 --- /dev/null +++ b/src/lib/corelib/use_corelib.pri @@ -0,0 +1,47 @@ +include(../../../qbs_version.pri) +include(../../library_dirname.pri) + +isEmpty(QBSLIBDIR) { + QBSLIBDIR = $$OUT_PWD/../../../$${QBS_LIBRARY_DIRNAME} +} + +unix { + LIBS += -L$$QBSLIBDIR -lqbscore +} + +!qbs_disable_rpath { + linux-*:QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,\$\$ORIGIN/../$${QBS_LIBRARY_DIRNAME}\' + macx:QMAKE_LFLAGS += -Wl,-rpath,@loader_path/../$${QBS_LIBRARY_DIRNAME} +} + +!CONFIG(static, static|shared) { + QBSCORELIBSUFFIX = $$QBS_VERSION_MAJ +} + +win32 { + CONFIG(debug, debug|release) { + QBSCORELIB = qbscored$$QBSCORELIBSUFFIX + } + CONFIG(release, debug|release) { + QBSCORELIB = qbscore$$QBSCORELIBSUFFIX + } + 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 +} +qbs_enable_project_file_updates:DEFINES += QBS_ENABLE_PROJECT_FILE_UPDATES diff --git a/src/lib/corelib/use_installed_corelib.pri b/src/lib/corelib/use_installed_corelib.pri new file mode 100644 index 00000000..d8bed503 --- /dev/null +++ b/src/lib/corelib/use_installed_corelib.pri @@ -0,0 +1,37 @@ +include(qbs_version.pri) + +QBSLIBDIR=$${PWD}/../../lib +unix { + LIBS += -L$$QBSLIBDIR -lqbscore +} + +!qbs_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 + } + 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 +} +qbs_enable_project_file_updates:DEFINES += QBS_ENABLE_PROJECT_FILE_UPDATES diff --git a/src/lib/library.pri b/src/lib/library.pri new file mode 100644 index 00000000..0ab69840 --- /dev/null +++ b/src/lib/library.pri @@ -0,0 +1,46 @@ +include(../library_dirname.pri) +include(../install_prefix.pri) + +TEMPLATE = lib +QT = core +!isEmpty(QBS_DLLDESTDIR):DLLDESTDIR = $${QBS_DLLDESTDIR} +else:DLLDESTDIR = ../../../bin +!isEmpty(QBS_DESTDIR):DESTDIR = $${QBS_DESTDIR} +else:DESTDIR = ../../../$${QBS_LIBRARY_DIRNAME} +CONFIG(static, static|shared) { + DEFINES += QBS_STATIC_LIB +} else { + DEFINES += QBS_LIBRARY +} +DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_PROCESS_COMBINED_ARGUMENT_START +INCLUDEPATH += $${PWD}/../ +contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols +win32:CONFIG(debug, debug|release):TARGET = $${TARGET}d +CONFIG += c++11 + +qbs_disable_rpath { + osx:QMAKE_LFLAGS_SONAME = -Wl,-install_name,$$QBS_INSTALL_PREFIX/$$QBS_LIBRARY_DIRNAME/ +} else { + osx:QMAKE_LFLAGS_SONAME = -Wl,-install_name,@rpath/ +} +include(../../qbs_version.pri) +VERSION = $${QBS_VERSION} + +linux { + # Turn off absurd qmake's soname "logic" and directly add the linker flag. + QMAKE_LFLAGS_SONAME = + QMAKE_LFLAGS = -Wl,-soname=lib$${TARGET}.so.$${QBS_VERSION_MAJ}.$${QBS_VERSION_MIN} +} + +win32 { + dlltarget.path = $${QBS_INSTALL_PREFIX}/bin + INSTALLS += dlltarget +} + +!win32|!qbs_no_dev_install { + !isEmpty(QBS_LIB_INSTALL_DIR): \ + target.path = $${QBS_LIB_INSTALL_DIR} + else: \ + target.path = $${QBS_INSTALL_PREFIX}/$${QBS_LIBRARY_DIRNAME} + INSTALLS += target +} diff --git a/src/lib/libs.qbs b/src/lib/libs.qbs new file mode 100644 index 00000000..b9756b44 --- /dev/null +++ b/src/lib/libs.qbs @@ -0,0 +1,9 @@ +import qbs + +Project { + references: [ + "corelib/corelib.qbs", + "corelib/generators/generators.qbs", + "qtprofilesetup/qtprofilesetup.qbs", + ] +} diff --git a/src/lib/qtprofilesetup/qtenvironment.h b/src/lib/qtprofilesetup/qtenvironment.h new file mode 100644 index 00000000..3bc2c6ec --- /dev/null +++ b/src/lib/qtprofilesetup/qtenvironment.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_QTENVIRONMENT_H +#define QBS_QTENVIRONMENT_H + +#include + +#include + +namespace qbs { + +class QBS_EXPORT QtEnvironment { +public: + QtEnvironment() + : staticBuild(false), frameworkBuild(false) + { + } + + QString installPrefixPath; + QString libraryPath; + QString includePath; + QString binaryPath; + QString qmlPath; + QString qmlImportPath; + QString documentationPath; + QString dataPath; + QString pluginPath; + QString qtLibInfix; + QString qtNameSpace; + QString mkspecPath; + QString mkspecName; + QString mkspecBasePath; + QStringList entryPointLibsDebug; + QStringList entryPointLibsRelease; + QStringList buildVariant; + QStringList configItems; + QStringList qtConfigItems; + QString architecture; + QString qtVersion; + int qtMajorVersion; + int qtMinorVersion; + int qtPatchVersion; + bool staticBuild; + bool frameworkBuild; +}; + +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/qtprofilesetup/qtmoduleinfo.cpp b/src/lib/qtprofilesetup/qtmoduleinfo.cpp new file mode 100644 index 00000000..93a84645 --- /dev/null +++ b/src/lib/qtprofilesetup/qtmoduleinfo.cpp @@ -0,0 +1,672 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qtmoduleinfo.h" + +#include "qtenvironment.h" + +#include +#include +#include + +#include +#include +#include + +namespace qbs { +namespace Internal { + +typedef QHash NamePathHash; +static void replaceQtLibNamesWithFilePath(const NamePathHash &namePathHash, QStringList *libList) +{ + for (int i = 0; i < libList->count(); ++i) { + QString &lib = (*libList)[i]; + const NamePathHash::ConstIterator it = namePathHash.find(lib); + if (it != namePathHash.constEnd()) + lib = it.value(); + } +} + +static void replaceQtLibNamesWithFilePath(QList *modules, const QtEnvironment &qtEnv) +{ + // We don't want to add the libraries for Qt modules via "-l", because of the + // danger that a wrong one will be picked up, e.g. from /usr/lib. Instead, + // we pull them in using the full file path. + typedef QHash NamePathHash; + NamePathHash linkerNamesToFilePathsDebug; + NamePathHash linkerNamesToFilePathsRelease; + foreach (const QtModuleInfo &m, *modules) { + linkerNamesToFilePathsDebug.insert(m.libNameForLinker(qtEnv, true), m.libFilePathDebug); + linkerNamesToFilePathsRelease.insert(m.libNameForLinker(qtEnv, false), + m.libFilePathRelease); + } + for (int i = 0; i < modules->count(); ++i) { + QtModuleInfo &module = (*modules)[i]; + replaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, &module.dynamicLibrariesDebug); + replaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, &module.staticLibrariesDebug); + replaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, + &module.dynamicLibrariesRelease); + replaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease, + &module.staticLibrariesRelease); + } +} + + +QtModuleInfo::QtModuleInfo() + : isPrivate(false), hasLibrary(true), isStaticLibrary(false), isPlugin(false), mustExist(true) +{ +} + +QtModuleInfo::QtModuleInfo(const QString &name, const QString &qbsName, const QStringList &deps) + : name(name), qbsName(qbsName), dependencies(deps), + isPrivate(qbsName.endsWith(QLatin1String("-private"))), + hasLibrary(!isPrivate), + isStaticLibrary(false), + isPlugin(false), + mustExist(true) +{ + const QString coreModule = QLatin1String("core"); + if (qbsName != coreModule && !dependencies.contains(coreModule)) + dependencies.prepend(coreModule); +} + +QString QtModuleInfo::moduleNameWithoutPrefix() const +{ + if (modulePrefix.isEmpty() && name.startsWith(QLatin1String("Qt"))) + return name.mid(2); // Strip off "Qt". + if (name.startsWith(modulePrefix)) + return name.mid(modulePrefix.length()); + return name; +} + +QString QtModuleInfo::frameworkHeadersPath(const QtEnvironment &qtEnvironment) const +{ + return qtEnvironment.libraryPath + QLatin1Char('/') + name + + QLatin1String(".framework/Headers"); +} + +QStringList QtModuleInfo::qt4ModuleIncludePaths(const QtEnvironment &qtEnvironment) const +{ + QStringList paths; + if (isFramework(qtEnvironment)) { + paths << frameworkHeadersPath(qtEnvironment); + } else { + paths << qtEnvironment.includePath + << qtEnvironment.includePath + QLatin1Char('/') + name; + } + return paths; +} + +QString QtModuleInfo::libraryBaseName(const QtEnvironment &qtEnvironment, + bool debugBuild) const +{ + if (isPlugin) + return libBaseName(name, debugBuild, qtEnvironment); + + // Some modules use a different naming scheme, so it doesn't get boring. + const bool libNameBroken = name == QLatin1String("Enginio") + || name == QLatin1String("DataVisualization"); + + QString libName = modulePrefix.isEmpty() && !libNameBroken ? QLatin1String("Qt") : modulePrefix; + if (qtEnvironment.qtMajorVersion >= 5 && !isFramework(qtEnvironment) && !libNameBroken) + libName += QString::number(qtEnvironment.qtMajorVersion); + libName += moduleNameWithoutPrefix(); + libName += qtEnvironment.qtLibInfix; + return libBaseName(libName, debugBuild, qtEnvironment); +} + +QString QtModuleInfo::libNameForLinker(const QtEnvironment &qtEnvironment, bool debugBuild) const +{ + if (!hasLibrary) + return QString(); + QString libName = libraryBaseName(qtEnvironment, debugBuild); + if (qtEnvironment.mkspecName.contains(QLatin1String("msvc"))) + libName += QLatin1String(".lib"); + return libName; +} + +void QtModuleInfo::setupLibraries(const QtEnvironment &qtEnv, QSet *nonExistingPrlFiles) +{ + setupLibraries(qtEnv, true, nonExistingPrlFiles); + setupLibraries(qtEnv, false, nonExistingPrlFiles); +} + +static QStringList makeList(const QByteArray &s) +{ + return QString::fromLatin1(s).split(QLatin1Char(' '), QString::SkipEmptyParts); +} + +void QtModuleInfo::setupLibraries(const QtEnvironment &qtEnv, bool debugBuild, + QSet *nonExistingPrlFiles) +{ + if (!hasLibrary) + return; // Can happen for Qt4 convenience modules, like "widgets". + + if (debugBuild) { + if (!qtEnv.buildVariant.contains(QLatin1String("debug"))) + return; + const QStringList modulesNeverBuiltAsDebug = QStringList() + << QLatin1String("bootstrap") << QLatin1String("qmldevtools"); + foreach (const QString &m, modulesNeverBuiltAsDebug) { + if (qbsName == m || qbsName == m + QLatin1String("-private")) + return; + } + } else if (!qtEnv.buildVariant.contains(QLatin1String("release"))) { + return; + } + + QStringList &libs = isStaticLibrary + ? (debugBuild ? staticLibrariesDebug : staticLibrariesRelease) + : (debugBuild ? dynamicLibrariesDebug : dynamicLibrariesRelease); + QStringList &frameworks = debugBuild ? frameworksDebug : frameworksRelease; + QStringList &frameworkPaths = debugBuild ? frameworkPathsDebug : frameworkPathsRelease; + QStringList &flags = debugBuild ? linkerFlagsDebug : linkerFlagsRelease; + QString &libFilePath = debugBuild ? libFilePathDebug : libFilePathRelease; + + if (qtEnv.mkspecName.contains(QLatin1String("ios")) && isStaticLibrary) { + const QtModuleInfo platformSupportModule(QLatin1String("QtPlatformSupport"), + QLatin1String("platformsupport")); + libs << QLatin1String("z") << QLatin1String("m") + << platformSupportModule.libNameForLinker(qtEnv, debugBuild); + flags << QLatin1String("-force_load") + << qtEnv.pluginPath + QLatin1String("/platforms/") + + libBaseName(QLatin1String("libqios"), debugBuild, qtEnv) + + QLatin1String(".a"); + } + + QString prlFilePath = isPlugin + ? qtEnv.pluginPath + QLatin1Char('/') + pluginData.type + : qtEnv.libraryPath; + prlFilePath += QLatin1Char('/'); + if (isFramework(qtEnv)) + prlFilePath.append(libraryBaseName(qtEnv, false)).append(QLatin1String(".framework/")); + const QString libDir = prlFilePath; + if (!qtEnv.mkspecName.startsWith(QLatin1String("win")) && !isFramework(qtEnv)) + prlFilePath += QLatin1String("lib"); + prlFilePath.append(libraryBaseName(qtEnv, debugBuild)); + const bool isNonStaticQt4OnWindows = qtEnv.mkspecName.startsWith(QLatin1String("win")) + && !isStaticLibrary && qtEnv.qtMajorVersion < 5; + if (isNonStaticQt4OnWindows) + prlFilePath.chop(1); // The prl file base name does *not* contain the version number... + prlFilePath.append(QLatin1String(".prl")); + if (nonExistingPrlFiles->contains(prlFilePath)) + return; + QFile prlFile(prlFilePath); + if (!prlFile.open(QIODevice::ReadOnly)) { + // We can't error out here, as some modules in a self-built Qt don't have the expected + // file names. Real-life example: "libQt0Feedback.prl". This is just too stupid + // to work around, so let's ignore it. + if (mustExist) { + qDebug("Skipping prl file '%s', because it cannot be opened (%s).", + qPrintable(prlFilePath), + qPrintable(prlFile.errorString())); + } + nonExistingPrlFiles->insert(prlFilePath); + return; + } + const QList prlLines = prlFile.readAll().split('\n'); + foreach (const QByteArray &line, prlLines) { + const QByteArray simplifiedLine = line.simplified(); + const int equalsOffset = simplifiedLine.indexOf('='); + if (equalsOffset == -1) + continue; + if (simplifiedLine.startsWith("QMAKE_PRL_TARGET")) { + const bool isMingw = qtEnv.mkspecName.startsWith(QLatin1String("win")) + && qtEnv.mkspecName.contains(QLatin1String("g++")); + const bool isQtVersionBefore56 = qtEnv.qtMajorVersion < 5 + || (qtEnv.qtMajorVersion == 5 && qtEnv.qtMinorVersion < 6); + libFilePath = libDir; + + // QMAKE_PRL_TARGET has a "lib" prefix, except for mingw. + // Of course, the exception has an exception too: For static libs, mingw *does* + // have the "lib" prefix. TODO: Shoot the people responsible for this. + if (isQtVersionBefore56 && isMingw && !isStaticLibrary) + libFilePath += QLatin1String("lib"); + + libFilePath += QString::fromLatin1(simplifiedLine.mid(equalsOffset + 1).trimmed()); + if (isNonStaticQt4OnWindows) + libFilePath += QString::number(4); // This is *not* part of QMAKE_PRL_TARGET... + if (isQtVersionBefore56) { + if (qtEnv.mkspecName.contains(QLatin1String("msvc"))) + libFilePath += QLatin1String(".lib"); + else if (isMingw) + libFilePath += QLatin1String(".a"); + } + continue; + } + if (!simplifiedLine.startsWith("QMAKE_PRL_LIBS")) + continue; + + // Assuming lib names and directories without spaces here. + QStringList parts = QString::fromLatin1(simplifiedLine.mid(equalsOffset + 1).trimmed()) + .split(QLatin1Char(' '), QString::SkipEmptyParts); + for (int i = 0; i < parts.count(); ++i) { + QString part = parts.at(i); + part.replace(QLatin1String("$$[QT_INSTALL_LIBS]"), qtEnv.libraryPath); + if (part.startsWith(QLatin1String("-l"))) { + libs << part.mid(2); + } else if (part.startsWith(QLatin1String("-L"))) { + libraryPaths << part.mid(2); + } else if (part.startsWith(QLatin1String("-F"))) { + frameworkPaths << part.mid(2); + } else if (part == QLatin1String("-framework")) { + if (++i < parts.count()) + frameworks << parts.at(i); + } else if (part.startsWith(QLatin1Char('-'))) { // Some other option, e.g. "-pthread". + flags << part; + } else if (part.startsWith(QLatin1String("/LIBPATH:"))) { + libraryPaths << part.mid(9).replace(QLatin1String("\\\\"), QLatin1String("/")); + } else { // Assume it's a file path/name. + libs << part.replace(QLatin1String("\\\\"), QLatin1String("/")); + } + } + + return; + } +} + +bool QtModuleInfo::isFramework(const QtEnvironment &qtEnv) const +{ + if (!qtEnv.frameworkBuild || isStaticLibrary) + return false; + const QStringList modulesNeverBuiltAsFrameworks = QStringList() + << QLatin1String("bootstrap") << QLatin1String("openglextensions") + << QLatin1String("platformsupport") << QLatin1String("qmldevtools") + << QLatin1String("uitools") << QLatin1String("harfbuzzng"); + return !modulesNeverBuiltAsFrameworks.contains(qbsName); +} + +// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users +// for that. +static void addTestModule(QList &modules) +{ + QtModuleInfo testModule(QLatin1String("QtTest"), QLatin1String("test"), + QStringList() << QLatin1String("testlib")); + testModule.hasLibrary = false; + modules << testModule; +} + +// See above. +static void addDesignerComponentsModule(QList &modules) +{ + QtModuleInfo module(QLatin1String("QtDesignerComponents"), + QLatin1String("designercomponents"), + QStringList() << QLatin1String("designercomponents-private")); + module.hasLibrary = false; + modules << module; +} + + +QList allQt4Modules(const QtEnvironment &qtEnvironment) +{ + // as per http://doc.qt.io/qt-4.8/modules.html + private stuff. + QList modules; + + QtModuleInfo core(QLatin1String("QtCore"), QLatin1String("core")); + core.compilerDefines << QLatin1String("QT_CORE_LIB"); + if (!qtEnvironment.qtNameSpace.isEmpty()) + core.compilerDefines << QLatin1String("QT_NAMESPACE=") + qtEnvironment.qtNameSpace; + + modules = QList() + << core + << QtModuleInfo(QLatin1String("QtCore"), QLatin1String("core-private"), + QStringList() << QLatin1String("core")) + << QtModuleInfo(QLatin1String("QtGui"), QLatin1String("gui")) + << QtModuleInfo(QLatin1String("QtGui"), QLatin1String("gui-private"), + QStringList() << QLatin1String("gui")) + << QtModuleInfo(QLatin1String("QtMultimedia"), QLatin1String("multimedia"), + QStringList() << QLatin1String("gui") << QLatin1String("network")) + << QtModuleInfo(QLatin1String("QtMultimedia"), QLatin1String("multimedia-private"), + QStringList() << QLatin1String("multimedia")) + << QtModuleInfo(QLatin1String("QtNetwork"), QLatin1String("network")) + << QtModuleInfo(QLatin1String("QtNetwork"), QLatin1String("network-private"), + QStringList() << QLatin1String("network")) + << QtModuleInfo(QLatin1String("QtOpenGL"), QLatin1String("opengl"), + QStringList() << QLatin1String("gui")) + << QtModuleInfo(QLatin1String("QtOpenGL"), QLatin1String("opengl-private"), + QStringList() << QLatin1String("opengl")) + << QtModuleInfo(QLatin1String("QtOpenVG"), QLatin1String("openvg"), + QStringList() << QLatin1String("gui")) + << QtModuleInfo(QLatin1String("QtScript"), QLatin1String("script")) + << QtModuleInfo(QLatin1String("QtScript"), QLatin1String("script-private"), + QStringList() << QLatin1String("script")) + << QtModuleInfo(QLatin1String("QtScriptTools"), QLatin1String("scripttols"), + QStringList() << QLatin1String("script") << QLatin1String("gui")) + << QtModuleInfo(QLatin1String("QtScriptTools"), QLatin1String("scripttols-private"), + QStringList() << QLatin1String("scripttols")) + << QtModuleInfo(QLatin1String("QtSql"), QLatin1String("sql")) + << QtModuleInfo(QLatin1String("QtSql"), QLatin1String("sql-private"), + QStringList() << QLatin1String("sql")) + << QtModuleInfo(QLatin1String("QtSvg"), QLatin1String("svg"), + QStringList() << QLatin1String("gui")) + << QtModuleInfo(QLatin1String("QtSvg"), QLatin1String("svg-private"), + QStringList() << QLatin1String("svg")) + << QtModuleInfo(QLatin1String("QtWebKit"), QLatin1String("webkit"), + QStringList() << QLatin1String("gui") << QLatin1String("network")) + << QtModuleInfo(QLatin1String("QtWebKit"), QLatin1String("webkit-private"), + QStringList() << QLatin1String("webkit")) + << QtModuleInfo(QLatin1String("QtXml"), QLatin1String("xml")) + << QtModuleInfo(QLatin1String("QtXml"), QLatin1String("xml-private"), + QStringList() << QLatin1String("xml")) + << QtModuleInfo(QLatin1String("QtXmlPatterns"), QLatin1String("xmlpatterns"), + QStringList() << QLatin1String("network")) + << QtModuleInfo(QLatin1String("QtXmlPatterns"), + QLatin1String("xmlpatterns-private"), + QStringList() << QLatin1String("xmlpatterns")) + << QtModuleInfo(QLatin1String("QtDeclarative"), QLatin1String("declarative"), + QStringList() << QLatin1String("gui") << QLatin1String("script")) + << QtModuleInfo(QLatin1String("QtDeclarative"), + QLatin1String("declarative-private"), + QStringList() << QLatin1String("declarative")) + << QtModuleInfo(QLatin1String("QtDesigner"), QLatin1String("designer"), + QStringList() << QLatin1String("gui") << QLatin1String("xml")) + << QtModuleInfo(QLatin1String("QtDesigner"), QLatin1String("designer-private"), + QStringList() << QLatin1String("designer")) + << QtModuleInfo(QLatin1String("QtUiTools"), QLatin1String("uitools")) + << QtModuleInfo(QLatin1String("QtUiTools"), QLatin1String("uitools-private"), + QStringList() << QLatin1String("uitools")) + << QtModuleInfo(QLatin1String("QtHelp"), QLatin1String("help"), + QStringList() << QLatin1String("network") << QLatin1String("sql")) + << QtModuleInfo(QLatin1String("QtHelp"), QLatin1String("help-private"), + QStringList() << QLatin1String("help")) + << QtModuleInfo(QLatin1String("QtTest"), QLatin1String("testlib")) + << QtModuleInfo(QLatin1String("QtTest"), QLatin1String("testlib-private"), + QStringList() << QLatin1String("testlib")); + + if (qtEnvironment.mkspecName.startsWith(QLatin1String("win"))) { + QtModuleInfo axcontainer(QLatin1String("QAxContainer"), QLatin1String("axcontainer")); + axcontainer.modulePrefix = QLatin1String("Q"); + axcontainer.isStaticLibrary = true; + axcontainer.includePaths << qtEnvironment.includePath + QLatin1String("/ActiveQt"); + modules << axcontainer; + + QtModuleInfo axserver = axcontainer; + axserver.name = QLatin1String("QAxServer"); + axserver.qbsName = QLatin1String("axserver"); + axserver.compilerDefines = QStringList() << QLatin1String("QAXSERVER"); + modules << axserver; + } else { + modules << QtModuleInfo(QLatin1String("QtDBus"), QLatin1String("dbus")) + << QtModuleInfo(QLatin1String("QtDBus"), QLatin1String("dbus-private"), + QStringList() << QLatin1String("dbus")); + } + + QtModuleInfo designerComponentsPrivate(QLatin1String("QtDesignerComponents"), + QLatin1String("designercomponents-private"), + QStringList() << QLatin1String("gui-private") << QLatin1String("designer-private")); + designerComponentsPrivate.hasLibrary = true; + modules << designerComponentsPrivate; + + QtModuleInfo phonon(QLatin1String("Phonon"), QLatin1String("phonon")); + phonon.includePaths = phonon.qt4ModuleIncludePaths(qtEnvironment); + modules << phonon; + + // Set up include paths that haven't been set up before this point. + for (QList::iterator it = modules.begin(); it != modules.end(); ++it) { + QtModuleInfo &module = *it; + if (!module.includePaths.isEmpty()) + continue; + module.includePaths = module.qt4ModuleIncludePaths(qtEnvironment); + } + + // Set up compiler defines haven't been set up before this point. + for (QList::iterator it = modules.begin(); it != modules.end(); ++it) { + QtModuleInfo &module = *it; + if (!module.compilerDefines.isEmpty()) + continue; + module.compilerDefines + << QLatin1String("QT_") + module.qbsName.toUpper() + QLatin1String("_LIB"); + } + + // These are for the convenience of project file authors. It allows them + // to add a dependency to e.g. "Qt.widgets" without a version check. + QtModuleInfo virtualModule; + virtualModule.hasLibrary = false; + virtualModule.qbsName = QLatin1String("widgets"); + virtualModule.dependencies = QStringList() << QLatin1String("core") << QLatin1String("gui"); + modules << virtualModule; + virtualModule.qbsName = QLatin1String("quick"); + virtualModule.dependencies = QStringList() << QLatin1String("declarative"); + modules << virtualModule; + virtualModule.qbsName = QLatin1String("concurrent"); + virtualModule.dependencies = QStringList() << QLatin1String("core"); + modules << virtualModule; + virtualModule.qbsName = QLatin1String("printsupport"); + virtualModule.dependencies = QStringList() << QLatin1String("core") << QLatin1String("gui"); + modules << virtualModule; + + addTestModule(modules); + addDesignerComponentsModule(modules); + + const QStringList modulesThatCanBeDisabled = QStringList() << QLatin1String("xmlpatterns") + << QLatin1String("multimedia") << QLatin1String("phonon") << QLatin1String("svg") + << QLatin1String("webkit") << QLatin1String("script") << QLatin1String("scripttools") + << QLatin1String("declarative") << QLatin1String("gui") << QLatin1String("dbus") + << QLatin1String("opengl") << QLatin1String("openvg"); + for (int i = 0; i < modules.count(); ++i) { + QString name = modules[i].qbsName; + name.remove(QLatin1String("-private")); + if (modulesThatCanBeDisabled.contains(name)) + modules[i].mustExist = false; + } + + QSet nonExistingPrlFiles; + for (int i = 0; i < modules.count(); ++i) { + QtModuleInfo &module = modules[i]; + if (qtEnvironment.staticBuild) + module.isStaticLibrary = true; + module.setupLibraries(qtEnvironment, &nonExistingPrlFiles); + } + replaceQtLibNamesWithFilePath(&modules, qtEnvironment); + + return modules; +} + +static QList getPriFileContentsRecursively(const Profile &profile, + const QString &priFilePath) +{ + QFile priFile(priFilePath); + if (!priFile.open(QIODevice::ReadOnly)) { + throw ErrorInfo(Tr::tr("Setting up Qt profile '%1' failed: Cannot open " + "file '%2' (%3).").arg(profile.name(), priFile.fileName(), priFile.errorString())); + } + QList lines = priFile.readAll().split('\n'); + for (int i = 0; i < lines.count(); ++i) { + const QByteArray includeString = "include("; + const QByteArray &line = lines.at(i).trimmed(); + if (!line.startsWith(includeString)) + continue; + const int offset = includeString.count(); + const int closingParenPos = line.indexOf(')', offset); + if (closingParenPos == -1) { + qDebug("Warning: Invalid include statement in '%s'", qPrintable(priFilePath)); + continue; + } + const QString includedFilePath + = QString::fromLocal8Bit(line.mid(offset, closingParenPos - offset)); + const QList &includedContents + = getPriFileContentsRecursively(profile, includedFilePath); + int j = i; + foreach (const QByteArray &includedLine, includedContents) + lines.insert(++j, includedLine); + lines.removeAt(i--); + } + return lines; +} + +QList allQt5Modules(const Profile &profile, const QtEnvironment &qtEnvironment) +{ + QSet nonExistingPrlFiles; + QList modules; + QDirIterator dit(qtEnvironment.mkspecBasePath + QLatin1String("/modules")); + while (dit.hasNext()) { + const QString moduleFileNamePrefix = QLatin1String("qt_lib_"); + const QString pluginFileNamePrefix = QLatin1String("qt_plugin_"); + const QString moduleFileNameSuffix = QLatin1String(".pri"); + dit.next(); + const bool fileHasPluginPrefix = dit.fileName().startsWith(pluginFileNamePrefix); + if ((!fileHasPluginPrefix && !dit.fileName().startsWith(moduleFileNamePrefix)) + || !dit.fileName().endsWith(moduleFileNameSuffix)) { + continue; + } + QtModuleInfo moduleInfo; + moduleInfo.isPlugin = fileHasPluginPrefix; + const QString fileNamePrefix + = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleFileNamePrefix; + moduleInfo.qbsName = dit.fileName().mid(fileNamePrefix.count(), + dit.fileName().count() - fileNamePrefix.count() + - moduleFileNameSuffix.count()); + if (moduleInfo.isPlugin) { + moduleInfo.name = moduleInfo.qbsName; + moduleInfo.isStaticLibrary = true; + } + const QByteArray moduleKeyPrefix = QByteArray(moduleInfo.isPlugin ? "QT_PLUGIN" : "QT") + + '.' + moduleInfo.qbsName.toLatin1() + '.'; + moduleInfo.qbsName.replace(QLatin1String("_private"), QLatin1String("-private")); + bool hasV2 = false; + bool hasModuleEntry = false; + foreach (const QByteArray &line, getPriFileContentsRecursively(profile, dit.filePath())) { + const QByteArray simplifiedLine = line.simplified(); + const int firstEqualsOffset = simplifiedLine.indexOf('='); + if (firstEqualsOffset == -1) + continue; + const QByteArray key = simplifiedLine.left(firstEqualsOffset).trimmed(); + const QByteArray value = simplifiedLine.mid(firstEqualsOffset + 1).trimmed(); + if (!key.startsWith(moduleKeyPrefix) || value.isEmpty()) + continue; + if (key.endsWith(".name")) { + moduleInfo.name = QString::fromLocal8Bit(value); + } else if (key.endsWith(".module")) { + hasModuleEntry = true; + } else if (key.endsWith(".depends")) { + moduleInfo.dependencies = QString::fromLocal8Bit(value).split(QLatin1Char(' ')); + for (int i = 0; i < moduleInfo.dependencies.count(); ++i) { + moduleInfo.dependencies[i].replace(QLatin1String("_private"), + QLatin1String("-private")); + } + } else if (key.endsWith(".module_config")) { + foreach (const QByteArray &elem, value.split(' ')) { + if (elem == "no_link") + moduleInfo.hasLibrary = false; + else if (elem == "staticlib") + moduleInfo.isStaticLibrary = true; + else if (elem == "internal_module") + moduleInfo.isPrivate = true; + else if (elem == "v2") + hasV2 = true; + } + } else if (key.endsWith(".includes")) { + moduleInfo.includePaths = QString::fromLocal8Bit(value).split(QLatin1Char(' ')); + for (int i = 0; i < moduleInfo.includePaths.count(); ++i) { + moduleInfo.includePaths[i] + .replace(QLatin1String("$$QT_MODULE_INCLUDE_BASE"), + qtEnvironment.includePath) + .replace(QLatin1String("$$QT_MODULE_LIB_BASE"), + qtEnvironment.libraryPath); + } + } else if (key.endsWith(".DEFINES")) { + moduleInfo.compilerDefines = QString::fromLocal8Bit(value) + .split(QLatin1Char(' '), QString::SkipEmptyParts); + } else if (key.endsWith(".VERSION")) { + moduleInfo.version = QString::fromLocal8Bit(value); + } else if (key.endsWith(".plugin_types")) { + moduleInfo.supportedPluginTypes = makeList(value); + } else if (key.endsWith(".TYPE")) { + moduleInfo.pluginData.type = QString::fromLatin1(value); + } else if (key.endsWith(".EXTENDS")) { + moduleInfo.pluginData.extends = QString::fromLatin1(value); + } else if (key.endsWith(".CLASS_NAME")) { + moduleInfo.pluginData.className = QString::fromLatin1(value); + } + } + if (hasV2 && !hasModuleEntry) + moduleInfo.hasLibrary = false; + + // Fix include paths for Apple frameworks. + // The qt_lib_XXX.pri files contain wrong values for versions < 5.6. + if (!hasV2 && moduleInfo.isFramework(qtEnvironment)) { + moduleInfo.includePaths.clear(); + QString baseIncDir = moduleInfo.frameworkHeadersPath(qtEnvironment); + if (moduleInfo.isPrivate) { + baseIncDir += QLatin1Char('/') + moduleInfo.version; + moduleInfo.includePaths + << baseIncDir + << baseIncDir + QLatin1Char('/') + moduleInfo.name; + } else { + moduleInfo.includePaths << baseIncDir; + } + } + + moduleInfo.setupLibraries(qtEnvironment, &nonExistingPrlFiles); + + modules << moduleInfo; + if (moduleInfo.qbsName == QLatin1String("testlib")) + addTestModule(modules); + if (moduleInfo.qbsName == QLatin1String("designercomponents-private")) + addDesignerComponentsModule(modules); + } + + replaceQtLibNamesWithFilePath(&modules, qtEnvironment); + return modules; +} + +QString QtModuleInfo::libBaseName(const QString &libName, bool debugBuild, + const QtEnvironment &qtEnvironment) const +{ + QString name = libName; + if (qtEnvironment.mkspecName.startsWith(QLatin1String("win"))) { + if (debugBuild) + name += QLatin1Char('d'); + if (!isStaticLibrary && qtEnvironment.qtMajorVersion < 5) + name += QString::number(qtEnvironment.qtMajorVersion); + } + if (qtEnvironment.mkspecName.contains(QLatin1String("macx")) + || qtEnvironment.mkspecName.contains(QLatin1String("ios")) + || qtEnvironment.mkspecName.contains(QLatin1String("darwin"))) { + if (!isFramework(qtEnvironment) + && qtEnvironment.buildVariant.contains(QLatin1String("debug")) + && (!qtEnvironment.buildVariant.contains(QLatin1String("release")) || debugBuild)) { + name += QLatin1String("_debug"); + } + } + return name; +} + +} // namespace Internal +} // namespace qbs + diff --git a/src/lib/qtprofilesetup/qtmoduleinfo.h b/src/lib/qtprofilesetup/qtmoduleinfo.h new file mode 100644 index 00000000..3087361c --- /dev/null +++ b/src/lib/qtprofilesetup/qtmoduleinfo.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_QTMODULEINFO_H +#define QBS_QTMODULEINFO_H + +#include +#include + +namespace qbs { +class QtEnvironment; +class Profile; + +namespace Internal { + +class QtModuleInfo +{ +public: + QtModuleInfo(); + QtModuleInfo(const QString &name, const QString &qbsName, + const QStringList &deps = QStringList()); + + QString moduleNameWithoutPrefix() const; + QString frameworkHeadersPath(const QtEnvironment &qtEnvironment) const; + QStringList qt4ModuleIncludePaths(const QtEnvironment &qtEnvironment) const; + QString libraryBaseName(const QtEnvironment &qtEnvironment, bool debugBuild) const; + QString libBaseName(const QString &libName, bool debugBuild, + const QtEnvironment &qtEnvironment) const; + QString libNameForLinker(const QtEnvironment &qtEnvironment, bool debugBuild) const; + void setupLibraries(const QtEnvironment &qtEnv, QSet *nonExistingPrlFiles); + bool isFramework(const QtEnvironment &qtEnv) const; + + QString modulePrefix; // default is empty and means "Qt". + QString name; // As in the path to the headers and ".name" in the pri files. + QString qbsName; // Lower-case version without "qt" prefix. + QString version; + QStringList dependencies; // qbs names. + QStringList includePaths; + QStringList compilerDefines; + QStringList staticLibrariesDebug; + QStringList staticLibrariesRelease; + QStringList dynamicLibrariesDebug; + QStringList dynamicLibrariesRelease; + QStringList linkerFlagsDebug; + QStringList linkerFlagsRelease; + QString libFilePathDebug; + QString libFilePathRelease; + QStringList frameworksDebug; + QStringList frameworksRelease; + QStringList frameworkPathsDebug; + QStringList frameworkPathsRelease; + QStringList libraryPaths; + bool isPrivate; + bool hasLibrary; + bool isStaticLibrary; + bool isPlugin; + bool mustExist; + QStringList supportedPluginTypes; + + struct PluginData { + QString type; + QString extends; + QString className; + } pluginData; + +private: + void setupLibraries(const QtEnvironment &qtEnv, bool debugBuild, + QSet *nonExistingPrlFiles); +}; + +QList allQt4Modules(const QtEnvironment &qtEnvironment); +QList allQt5Modules(const Profile &profile, const QtEnvironment &qtEnvironment); + +} // namespace Internal +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/qtprofilesetup/qtprofilesetup.cpp b/src/lib/qtprofilesetup/qtprofilesetup.cpp new file mode 100644 index 00000000..8e15ac01 --- /dev/null +++ b/src/lib/qtprofilesetup/qtprofilesetup.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtprofilesetup.h" + +#include "qtmoduleinfo.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace qbs { +using namespace Internal; + +template +QByteArray utf8JSLiteral(T t) +{ + return toJSLiteral(t).toUtf8(); +} + +static QString pathToJSLiteral(const QString &path) +{ + return toJSLiteral(QDir::fromNativeSeparators(path)); +} + +static QString defaultQpaPlugin(const Profile &profile, const QtModuleInfo &module, + const QtEnvironment &qtEnv) +{ + if (qtEnv.qtMajorVersion < 5) + return QString(); + QFile qConfigPri(qtEnv.mkspecBasePath + QLatin1String("/qconfig.pri")); + if (!qConfigPri.open(QIODevice::ReadOnly)) { + throw ErrorInfo(Tr::tr("Setting up Qt profile '%1' failed: Cannot open " + "file '%2' (%3).") + .arg(profile.name(), qConfigPri.fileName(), qConfigPri.errorString())); + } + const QList lines = qConfigPri.readAll().split('\n'); + const QByteArray magicString = "QT_DEFAULT_QPA_PLUGIN ="; + foreach (const QByteArray &line, lines) { + const QByteArray simplifiedLine = line.simplified(); + if (simplifiedLine.startsWith(magicString)) + return QString::fromLatin1(simplifiedLine.mid(magicString.count()).trimmed()); + } + if (module.isStaticLibrary) + qDebug("Warning: Could not determine default QPA plugin for static Qt."); + return QString(); +} + +static void replaceSpecialValues(QByteArray *content, const Profile &profile, + const QtModuleInfo &module, const QtEnvironment &qtEnvironment) +{ + content->replace("@name@", utf8JSLiteral(module.moduleNameWithoutPrefix())); + content->replace("@has_library@", utf8JSLiteral(module.hasLibrary)); + content->replace("@dependencies@", utf8JSLiteral(module.dependencies)); + content->replace("@includes@", utf8JSLiteral(module.includePaths)); + content->replace("@staticLibsDebug@", utf8JSLiteral(module.staticLibrariesDebug)); + content->replace("@staticLibsRelease@", utf8JSLiteral(module.staticLibrariesRelease)); + content->replace("@dynamicLibsDebug@", utf8JSLiteral(module.dynamicLibrariesDebug)); + content->replace("@dynamicLibsRelease@", utf8JSLiteral(module.dynamicLibrariesRelease)); + content->replace("@linkerFlagsDebug@", utf8JSLiteral(module.linkerFlagsDebug)); + content->replace("@linkerFlagsRelease@", utf8JSLiteral(module.linkerFlagsRelease)); + content->replace("@libraryPaths@", utf8JSLiteral(module.libraryPaths)); + content->replace("@frameworkPathsDebug@", utf8JSLiteral(module.frameworkPathsDebug)); + content->replace("@frameworkPathsRelease@", utf8JSLiteral(module.frameworkPathsRelease)); + content->replace("@frameworksDebug@", utf8JSLiteral(module.frameworksDebug)); + content->replace("@frameworksRelease@", utf8JSLiteral(module.frameworksRelease)); + content->replace("@libFilePathDebug@", utf8JSLiteral(module.libFilePathDebug)); + content->replace("@libFilePathRelease@", utf8JSLiteral(module.libFilePathRelease)); + content->replace("@libNameForLinkerDebug@", + utf8JSLiteral(module.libNameForLinker(qtEnvironment, true))); + content->replace("@libNameForLinkerRelease@", + utf8JSLiteral(module.libNameForLinker(qtEnvironment, false))); + content->replace("@entryPointLibsDebug@", utf8JSLiteral(qtEnvironment.entryPointLibsDebug)); + content->replace("@entryPointLibsRelease@", utf8JSLiteral(qtEnvironment.entryPointLibsRelease)); + QByteArray propertiesString; + QByteArray compilerDefines = utf8JSLiteral(module.compilerDefines); + if (module.qbsName == QLatin1String("declarative") + || module.qbsName == QLatin1String("quick")) { + const QByteArray debugMacro = module.qbsName == QLatin1String("declarative") + || qtEnvironment.qtMajorVersion < 5 + ? "QT_DECLARATIVE_DEBUG" : "QT_QML_DEBUG"; + + const QString indent = QLatin1String(" "); + QTextStream s(&propertiesString); + s << "property bool qmlDebugging: false" << endl; + s << indent << "property string qmlPath"; + if (qtEnvironment.qmlPath.isEmpty()) + s << endl; + else + s << ": " << pathToJSLiteral(qtEnvironment.qmlPath) << endl; + + s << indent << "property string qmlImportsPath: " + << pathToJSLiteral(qtEnvironment.qmlImportPath); + + const QByteArray baIndent(4, ' '); + compilerDefines = "{\n" + + baIndent + baIndent + "var result = " + compilerDefines + ";\n" + + baIndent + baIndent + "if (qmlDebugging)\n" + + baIndent + baIndent + baIndent + "result.push(\"" + debugMacro + "\");\n" + + baIndent + baIndent + "return result;\n" + + baIndent + "}"; + } + content->replace("@defines@", compilerDefines); + if (module.qbsName == QLatin1String("gui")) { + content->replace("@defaultQpaPlugin@", + utf8JSLiteral(defaultQpaPlugin(profile, module, qtEnvironment))); + } + if (module.isStaticLibrary) { + if (!propertiesString.isEmpty()) + propertiesString += "\n "; + propertiesString += "isStaticLibrary: true"; + } + if (module.isPlugin) + content->replace("@className@", utf8JSLiteral(module.pluginData.className)); + content->replace("@special_properties@", propertiesString); +} + +static void copyTemplateFile(const QString &fileName, const QString &targetDirectory, + const Profile &profile, const QtEnvironment &qtEnv, QStringList *allFiles, + const QtModuleInfo *module = 0) +{ + if (!QDir::root().mkpath(targetDirectory)) { + throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: " + "Cannot create directory '%2'.") + .arg(profile.name(), targetDirectory)); + } + QFile sourceFile(QLatin1String(":/templates/") + fileName); + if (!sourceFile.open(QIODevice::ReadOnly)) { + throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: " + "Cannot open '%1' (%2).").arg(sourceFile.fileName(), sourceFile.errorString())); + } + QByteArray newContent = sourceFile.readAll(); + if (module) + replaceSpecialValues(&newContent, profile, *module, qtEnv); + sourceFile.close(); + const QString targetPath = targetDirectory + QLatin1Char('/') + fileName; + allFiles->append(QFileInfo(targetPath).absoluteFilePath()); + QFile targetFile(targetPath); + if (targetFile.open(QIODevice::ReadOnly)) { + if (newContent == targetFile.readAll()) // No need to overwrite anything in this case. + return; + targetFile.close(); + } + if (!targetFile.open(QIODevice::WriteOnly)) { + throw ErrorInfo(Internal::Tr::tr("Setting up Qt profile '%1' failed: " + "Cannot open '%1' (%2).").arg(targetFile.fileName(), targetFile.errorString())); + } + targetFile.resize(0); + targetFile.write(newContent); +} + +static void createModules(Profile &profile, Settings *settings, + const QtEnvironment &qtEnvironment) +{ + const QList modules = qtEnvironment.qtMajorVersion < 5 + ? allQt4Modules(qtEnvironment) + : allQt5Modules(profile, qtEnvironment); + const QString profileBaseDir = QString::fromLatin1("%1/profiles/%2") + .arg(QFileInfo(settings->fileName()).dir().absolutePath(), profile.name()); + const QString qbsQtModuleBaseDir = profileBaseDir + QLatin1String("/modules/Qt"); + QStringList allFiles; + copyTemplateFile(QLatin1String("QtModule.qbs"), qbsQtModuleBaseDir, profile, qtEnvironment, + &allFiles); + copyTemplateFile(QLatin1String("QtPlugin.qbs"), qbsQtModuleBaseDir, profile, qtEnvironment, + &allFiles); + foreach (const QtModuleInfo &module, modules) { + const QString qbsQtModuleDir = qbsQtModuleBaseDir + QLatin1Char('/') + module.qbsName; + QString moduleTemplateFileName; + if (module.qbsName == QLatin1String("core")) { + moduleTemplateFileName = QLatin1String("core.qbs"); + copyTemplateFile(QLatin1String("moc.js"), qbsQtModuleDir, profile, qtEnvironment, + &allFiles); + copyTemplateFile(QLatin1String("qdoc.js"), qbsQtModuleDir, profile, qtEnvironment, + &allFiles); + } else if (module.qbsName == QLatin1String("gui")) { + moduleTemplateFileName = QLatin1String("gui.qbs"); + } else if (module.qbsName == QLatin1String("scxml")) { + moduleTemplateFileName = QLatin1String("scxml.qbs"); + } else if (module.qbsName == QLatin1String("dbus")) { + moduleTemplateFileName = QLatin1String("dbus.qbs"); + copyTemplateFile(QLatin1String("dbus.js"), qbsQtModuleDir, profile, qtEnvironment, + &allFiles); + } else if (module.qbsName == QLatin1String("phonon")) { + moduleTemplateFileName = QLatin1String("phonon.qbs"); + } else if (module.isPlugin) { + moduleTemplateFileName = QLatin1String("plugin.qbs"); + } else { + moduleTemplateFileName = QLatin1String("module.qbs"); + } + copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, profile, qtEnvironment, &allFiles, + &module); + } + QDirIterator dit(qbsQtModuleBaseDir, QDirIterator::Subdirectories); + while (dit.hasNext()) { + dit.next(); + const QFileInfo &fi = dit.fileInfo(); + if (!fi.isFile()) + continue; + const QString filePath = fi.absoluteFilePath(); + if (!allFiles.contains(filePath) && !QFile::remove(filePath)) + qDebug("Warning: Failed to remove outdated file '%s'.", qPrintable(filePath)); + } + profile.setValue(QLatin1String("preferences.qbsSearchPaths"), profileBaseDir); +} + +static QString guessMinimumWindowsVersion(const QtEnvironment &qt) +{ + if (qt.mkspecName.startsWith(QLatin1String("winrt-"))) + return QLatin1String("6.2"); + + if (!qt.mkspecName.startsWith(QLatin1String("win32-"))) + return QString(); + + if (qt.architecture == QLatin1String("x86_64") + || qt.architecture == QLatin1String("ia64")) { + return QLatin1String("5.2"); + } + + QRegExp rex(QLatin1String("^win32-msvc(\\d+)$")); + if (rex.exactMatch(qt.mkspecName)) { + int msvcVersion = rex.cap(1).toInt(); + if (msvcVersion < 2012) + return QLatin1String("5.0"); + else + return QLatin1String("5.1"); + } + + return qt.qtMajorVersion < 5 ? QLatin1String("5.0") : QLatin1String("5.1"); +} + +static bool checkForStaticBuild(const QtEnvironment &qt) +{ + if (qt.qtMajorVersion >= 5) + return qt.qtConfigItems.contains(QLatin1String("static")); + if (qt.frameworkBuild) + return false; // there are no Qt4 static frameworks + const bool isWin = qt.mkspecName.startsWith(QLatin1String("win")); + const QDir libdir(isWin ? qt.binaryPath : qt.libraryPath); + const QStringList coreLibFiles + = libdir.entryList(QStringList(QLatin1String("*Core*")), QDir::Files); + if (coreLibFiles.isEmpty()) + throw ErrorInfo(Internal::Tr::tr("Could not determine whether Qt is a static build.")); + foreach (const QString &fileName, coreLibFiles) { + if (QLibrary::isLibrary(fileName)) + return false; + } + return true; +} + +static QStringList fillEntryPointLibs(const QtEnvironment &qtEnvironment, const Version &qtVersion, + bool debug) +{ + QStringList result; + QString qtmain = qtEnvironment.libraryPath + QLatin1Char('/'); + const bool isMinGW = qtEnvironment.mkspecName.startsWith(QLatin1String("win32-g++")); + if (isMinGW) + qtmain += QLatin1String("lib"); + qtmain += QLatin1String("qtmain") + qtEnvironment.qtLibInfix; + if (debug) + qtmain += QLatin1Char('d'); + if (isMinGW) { + qtmain += QLatin1String(".a"); + } else { + qtmain += QLatin1String(".lib"); + if (qtVersion >= Version(5, 4, 0)) + result << QLatin1String("Shell32.lib"); + } + result << qtmain; + return result; +} + +void doSetupQtProfile(const QString &profileName, Settings *settings, + const QtEnvironment &_qtEnvironment) +{ + QtEnvironment qtEnvironment = _qtEnvironment; + qtEnvironment.staticBuild = checkForStaticBuild(qtEnvironment); + + // determine whether user apps require C++11 + if (qtEnvironment.qtConfigItems.contains(QLatin1String("c++11")) && qtEnvironment.staticBuild) + qtEnvironment.configItems.append(QLatin1String("c++11")); + + Profile profile(profileName, settings); + profile.removeProfile(); + const QString settingsTemplate(QLatin1String("Qt.core.%1")); + profile.setValue(settingsTemplate.arg(QLatin1String("config")), qtEnvironment.configItems); + profile.setValue(settingsTemplate.arg(QLatin1String("qtConfig")), qtEnvironment.qtConfigItems); + profile.setValue(settingsTemplate.arg(QLatin1String("binPath")), qtEnvironment.binaryPath); + profile.setValue(settingsTemplate.arg(QLatin1String("libPath")), qtEnvironment.libraryPath); + profile.setValue(settingsTemplate.arg(QLatin1String("pluginPath")), qtEnvironment.pluginPath); + profile.setValue(settingsTemplate.arg(QLatin1String("incPath")), qtEnvironment.includePath); + profile.setValue(settingsTemplate.arg(QLatin1String("mkspecPath")), qtEnvironment.mkspecPath); + profile.setValue(settingsTemplate.arg(QLatin1String("docPath")), + qtEnvironment.documentationPath); + profile.setValue(settingsTemplate.arg(QLatin1String("version")), qtEnvironment.qtVersion); + profile.setValue(settingsTemplate.arg(QLatin1String("libInfix")), qtEnvironment.qtLibInfix); + profile.setValue(settingsTemplate.arg(QLatin1String("availableBuildVariants")), + qtEnvironment.buildVariant); + profile.setValue(settingsTemplate.arg(QLatin1String("staticBuild")), qtEnvironment.staticBuild); + + // Set the minimum operating system versions appropriate for this Qt version + const QString windowsVersion = guessMinimumWindowsVersion(qtEnvironment); + QString macosVersion, iosVersion, androidVersion; + + if (!windowsVersion.isEmpty()) { // Is target OS Windows? + const Version qtVersion = Version(qtEnvironment.qtMajorVersion, + qtEnvironment.qtMinorVersion, + qtEnvironment.qtPatchVersion); + qtEnvironment.entryPointLibsDebug = fillEntryPointLibs(qtEnvironment, qtVersion, true); + qtEnvironment.entryPointLibsRelease = fillEntryPointLibs(qtEnvironment, qtVersion, false); + } else if (qtEnvironment.mkspecPath.contains(QLatin1String("macx"))) { + profile.setValue(settingsTemplate.arg(QLatin1String("frameworkBuild")), qtEnvironment.frameworkBuild); + if (qtEnvironment.qtMajorVersion >= 5) { + macosVersion = QLatin1String("10.6"); + } else if (qtEnvironment.qtMajorVersion == 4 && qtEnvironment.qtMinorVersion >= 6) { + QDir qconfigDir; + if (qtEnvironment.frameworkBuild) { + qconfigDir.setPath(qtEnvironment.libraryPath); + qconfigDir.cd(QLatin1String("QtCore.framework/Headers")); + } else { + qconfigDir.setPath(qtEnvironment.includePath); + qconfigDir.cd(QLatin1String("Qt")); + } + QFile qconfig(qconfigDir.absoluteFilePath(QLatin1String("qconfig.h"))); + if (qconfig.open(QIODevice::ReadOnly)) { + bool qtCocoaBuild = false; + QTextStream ts(&qconfig); + QString line; + do { + line = ts.readLine(); + if (QRegExp(QLatin1String("\\s*#define\\s+QT_MAC_USE_COCOA\\s+1\\s*"), + Qt::CaseSensitive).exactMatch(line)) { + qtCocoaBuild = true; + break; + } + } while (!line.isNull()); + + if (ts.status() == QTextStream::Ok) + macosVersion = qtCocoaBuild ? QLatin1String("10.5") : QLatin1String("10.4"); + } + + if (macosVersion.isEmpty()) { + throw ErrorInfo(Internal::Tr::tr("Error reading qconfig.h; could not determine " + "whether Qt is using Cocoa or Carbon")); + } + } + + if (qtEnvironment.qtConfigItems.contains(QLatin1String("c++11"))) + macosVersion = QLatin1String("10.7"); + } + + if (qtEnvironment.mkspecPath.contains(QLatin1String("ios")) && qtEnvironment.qtMajorVersion >= 5) + iosVersion = QLatin1String("5.0"); + + if (qtEnvironment.mkspecPath.contains(QLatin1String("android"))) { + if (qtEnvironment.qtMajorVersion >= 5) + androidVersion = QLatin1String("2.3"); + else if (qtEnvironment.qtMajorVersion == 4 && qtEnvironment.qtMinorVersion >= 8) + androidVersion = QLatin1String("1.6"); // Necessitas + } + + // ### TODO: wince, winphone, blackberry + + if (!windowsVersion.isEmpty()) + profile.setValue(QLatin1String("cpp.minimumWindowsVersion"), windowsVersion); + + if (!macosVersion.isEmpty()) + profile.setValue(QLatin1String("cpp.minimumMacosVersion"), macosVersion); + + if (!iosVersion.isEmpty()) + profile.setValue(QLatin1String("cpp.minimumIosVersion"), iosVersion); + + if (!androidVersion.isEmpty()) + profile.setValue(QLatin1String("cpp.minimumAndroidVersion"), androidVersion); + + createModules(profile, settings, qtEnvironment); +} + +ErrorInfo setupQtProfile(const QString &profileName, Settings *settings, + const QtEnvironment &qtEnvironment) +{ + try { + doSetupQtProfile(profileName, settings, qtEnvironment); + return ErrorInfo(); + } catch (const ErrorInfo &e) { + return e; + } +} + +} // namespace qbs diff --git a/src/lib/qtprofilesetup/qtprofilesetup.h b/src/lib/qtprofilesetup/qtprofilesetup.h new file mode 100644 index 00000000..613aedf4 --- /dev/null +++ b/src/lib/qtprofilesetup/qtprofilesetup.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_SETUPQTPROFILE_H +#define QBS_SETUPQTPROFILE_H + +#include "qtenvironment.h" + +#include + +#include + +namespace qbs { +class ErrorInfo; +class Settings; + +QBS_EXPORT ErrorInfo setupQtProfile(const QString &profileName, Settings *settings, + const QtEnvironment &qtEnvironment); + +} // namespace qbs + +#endif // Include guard. diff --git a/src/lib/qtprofilesetup/qtprofilesetup.pro b/src/lib/qtprofilesetup/qtprofilesetup.pro new file mode 100644 index 00000000..cc89001b --- /dev/null +++ b/src/lib/qtprofilesetup/qtprofilesetup.pro @@ -0,0 +1,24 @@ +TARGET = qbsqtprofilesetup +include(../library.pri) +include(../corelib/use_corelib.pri) + +HEADERS = \ + qtenvironment.h \ + qtmoduleinfo.h \ + qtprofilesetup.h + +SOURCES = \ + qtmoduleinfo.cpp \ + qtprofilesetup.cpp + +RESOURCES = templates.qrc + +!qbs_no_dev_install { + header.files = qtenvironment.h qtprofilesetup.h + header.path = $${QBS_INSTALL_PREFIX}/include/qbs + use_pri.files = use_installed_qtprofilesetup.pri + use_pri.path = $${header.path} + INSTALLS += header use_pri +} + +OTHER_FILES += templates/* diff --git a/src/lib/qtprofilesetup/qtprofilesetup.qbs b/src/lib/qtprofilesetup/qtprofilesetup.qbs new file mode 100644 index 00000000..7f739e10 --- /dev/null +++ b/src/lib/qtprofilesetup/qtprofilesetup.qbs @@ -0,0 +1,29 @@ +import qbs + +QbsLibrary { + name: "qbsqtprofilesetup" + Depends { name: "qbscore" } + + Group { + name: "Public API headers" + files: [ + "qtenvironment.h", + "qtprofilesetup.h", + "use_installed_qtprofilesetup.pri", + ] + qbs.install: qbsbuildconfig.installApiHeaders + qbs.installDir: headerInstallPrefix + } + + files: [ + "qtprofilesetup.cpp", + "qtmoduleinfo.cpp", + "qtmoduleinfo.h", + "templates.qrc", + "templates/*" + ] + + Export { + Depends { name: "qbscore" } + } +} diff --git a/src/lib/qtprofilesetup/templates.qrc b/src/lib/qtprofilesetup/templates.qrc new file mode 100644 index 00000000..bcbf2bd6 --- /dev/null +++ b/src/lib/qtprofilesetup/templates.qrc @@ -0,0 +1,16 @@ + + + templates/core.qbs + templates/gui.qbs + templates/phonon.qbs + templates/module.qbs + templates/QtModule.qbs + templates/moc.js + templates/plugin.qbs + templates/qdoc.js + templates/QtPlugin.qbs + templates/dbus.js + templates/dbus.qbs + templates/scxml.qbs + + diff --git a/src/lib/qtprofilesetup/templates/QtModule.qbs b/src/lib/qtprofilesetup/templates/QtModule.qbs new file mode 100644 index 00000000..07618bf2 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/QtModule.qbs @@ -0,0 +1,56 @@ +import qbs 1.0 +import qbs.FileInfo + +Module { + Depends { name: "cpp" } + Depends { name: "Qt.core" } + + property string qtModuleName + property path binPath: Qt.core.binPath + property path incPath: Qt.core.incPath + property path libPath: Qt.core.libPath + property string qtLibInfix: Qt.core.libInfix + property string libNameForLinkerDebug + property string libNameForLinkerRelease + property string libNameForLinker: Qt.core.qtBuildVariant === "debug" + ? libNameForLinkerDebug : libNameForLinkerRelease + property string libFilePathDebug + property string libFilePathRelease + property string libFilePath: Qt.core.qtBuildVariant === "debug" + ? libFilePathDebug : libFilePathRelease + version: Qt.core.version + property bool hasLibrary: true + property bool isStaticLibrary: false + property bool isPlugin: false + + property stringList staticLibsDebug + property stringList staticLibsRelease + property stringList dynamicLibsDebug + property stringList dynamicLibsRelease + property stringList linkerFlagsDebug + property stringList linkerFlagsRelease + property stringList staticLibs: Qt.core.qtBuildVariant === "debug" + ? staticLibsDebug : staticLibsRelease + property stringList dynamicLibs: Qt.core.qtBuildVariant === "debug" + ? dynamicLibsDebug : dynamicLibsRelease + property stringList frameworksDebug + property stringList frameworksRelease + property stringList frameworkPathsDebug + property stringList frameworkPathsRelease + property stringList mFrameworks: Qt.core.qtBuildVariant === "debug" + ? frameworksDebug : frameworksRelease + property stringList mFrameworkPaths: Qt.core.qtBuildVariant === "debug" + ? frameworkPathsDebug: frameworkPathsRelease + cpp.linkerFlags: Qt.core.qtBuildVariant === "debug" + ? linkerFlagsDebug : linkerFlagsRelease + + Properties { + condition: qtModuleName != undefined && hasLibrary + cpp.staticLibraries: (isStaticLibrary ? [libFilePath] : []).concat(staticLibs) + cpp.dynamicLibraries: (!isStaticLibrary && !Qt.core.frameworkBuild + ? [libFilePath] : []).concat(dynamicLibs) + cpp.frameworks: mFrameworks.concat(!isStaticLibrary && Qt.core.frameworkBuild + ? [libNameForLinker] : []) + cpp.frameworkPaths: mFrameworkPaths + } +} diff --git a/src/lib/qtprofilesetup/templates/QtPlugin.qbs b/src/lib/qtprofilesetup/templates/QtPlugin.qbs new file mode 100644 index 00000000..9a916f69 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/QtPlugin.qbs @@ -0,0 +1,32 @@ +import qbs 1.0 +import qbs.FileInfo +import qbs.TextFile + +QtModule { + isPlugin: true + + property string className + + Rule { + condition: isStaticLibrary + multiplex: true + Artifact { + filePath: product.targetName + "_qt_plugin_import_" + + parent.parent.qtModuleName + ".cpp" + fileTags: "cpp" + } + + prepare: { + var cmd = new JavaScriptCommand(); + var pluginName = product.moduleProperty(product.moduleName, "qtModuleName"); + cmd.description = "Creating static import for plugin '" + pluginName + "'."; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + var className = product.moduleProperty(product.moduleName, "className"); + f.writeLine("#include \n\nQ_IMPORT_PLUGIN(" + className + ")"); + f.close(); + }; + return cmd; + } + } +} diff --git a/src/lib/qtprofilesetup/templates/core.qbs b/src/lib/qtprofilesetup/templates/core.qbs new file mode 100644 index 00000000..a5cfb9f0 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/core.qbs @@ -0,0 +1,420 @@ +import qbs 1.0 +import qbs.FileInfo +import qbs.ModUtils +import qbs.TextFile +import qbs.Utilities +import "moc.js" as Moc +import "qdoc.js" as Qdoc + +Module { + id: qtcore + + Depends { name: "cpp" } + + property string libInfix: "" + property stringList config + property stringList qtConfig + property path binPath + property path incPath + property path libPath + property path pluginPath + property path mkspecPath + property string mocName: "moc" + property stringList mocFlags: [] + property string lreleaseName: "lrelease" + property string qdocName: versionMajor >= 5 ? "qdoc" : "qdoc3" + property stringList qdocEnvironment + property path docPath + property stringList helpGeneratorArgs: versionMajor >= 5 ? ["-platform", "minimal"] : [] + property var versionParts: version ? version.split('.').map(function(item) { return parseInt(item, 10); }) : [] + property int versionMajor: versionParts[0] + property int versionMinor: versionParts[1] + property int versionPatch: versionParts[2] + property bool frameworkBuild + property bool staticBuild + property stringList pluginMetaData: [] + + property stringList availableBuildVariants + property string qtBuildVariant: { + if (availableBuildVariants.contains(qbs.buildVariant)) + return qbs.buildVariant; + return availableBuildVariants.length > 0 ? availableBuildVariants[0] : ""; + } + + property stringList staticLibsDebug: @staticLibsDebug@ + property stringList staticLibsRelease: @staticLibsRelease@ + property stringList dynamicLibsDebug: @dynamicLibsDebug@ + property stringList dynamicLibsRelease: @dynamicLibsRelease@ + property stringList staticLibs: qtBuildVariant === "debug" + ? staticLibsDebug : staticLibsRelease + property stringList dynamicLibs: qtBuildVariant === "debug" + ? dynamicLibsDebug : dynamicLibsRelease + property stringList linkerFlagsDebug: @linkerFlagsDebug@ + property stringList linkerFlagsRelease: @linkerFlagsRelease@ + property stringList coreLinkerFlags: qtBuildVariant === "debug" + ? linkerFlagsDebug : linkerFlagsRelease + property stringList frameworksDebug: @frameworksDebug@ + property stringList frameworksRelease: @frameworksRelease@ + property stringList coreFrameworks: qtBuildVariant === "debug" + ? frameworksDebug : frameworksRelease + property stringList frameworkPathsDebug: @frameworkPathsDebug@ + property stringList frameworkPathsRelease: @frameworkPathsRelease@ + property stringList coreFrameworkPaths: qtBuildVariant === "debug" + ? frameworkPathsDebug : frameworkPathsRelease + property string libNameForLinkerDebug: @libNameForLinkerDebug@ + property string libNameForLinkerRelease: @libNameForLinkerRelease@ + property string libNameForLinker: qtBuildVariant === "debug" + ? libNameForLinkerDebug : libNameForLinkerRelease + property string libFilePathDebug: @libFilePathDebug@ + property string libFilePathRelease: @libFilePathRelease@ + property string libFilePath: qtBuildVariant === "debug" + ? libFilePathDebug : libFilePathRelease + + coreLibPaths: @libraryPaths@ + + // These are deliberately not path types + // We don't want to resolve them against the source directory + property string generatedHeadersDir: product.buildDirectory + "/qt.headers" + property string generatedFilesDir: generatedHeadersDir // TODO: Remove in 1.8 + property string qdocOutputDir: product.buildDirectory + "/qdoc_html" + property string qmDir: product.destinationDirectory + property string qmBaseName: product.targetName + property bool lreleaseMultiplexMode: false + + cpp.entryPoint: qbs.targetOS.containsAny(["ios", "tvos"]) + && Utilities.versionCompare(version, "5.6.0") >= 0 + ? "_qt_main_wrapper" + : undefined + cpp.cxxLanguageVersion: Utilities.versionCompare(version, "5.7.0") >= 0 ? "c++11" : original + cpp.defines: { + var defines = @defines@; + // ### QT_NO_DEBUG must be added if the current build variant is derived + // from the build variant "release" + if (!qbs.debugInformation) + defines.push("QT_NO_DEBUG"); + if (qbs.targetOS.containsAny(["ios", "tvos"])) { + defines = defines.concat(["DARWIN_NO_CARBON", "QT_NO_CORESERVICES", "QT_NO_PRINTER", + "QT_NO_PRINTDIALOG"]); + if (Utilities.versionCompare(version, "5.6.0") < 0) + defines.push("main=qtmn"); + } + return defines; + } + cpp.includePaths: { + var paths = @includes@; + paths.push(mkspecPath, generatedHeadersDir); + return paths; + } + cpp.libraryPaths: { + var libPaths = [libPath]; + if (staticBuild && pluginPath) + libPaths.push(pluginPath + "/platforms"); + libPaths = libPaths.concat(coreLibPaths); + return libPaths; + } + cpp.staticLibraries: { + var libs = []; + if (staticBuild) + libs.push(libFilePath); + if (qbs.targetOS.contains('windows') && !product.consoleApplication) { + libs = libs.concat(qtBuildVariant === "debug" + ? @entryPointLibsDebug@ : @entryPointLibsRelease@); + } + libs = libs.concat(staticLibs); + return libs; + } + cpp.dynamicLibraries: { + var libs = []; + if (!staticBuild && !frameworkBuild) + libs.push(libFilePath); + libs = libs.concat(dynamicLibs); + return libs; + } + cpp.linkerFlags: coreLinkerFlags + cpp.frameworkPaths: coreFrameworkPaths.concat(frameworkBuild ? [libPath] : []) + cpp.frameworks: { + var frameworks = coreFrameworks + if (frameworkBuild) + frameworks.push(libNameForLinker); + if (qbs.targetOS.contains('ios') && staticBuild) + frameworks = frameworks.concat(["Foundation", "CoreFoundation"]); + if (frameworks.length === 0) + return undefined; + return frameworks; + } + cpp.rpaths: qbs.targetOS.contains('linux') ? [libPath] : undefined + cpp.positionIndependentCode: versionMajor >= 5 ? true : undefined + cpp.cxxFlags: { + var flags = []; + if (qbs.toolchain.contains('msvc')) { + flags.push('/Zm200'); + if (versionMajor < 5) + flags.push('/Zc:wchar_t-'); + } + + return flags; + } + cpp.cxxStandardLibrary: { + if (qbs.targetOS.contains('darwin') && qbs.toolchain.contains('clang') + && config.contains('c++11')) + return "libc++"; + return original; + } + + additionalProductTypes: ["qm"] + + validate: { + var validator = new ModUtils.PropertyValidator("Qt.core"); + validator.setRequiredProperty("binPath", binPath); + validator.setRequiredProperty("incPath", incPath); + validator.setRequiredProperty("libPath", libPath); + validator.setRequiredProperty("mkspecPath", mkspecPath); + validator.setRequiredProperty("version", version); + validator.setRequiredProperty("config", config); + validator.setRequiredProperty("qtConfig", qtConfig); + validator.setRequiredProperty("versionMajor", versionMajor); + validator.setRequiredProperty("versionMinor", versionMinor); + validator.setRequiredProperty("versionPatch", versionPatch); + + if (!staticBuild) + validator.setRequiredProperty("pluginPath", pluginPath); + + // Allow custom version suffix since some distributions might want to do this, + // but otherwise the version must start with a valid 3-component string + validator.addVersionValidator("version", version, 3, 3, true); + validator.addRangeValidator("versionMajor", versionMajor, 1); + validator.addRangeValidator("versionMinor", versionMinor, 0); + validator.addRangeValidator("versionPatch", versionPatch, 0); + + validator.addCustomValidator("availableBuildVariants", availableBuildVariants, function (v) { + return v.length > 0; + }, "the Qt installation supports no build variants"); + + validator.addCustomValidator("qtBuildVariant", qtBuildVariant, function (variant) { + return availableBuildVariants.contains(variant); + }, "'" + qtBuildVariant + "' is not supported by this Qt installation"); + + validator.addCustomValidator("qtBuildVariant", qtBuildVariant, function (variant) { + return variant === qbs.buildVariant || !qbs.toolchain.contains("msvc"); + }, " is '" + qtBuildVariant + "', but qbs.buildVariant is '" + qbs.buildVariant + + "', which is not allowed when using MSVC"); + + validator.addFileNameValidator("resourceFileBaseName", resourceFileBaseName); + + validator.validate(); + } + + setupRunEnvironment: { + var env; + if (qbs.targetOS.contains('windows')) { + env = new ModUtils.EnvironmentVariable("PATH", qbs.pathListSeparator, true); + env.append(binPath); + env.set(); + } else if (qbs.targetOS.contains("darwin")) { + env = new ModUtils.EnvironmentVariable("DYLD_FRAMEWORK_PATH", qbs.pathListSeparator); + env.append(libPath); + env.set(); + + env = new ModUtils.EnvironmentVariable("DYLD_LIBRARY_PATH", qbs.pathListSeparator); + env.append(libPath); + env.set(); + } + } + + FileTagger { + patterns: ["*.qrc"] + fileTags: ["qrc"] + } + + FileTagger { + patterns: ["*.ts"] + fileTags: ["ts"] + } + + FileTagger { + patterns: ["*.qdoc"] + fileTags: ["qdoc"] + } + + FileTagger { + patterns: ["*.qdocconf"] + fileTags: ["qdocconf"] + } + + FileTagger { + patterns: ["*.qhp"] + fileTags: ["qhp"] + } + + Rule { + name: "QtCoreMocRule" + inputs: ["objcpp", "cpp", "hpp"] + auxiliaryInputs: ["qt_plugin_metadata"] + excludedAuxiliaryInputs: ["unmocable"] + outputFileTags: ["hpp", "cpp", "unmocable"] + outputArtifacts: { + var mocinfo = QtMocScanner.apply(input); + if (!mocinfo.hasQObjectMacro) + return []; + var artifact = { fileTags: ["unmocable"] }; + if (input.fileTags.contains("hpp")) { + artifact.filePath = ModUtils.moduleProperty(product, "generatedHeadersDir") + + "/moc_" + input.completeBaseName + ".cpp"; + } else { + artifact.filePath = ModUtils.moduleProperty(product, "generatedHeadersDir") + + '/' + input.completeBaseName + ".moc"; + } + artifact.fileTags.push(mocinfo.mustCompile ? "cpp" : "hpp"); + if (mocinfo.hasPluginMetaDataMacro) + artifact.explicitlyDependsOn = ["qt_plugin_metadata"]; + return [artifact]; + } + prepare: { + var cmd = new Command(Moc.fullPath(product), + Moc.args(product, input, output.filePath)); + cmd.description = 'moc ' + input.fileName; + cmd.highlight = 'codegen'; + return cmd; + } + } + + property path resourceSourceBase + property string resourcePrefix: "/" + property string resourceFileBaseName: product.targetName + Rule { + multiplex: true + inputs: ["qt.core.resource_data"] + Artifact { + filePath: product.moduleProperty("Qt.core", "resourceFileBaseName") + ".qrc" + fileTags: ["qrc"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating " + output.fileName; + cmd.sourceCode = function() { + var qrcFile = new TextFile(output.filePath, TextFile.WriteOnly); + try { + qrcFile.writeLine(''); + qrcFile.writeLine(''); + + var inputsByPrefix = {} + for (var i = 0; i < inputs["qt.core.resource_data"].length; ++i) { + var inp = inputs["qt.core.resource_data"][i]; + var prefix = inp.moduleProperty("Qt.core", "resourcePrefix"); + var inputsList = inputsByPrefix[prefix] || []; + inputsList.push(inp); + inputsByPrefix[prefix] = inputsList; + } + + for (var prefix in inputsByPrefix) { + qrcFile.writeLine(''); + for (var i = 0; i < inputsByPrefix[prefix].length; ++i) { + var inp = inputsByPrefix[prefix][i]; + var fullResPath = inp.filePath; + var baseDir = inp.moduleProperty("Qt.core", "resourceSourceBase"); + var resAlias = baseDir + ? FileInfo.relativePath(baseDir, fullResPath) : inp.fileName; + qrcFile.writeLine('' + + fullResPath + ''); + } + qrcFile.writeLine(''); + } + + qrcFile.writeLine(''); + } finally { + qrcFile.close(); + } + }; + return [cmd]; + } + } + + Rule { + inputs: ["qrc"] + + Artifact { + filePath: "qrc_" + input.completeBaseName + ".cpp" + fileTags: ["cpp"] + } + prepare: { + var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/rcc', + [input.filePath, '-name', + FileInfo.completeBaseName(input.filePath), + '-o', output.filePath]); + cmd.description = 'rcc ' + input.fileName; + cmd.highlight = 'codegen'; + return cmd; + } + } + + Rule { + inputs: ["ts"] + multiplex: lreleaseMultiplexMode + + Artifact { + filePath: FileInfo.joinPaths(ModUtils.moduleProperty(product, "qmDir"), + (ModUtils.moduleProperty(product, "lreleaseMultiplexMode") + ? ModUtils.moduleProperty(product, "qmBaseName") + : input.baseName) + ".qm") + fileTags: ["qm"] + } + + prepare: { + var inputFilePaths; + if (ModUtils.moduleProperty(product, "lreleaseMultiplexMode")) + inputFilePaths = inputs["ts"].map(function(artifact) { return artifact.filePath; }); + else + inputFilePaths = [input.filePath]; + var args = ['-silent', '-qm', output.filePath].concat(inputFilePaths); + var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/' + + ModUtils.moduleProperty(product, "lreleaseName"), args); + cmd.description = 'Creating ' + output.fileName; + cmd.highlight = 'filegen'; + return cmd; + } + } + + Rule { + inputs: "qdocconf-main" + explicitlyDependsOn: ["qdoc", "qdocconf"] + + outputFileTags: ModUtils.allFileTags(Qdoc.qdocFileTaggers()) + outputArtifacts: Qdoc.outputArtifacts(product, input) + + prepare: { + var outputDir = ModUtils.moduleProperty(product, "qdocOutputDir"); + var args = Qdoc.qdocArgs(product, input, outputDir); + var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/' + + ModUtils.moduleProperty(product, "qdocName"), args); + cmd.description = 'qdoc ' + input.fileName; + cmd.highlight = 'filegen'; + cmd.environment = ModUtils.moduleProperty(product, "qdocEnvironment"); + cmd.environment.push("OUTDIR=" + outputDir); // Qt 4 replacement for -outputdir + return cmd; + } + } + + Rule { + inputs: "qhp" + + Artifact { + filePath: input.completeBaseName + ".qch" + fileTags: ["qch"] + } + + prepare: { + var args = [input.filePath]; + args = args.concat(ModUtils.moduleProperty(product, "helpGeneratorArgs")); + args.push("-o"); + args.push(output.filePath); + var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + "/qhelpgenerator", + args); + cmd.description = 'qhelpgenerator ' + input.fileName; + cmd.highlight = 'filegen'; + cmd.stdoutFilterFunction = function(output) { + return ""; + }; + return cmd; + } + } +} diff --git a/src/lib/qtprofilesetup/templates/dbus.js b/src/lib/qtprofilesetup/templates/dbus.js new file mode 100644 index 00000000..36978282 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/dbus.js @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = loadExtension("qbs.FileInfo"); + +function outputFileName(input, suffix) +{ + var parts = input.fileName.split('.').filter(function(s) { return s.length > 0; }); + if (parts.length === 0) + throw "Cannot run qdbusxml2cpp on '" + input.filePath + "': Unsuitable file name."; + var outputBaseName = parts.length === 1 ? parts[0] : parts[parts.length - 2]; + return outputBaseName.toLowerCase() + suffix; +} + +function createCommands(product, input, outputs, option) +{ + var exe = ModUtils.moduleProperty(product, "binPath") + '/' + + ModUtils.moduleProperty(product, "xml2cppName"); + var hppOutput = outputs["hpp"][0]; + var hppArgs = ModUtils.moduleProperty(product, "xml2CppHeaderFlags"); + hppArgs.push(option, hppOutput.fileName + ':', input.filePath); // Can't use filePath on Windows + var hppCmd = new Command(exe, hppArgs) + hppCmd.description = "qdbusxml2cpp " + input.fileName + " -> " + hppOutput.fileName; + hppCmd.highlight = "codegen"; + hppCmd.workingDirectory = FileInfo.path(hppOutput.filePath); + var cppOutput = outputs["cpp"][0]; + var cppArgs = ModUtils.moduleProperty(product, "xml2CppSourceFlags"); + cppArgs.push("-i", hppOutput.filePath, option, ':' + cppOutput.fileName, input.filePath); + var cppCmd = new Command(exe, cppArgs) + cppCmd.description = "qdbusxml2cpp " + input.fileName + " -> " + cppOutput.fileName; + cppCmd.highlight = "codegen"; + cppCmd.workingDirectory = FileInfo.path(cppOutput.filePath); + return [hppCmd, cppCmd]; +} diff --git a/src/lib/qtprofilesetup/templates/dbus.qbs b/src/lib/qtprofilesetup/templates/dbus.qbs new file mode 100644 index 00000000..3ab85966 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/dbus.qbs @@ -0,0 +1,71 @@ +import qbs 1.0 +import qbs.FileInfo +import qbs.ModUtils +import "../QtModule.qbs" as QtModule +import "dbus.js" as DBus + +QtModule { + qtModuleName: "DBus" + + property string xml2cppName: "qdbusxml2cpp" + property stringList xml2CppHeaderFlags: [] + property stringList xml2CppSourceFlags: [] + + Rule { + inputs: ["qt.dbus.adaptor"] + + Artifact { + filePath: FileInfo.joinPaths(product.moduleProperty("Qt.core", "generatedHeadersDir"), + DBus.outputFileName(input, "_adaptor.h")) + fileTags: ["hpp"] + } + Artifact { + filePath: DBus.outputFileName(input, "_adaptor.cpp") + fileTags: ["cpp"] + } + + prepare: { + return DBus.createCommands(product, input, outputs, "-a"); + } + } + + Rule { + inputs: ["qt.dbus.interface"] + + Artifact { + filePath: FileInfo.joinPaths(product.moduleProperty("Qt.core", "generatedHeadersDir"), + DBus.outputFileName(input, "_interface.h")) + fileTags: ["hpp"] + } + Artifact { + filePath: DBus.outputFileName(input, "_interface.cpp") + fileTags: ["cpp"] + } + + prepare: { + return DBus.createCommands(product, input, outputs, "-p"); + } + } + + staticLibsDebug: @staticLibsDebug@ + staticLibsRelease: @staticLibsRelease@ + dynamicLibsDebug: @dynamicLibsDebug@ + dynamicLibsRelease: @dynamicLibsRelease@ + linkerFlagsDebug: @linkerFlagsDebug@ + linkerFlagsRelease: @linkerFlagsRelease@ + frameworksDebug: @frameworksDebug@ + frameworksRelease: @frameworksRelease@ + frameworkPathsDebug: @frameworkPathsDebug@ + frameworkPathsRelease: @frameworkPathsRelease@ + libNameForLinkerDebug: @libNameForLinkerDebug@ + libNameForLinkerRelease: @libNameForLinkerRelease@ + libFilePathDebug: @libFilePathDebug@ + libFilePathRelease: @libFilePathRelease@ + + cpp.defines: @defines@ + cpp.includePaths: @includes@ + cpp.libraryPaths: @libraryPaths@ + + @special_properties@ +} + diff --git a/src/lib/qtprofilesetup/templates/gui.qbs b/src/lib/qtprofilesetup/templates/gui.qbs new file mode 100644 index 00000000..48d839af --- /dev/null +++ b/src/lib/qtprofilesetup/templates/gui.qbs @@ -0,0 +1,63 @@ +import qbs 1.0 +import qbs.FileInfo +import qbs.ModUtils +import '../QtModule.qbs' as QtModule + +QtModule { + qtModuleName: "Gui" + + property string uicName: "uic" + + FileTagger { + patterns: ["*.ui"] + fileTags: ["ui"] + } + + Rule { + inputs: ["ui"] + + Artifact { + filePath: FileInfo.joinPaths(product.moduleProperty("Qt.core", "generatedHeadersDir"), + 'ui_' + input.completeBaseName + '.h') + fileTags: ["hpp"] + } + + prepare: { + var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/' + + ModUtils.moduleProperty(product, "uicName"), + [input.filePath, '-o', output.filePath]) + cmd.description = 'uic ' + input.fileName; + cmd.highlight = 'codegen'; + return cmd; + } + } + + property string defaultQpaPlugin: @defaultQpaPlugin@ + staticLibsDebug: @staticLibsDebug@ + staticLibsRelease: @staticLibsRelease@ + dynamicLibsDebug: @dynamicLibsDebug@ + dynamicLibsRelease: @dynamicLibsRelease@ + linkerFlagsDebug: @linkerFlagsDebug@ + linkerFlagsRelease: @linkerFlagsRelease@ + frameworksDebug: @frameworksDebug@ + frameworksRelease: @frameworksRelease@ + frameworkPathsDebug: @frameworkPathsDebug@ + frameworkPathsRelease: @frameworkPathsRelease@ + libNameForLinkerDebug: @libNameForLinkerDebug@ + libNameForLinkerRelease: @libNameForLinkerRelease@ + libFilePathDebug: @libFilePathDebug@ + libFilePathRelease: @libFilePathRelease@ + + cpp.defines: @defines@ + cpp.includePaths: @includes@ + cpp.libraryPaths: @libraryPaths@ + + Properties { + condition: Qt.core.staticBuild && qbs.targetOS.contains("ios") + cpp.frameworks: base.concat(["UIKit", "QuartzCore", "CoreText", "CoreGraphics", + "Foundation", "CoreFoundation"]) + } + cpp.frameworks: base + @special_properties@ +} + diff --git a/src/lib/qtprofilesetup/templates/moc.js b/src/lib/qtprofilesetup/templates/moc.js new file mode 100644 index 00000000..bac9bc53 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/moc.js @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var ModUtils = loadExtension("qbs.ModUtils"); + +function args(product, input, outputFileName) +{ + var defines = product.moduleProperty("cpp", "compilerDefines"); + defines = defines.uniqueConcat(product.moduleProperty("cpp", "platformDefines")); + defines = defines.uniqueConcat( + ModUtils.modulePropertiesFromArtifacts(product, [input], 'cpp', 'defines')); + var includePaths = ModUtils.modulePropertiesFromArtifacts(product, [input], 'cpp', 'includePaths'); + includePaths = includePaths.uniqueConcat(ModUtils.modulePropertiesFromArtifacts( + product, [input], 'cpp', 'systemIncludePaths')); + var useCompilerPaths = product.moduleProperty("Qt.core", "versionMajor") >= 5; + if (useCompilerPaths) { + includePaths = includePaths.uniqueConcat(ModUtils.modulePropertiesFromArtifacts( + product, [input], 'cpp', 'compilerIncludePaths')); + } + var frameworkPaths = product.moduleProperty("cpp", "frameworkPaths"); + frameworkPaths = frameworkPaths.uniqueConcat( + product.moduleProperty("cpp", "systemFrameworkPaths")); + if (useCompilerPaths) { + frameworkPaths = frameworkPaths.uniqueConcat( + product.moduleProperty("cpp", "compilerFrameworkPaths")); + } + var pluginMetaData = product.moduleProperty("Qt.core", "pluginMetaData"); + var args = []; + args = args.concat( + defines.map(function(item) { return '-D' + item; }), + includePaths.map(function(item) { return '-I' + item; }), + frameworkPaths.map(function(item) { return '-F' + item; }), + pluginMetaData.map(function(item) { return '-M' + item; }), + ModUtils.moduleProperty(product, "mocFlags"), + '-o', outputFileName, + input.filePath); + return args; +} + +function fullPath(product) +{ + return ModUtils.moduleProperty(product, "binPath") + '/' + ModUtils.moduleProperty(product, "mocName"); +} diff --git a/src/lib/qtprofilesetup/templates/module.qbs b/src/lib/qtprofilesetup/templates/module.qbs new file mode 100644 index 00000000..4d5b8021 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/module.qbs @@ -0,0 +1,27 @@ +import qbs 1.0 +import '../QtModule.qbs' as QtModule + +QtModule { + qtModuleName: @name@ + Depends { name: "Qt"; submodules: @dependencies@} + + hasLibrary: @has_library@ + staticLibsDebug: @staticLibsDebug@ + staticLibsRelease: @staticLibsRelease@ + dynamicLibsDebug: @dynamicLibsDebug@ + dynamicLibsRelease: @dynamicLibsRelease@ + linkerFlagsDebug: @linkerFlagsDebug@ + linkerFlagsRelease: @linkerFlagsRelease@ + frameworksDebug: @frameworksDebug@ + frameworksRelease: @frameworksRelease@ + frameworkPathsDebug: @frameworkPathsDebug@ + frameworkPathsRelease: @frameworkPathsRelease@ + libNameForLinkerDebug: @libNameForLinkerDebug@ + libNameForLinkerRelease: @libNameForLinkerRelease@ + libFilePathDebug: @libFilePathDebug@ + libFilePathRelease: @libFilePathRelease@ + cpp.defines: @defines@ + cpp.includePaths: @includes@ + cpp.libraryPaths: @libraryPaths@ + @special_properties@ +} diff --git a/src/lib/qtprofilesetup/templates/phonon.qbs b/src/lib/qtprofilesetup/templates/phonon.qbs new file mode 100644 index 00000000..ea843478 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/phonon.qbs @@ -0,0 +1,9 @@ +import qbs 1.0 +import '../QtModule.qbs' as QtModule + +QtModule { + qtModuleName: "phonon" + Depends { name: "Qt"; submodules: ['core'] } + cpp.defines: @defines@ + cpp.includePaths: @includes@ +} diff --git a/src/lib/qtprofilesetup/templates/plugin.qbs b/src/lib/qtprofilesetup/templates/plugin.qbs new file mode 100644 index 00000000..3a07a363 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/plugin.qbs @@ -0,0 +1,25 @@ +import qbs 1.0 +import '../QtPlugin.qbs' as QtPlugin + +QtPlugin { + qtModuleName: @name@ + Depends { name: "Qt"; submodules: @dependencies@} + + className: @className@ + staticLibsDebug: @staticLibsDebug@ + staticLibsRelease: @staticLibsRelease@ + dynamicLibsDebug: @dynamicLibsDebug@ + dynamicLibsRelease: @dynamicLibsRelease@ + linkerFlagsDebug: @linkerFlagsDebug@ + linkerFlagsRelease: @linkerFlagsRelease@ + frameworksDebug: @frameworksDebug@ + frameworksRelease: @frameworksRelease@ + frameworkPathsDebug: @frameworkPathsDebug@ + frameworkPathsRelease: @frameworkPathsRelease@ + libNameForLinkerDebug: @libNameForLinkerDebug@ + libNameForLinkerRelease: @libNameForLinkerRelease@ + libFilePathDebug: @libFilePathDebug@ + libFilePathRelease: @libFilePathRelease@ + cpp.libraryPaths: @libraryPaths@ + @special_properties@ +} diff --git a/src/lib/qtprofilesetup/templates/qdoc.js b/src/lib/qtprofilesetup/templates/qdoc.js new file mode 100644 index 00000000..86840346 --- /dev/null +++ b/src/lib/qtprofilesetup/templates/qdoc.js @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/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 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var FileInfo = loadExtension("qbs.FileInfo"); +var ModUtils = loadExtension("qbs.ModUtils"); + +function qdocArgs(product, input, outputDir) { + var args = [input.filePath]; + var qtVersion = ModUtils.moduleProperty(product, "versionMajor"); + if (qtVersion >= 5) { + args.push("-outputdir"); + args.push(outputDir); + } + + return args; +} + +var _qdocDefaultFileTag = "qdoc-output"; +function qdocFileTaggers() { + var t = _qdocDefaultFileTag; + return { + ".qhp": [t, "qhp"], + ".qhp.sha1": [t, "qhp-sha1"], + ".css": [t, "qdoc-css"], + ".html": [t, "qdoc-html"], + ".index": [t, "qdoc-index"], + ".png": [t, "qdoc-png"] + }; +} + +function outputArtifacts(product, input) { + var tracker = new ModUtils.BlackboxOutputArtifactTracker(); + tracker.hostOS = product.moduleProperty("qbs", "hostOS"); + tracker.shellPath = product.moduleProperty("qbs", "shellPath"); + tracker.defaultFileTags = [_qdocDefaultFileTag]; + tracker.fileTaggers = qdocFileTaggers(); + tracker.command = FileInfo.joinPaths(ModUtils.moduleProperty(product, "binPath"), + ModUtils.moduleProperty(product, "qdocName")); + tracker.commandArgsFunction = function (outputDirectory) { + return qdocArgs(product, input, outputDirectory); + }; + tracker.commandEnvironmentFunction = function (outputDirectory) { + var env = {}; + var qdocEnv = ModUtils.moduleProperty(product, "qdocEnvironment"); + for (var j = 0; j < qdocEnv.length; ++j) { + var e = qdocEnv[j]; + var idx = e.indexOf("="); + var name = e.slice(0, idx); + var value = e.slice(idx + 1, e.length); + env[name] = value; + } + env["OUTDIR"] = outputDirectory; // Qt 4 replacement for -outputdir + return env; + }; + return tracker.artifacts(ModUtils.moduleProperty(product, "qdocOutputDir")); +} diff --git a/src/lib/qtprofilesetup/templates/scxml.qbs b/src/lib/qtprofilesetup/templates/scxml.qbs new file mode 100644 index 00000000..206d914a --- /dev/null +++ b/src/lib/qtprofilesetup/templates/scxml.qbs @@ -0,0 +1,68 @@ +import qbs 1.0 +import qbs.FileInfo +import "../QtModule.qbs" as QtModule + +QtModule { + qtModuleName: "Scxml" + + property string qscxmlcName: "qscxmlc" + property string className + property string namespace + + Rule { + inputs: ["qt.scxml.compilable"] + + Artifact { + filePath: FileInfo.joinPaths(product.moduleProperty("Qt.core", "generatedHeadersDir"), + input.baseName + ".h") + fileTags: ["hpp", "unmocable"] + } + Artifact { + filePath: input.baseName + ".cpp" + fileTags: ["cpp"] + } + + prepare: { + var compilerName = product.moduleProperty("Qt.scxml", "qscxmlcName"); + var compilerPath = FileInfo.joinPaths(input.moduleProperty("Qt.core", "binPath"), + compilerName); + var args = ["--header", outputs["hpp"][0].filePath, + "--impl", outputs["cpp"][0].filePath]; + var cxx98 = input.moduleProperty("cpp", "cxxLanguageVersion") === "c++98"; + if (cxx98) + args.push("-no-c++11"); + var className = input.moduleProperty("Qt.scxml", "className"); + if (className) + args.push("--classname", className); + var namespace = input.moduleProperty("Qt.scxml", "namespace"); + if (namespace) + args.push("--namespace", namespace); + args.push(input.filePath); + var cmd = new Command(compilerPath, args); + cmd.description = "compiling " + input.fileName; + cmd.highlight = "codegen"; + return [cmd]; + } + } + + staticLibsDebug: @staticLibsDebug@ + staticLibsRelease: @staticLibsRelease@ + dynamicLibsDebug: @dynamicLibsDebug@ + dynamicLibsRelease: @dynamicLibsRelease@ + linkerFlagsDebug: @linkerFlagsDebug@ + linkerFlagsRelease: @linkerFlagsRelease@ + frameworksDebug: @frameworksDebug@ + frameworksRelease: @frameworksRelease@ + frameworkPathsDebug: @frameworkPathsDebug@ + frameworkPathsRelease: @frameworkPathsRelease@ + libNameForLinkerDebug: @libNameForLinkerDebug@ + libNameForLinkerRelease: @libNameForLinkerRelease@ + libFilePathDebug: @libFilePathDebug@ + libFilePathRelease: @libFilePathRelease@ + + cpp.defines: @defines@ + cpp.includePaths: @includes@ + cpp.libraryPaths: @libraryPaths@ + + @special_properties@ +} diff --git a/src/lib/qtprofilesetup/use_installed_qtprofilesetup.pri b/src/lib/qtprofilesetup/use_installed_qtprofilesetup.pri new file mode 100644 index 00000000..c3fa4a83 --- /dev/null +++ b/src/lib/qtprofilesetup/use_installed_qtprofilesetup.pri @@ -0,0 +1,20 @@ +include(use_installed_corelib.pri) + +LIBNAME=qbsqtprofilesetup + +unix:LIBS += -l$${LIBNAME} + +win32 { + CONFIG(debug, debug|release) { + QBSQTPROFILELIB = $${LIBNAME}d$${QBSCORELIBSUFFIX} + } + CONFIG(release, debug|release) { + QBSQTPROFILELIB = $${LIBNAME}$${QBSCORELIBSUFFIX} + } + msvc { + QBSQTPROFILELIB = $${QBSQTPROFILELIB}.lib + } else { + QBSQTPROFILELIB = lib$${QBSQTPROFILELIB} + } + LIBS += $${QBSQTPROFILELIB} +} diff --git a/src/lib/qtprofilesetup/use_qtprofilesetup.pri b/src/lib/qtprofilesetup/use_qtprofilesetup.pri new file mode 100644 index 00000000..0bf44e09 --- /dev/null +++ b/src/lib/qtprofilesetup/use_qtprofilesetup.pri @@ -0,0 +1,48 @@ +include(../../../qbs_version.pri) +include(../../library_dirname.pri) + +isEmpty(QBSLIBDIR) { + QBSLIBDIR = $${OUT_PWD}/../../../$${QBS_LIBRARY_DIRNAME} +} + +LIBNAME=qbsqtprofilesetup + +unix { + LIBS += -L$${QBSLIBDIR} -l$${LIBNAME} +} + +!qbs_disable_rpath { + linux-*:QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,\$\$ORIGIN/../$${QBS_LIBRARY_DIRNAME}\' + macx:QMAKE_LFLAGS += -Wl,-rpath,@loader_path/../$${QBS_LIBRARY_DIRNAME} +} + +!CONFIG(static, static|shared) { + QBSQTPROFILELIBSUFFIX = $${QBS_VERSION_MAJ} +} + +win32 { + CONFIG(debug, debug|release) { + QBSQTPROFILELIB = $${LIBNAME}d$${QBSQTPROFILELIBSUFFIX} + } + CONFIG(release, debug|release) { + QBSQTPROFILELIB = $${LIBNAME}$${QBSQTPROFILELIBSUFFIX} + } + msvc { + LIBS += /LIBPATH:$$QBSLIBDIR + QBSQTPROFILELIB = $${QBSQTPROFILELIB}.lib + LIBS += Shell32.lib + } else { + LIBS += -L$${QBSLIBDIR} + QBSQTPROFILELIB = lib$${QBSQTPROFILELIB} + } + LIBS += $${QBSQTPROFILELIB} +} + +INCLUDEPATH += \ + $$PWD + +CONFIG += depend_includepath + +CONFIG(static, static|shared) { + DEFINES += QBS_STATIC_LIB +} diff --git a/src/libexec/libexec.pri b/src/libexec/libexec.pri new file mode 100644 index 00000000..5eeb7b9d --- /dev/null +++ b/src/libexec/libexec.pri @@ -0,0 +1,8 @@ +include(../install_prefix.pri) + +!isEmpty(QBS_LIBEXEC_DESTDIR):DESTDIR=$${QBS_LIBEXEC_DESTDIR} +else:DESTDIR = ../../../libexec/qbs + +!isEmpty(QBS_LIBEXEC_INSTALL_DIR):target.path = $${QBS_LIBEXEC_INSTALL_DIR} +else:target.path = $${QBS_INSTALL_PREFIX}/libexec/qbs +INSTALLS += target diff --git a/src/libexec/libexec.pro b/src/libexec/libexec.pro new file mode 100644 index 00000000..96710850 --- /dev/null +++ b/src/libexec/libexec.pro @@ -0,0 +1 @@ +TEMPLATE = subdirs diff --git a/src/libexec/libexec.qbs b/src/libexec/libexec.qbs new file mode 100644 index 00000000..489864a2 --- /dev/null +++ b/src/libexec/libexec.qbs @@ -0,0 +1,6 @@ +import qbs + +Project { + references: [ + ] +} diff --git a/src/library_dirname.pri b/src/library_dirname.pri new file mode 100644 index 00000000..5d0712d7 --- /dev/null +++ b/src/library_dirname.pri @@ -0,0 +1 @@ +isEmpty(QBS_LIBRARY_DIRNAME):QBS_LIBRARY_DIRNAME = lib diff --git a/src/plugins/plugins.pri b/src/plugins/plugins.pri new file mode 100644 index 00000000..ca07aa08 --- /dev/null +++ b/src/plugins/plugins.pri @@ -0,0 +1,22 @@ +include(../library_dirname.pri) +include(../install_prefix.pri) + +!isEmpty(QBS_PLUGINS_BUILD_DIR) { + destdirPrefix = $${QBS_PLUGINS_BUILD_DIR} +} else { + destdirPrefix = $$shadowed($$PWD)/../../$${QBS_LIBRARY_DIRNAME} +} +DESTDIR = $${destdirPrefix}/qbs/plugins +TEMPLATE = lib + +CONFIG += depend_includepath +CONFIG += shared +CONFIG += c++11 +unix: CONFIG += plugin + +!isEmpty(QBS_PLUGINS_INSTALL_DIR): \ + installPrefix = $${QBS_PLUGINS_INSTALL_DIR} +else: \ + installPrefix = $${QBS_INSTALL_PREFIX}/$${QBS_LIBRARY_DIRNAME} +target.path = $${installPrefix}/qbs/plugins +INSTALLS += target diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro new file mode 100644 index 00000000..d3823ae3 --- /dev/null +++ b/src/plugins/plugins.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = scanner diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs new file mode 100644 index 00000000..eb2fb361 --- /dev/null +++ b/src/plugins/plugins.qbs @@ -0,0 +1,9 @@ +import qbs + +Project { + name: "qbs plugins" + references: [ + "scanner/cpp/cpp.qbs", + "scanner/qt/qt.qbs" + ] +} diff --git a/src/plugins/scanner/cpp/CPlusPlusForwardDeclarations.h b/src/plugins/scanner/cpp/CPlusPlusForwardDeclarations.h new file mode 100644 index 00000000..d0d7f663 --- /dev/null +++ b/src/plugins/scanner/cpp/CPlusPlusForwardDeclarations.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CPLUSPLUS_CPLUSPLUSFORWARDDECLARATIONS_H +#define CPLUSPLUS_CPLUSPLUSFORWARDDECLARATIONS_H + +#include +#include + +#ifndef CPLUSPLUS_WITHOUT_QT +# include + +//# if defined(CPLUSPLUS_BUILD_LIB) +//# define CPLUSPLUS_EXPORT Q_DECL_EXPORT +//# elif defined(CPLUSPLUS_BUILD_STATIC_LIB) +//# define CPLUSPLUS_EXPORT +//# else +//# define CPLUSPLUS_EXPORT Q_DECL_IMPORT +//# endif +//#else +# define CPLUSPLUS_EXPORT +#endif + +namespace CPlusPlus { + +class TranslationUnit; +class Control; +class MemoryPool; +class DiagnosticClient; + +class Identifier; +class Literal; +class StringLiteral; +class NumericLiteral; + +class SymbolTable; + +// names +class NameVisitor; +class Name; +class Identifier; +class TemplateNameId; +class DestructorNameId; +class OperatorNameId; +class ConversionNameId; +class QualifiedNameId; +class SelectorNameId; + +// types +class TypeMatcher; +class FullySpecifiedType; +class TypeVisitor; +class Type; +class UndefinedType; +class VoidType; +class IntegerType; +class FloatType; +class PointerToMemberType; +class PointerType; +class ReferenceType; +class ArrayType; +class NamedType; + +// symbols +class SymbolVisitor; +class Symbol; +class Scope; +class UsingNamespaceDirective; +class UsingDeclaration; +class Declaration; +class Argument; +class TypenameArgument; +class Function; +class Namespace; +class NamespaceAlias; +class Template; +class BaseClass; +class Block; +class Class; +class Enum; +class ForwardClassDeclaration; + +class Token; + +// Objective-C symbols +class ObjCBaseClass; +class ObjCBaseProtocol; +class ObjCClass; +class ObjCForwardClassDeclaration; +class ObjCProtocol; +class ObjCForwardProtocolDeclaration; +class ObjCMethod; +class ObjCPropertyDeclaration; + +} // end of namespace CPlusPlus + +#endif // CPLUSPLUS_CPLUSPLUSFORWARDDECLARATIONS_H diff --git a/src/plugins/scanner/cpp/Lexer.cpp b/src/plugins/scanner/cpp/Lexer.cpp new file mode 100644 index 00000000..4e06f93b --- /dev/null +++ b/src/plugins/scanner/cpp/Lexer.cpp @@ -0,0 +1,671 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "Lexer.h" +#include + +namespace CPlusPlus { + +Lexer::Lexer(const char *firstChar, const char *lastChar) + : _state(State_Default), + _flags(0), + _currentLine(1) +{ + setSource(firstChar, lastChar); +} + +Lexer::~Lexer() +{ } + +void Lexer::setSource(const char *firstChar, const char *lastChar) +{ + _firstChar = firstChar; + _lastChar = lastChar; + _currentChar = _firstChar - 1; + _tokenStart = _currentChar; + _yychar = '\n'; +} + +void Lexer::setStartWithNewline(bool enabled) +{ + if (enabled) + _yychar = '\n'; + else + _yychar = ' '; +} + +int Lexer::state() const +{ return _state; } + +void Lexer::setState(int state) +{ _state = state; } + +bool Lexer::qtMocRunEnabled() const +{ return f._qtMocRunEnabled; } + +void Lexer::setQtMocRunEnabled(bool onoff) +{ f._qtMocRunEnabled = onoff; } + +bool Lexer::cxx0xEnabled() const +{ return f._cxx0xEnabled; } + +void Lexer::setCxxOxEnabled(bool onoff) +{ f._cxx0xEnabled = onoff; } + +bool Lexer::objCEnabled() const +{ return f._objCEnabled; } + +void Lexer::setObjCEnabled(bool onoff) +{ f._objCEnabled = onoff; } + +bool Lexer::isIncremental() const +{ return f._isIncremental; } + +void Lexer::setIncremental(bool isIncremental) +{ f._isIncremental = isIncremental; } + +bool Lexer::scanCommentTokens() const +{ return f._scanCommentTokens; } + +void Lexer::setScanCommentTokens(bool onoff) +{ f._scanCommentTokens = onoff; } + +void Lexer::setScanAngleStringLiteralTokens(bool onoff) +{ f._scanAngleStringLiteralTokens = onoff; } + +void Lexer::pushLineStartOffset() +{ + ++_currentLine; +} + +unsigned Lexer::tokenOffset() const +{ return _tokenStart - _firstChar; } + +unsigned Lexer::tokenLength() const +{ return _currentChar - _tokenStart; } + +const char *Lexer::tokenBegin() const +{ return _tokenStart; } + +const char *Lexer::tokenEnd() const +{ return _currentChar; } + +unsigned Lexer::currentLine() const +{ return _currentLine; } + +void Lexer::scan(Token *tok) +{ + tok->reset(); + scan_helper(tok); + tok->f.length = _currentChar - _tokenStart; +} + +void Lexer::scan_helper(Token *tok) +{ + _Lagain: + while (_yychar && std::isspace(_yychar)) { + if (_yychar == '\n') { + tok->f.joined = false; + tok->f.newline = true; + } else { + tok->f.whitespace = true; + } + yyinp(); + } + + tok->lineno = _currentLine; + _tokenStart = _currentChar; + tok->offset = _currentChar - _firstChar; + + if (_state == State_MultiLineComment || _state == State_MultiLineDoxyComment) { + const int originalState = _state; + + if (! _yychar) { + tok->f.kind = T_EOF_SYMBOL; + return; + } + + while (_yychar) { + if (_yychar != '*') + yyinp(); + else { + yyinp(); + if (_yychar == '/') { + yyinp(); + _state = State_Default; + break; + } + } + } + + if (! f._scanCommentTokens) + goto _Lagain; + + else if (originalState == State_MultiLineComment) + tok->f.kind = T_COMMENT; + else + tok->f.kind = T_DOXY_COMMENT; + return; // done + } + + if (! _yychar) { + tok->f.kind = T_EOF_SYMBOL; + return; + } + + unsigned char ch = _yychar; + yyinp(); + + switch (ch) { + case '\\': + while (_yychar != '\n' && std::isspace(_yychar)) + yyinp(); + // ### assert(! _yychar || _yychar == '\n'); + if (_yychar == '\n') { + tok->f.joined = true; + tok->f.newline = false; + yyinp(); + } + goto _Lagain; + + case '"': case '\'': { + const char quote = ch; + + tok->f.kind = quote == '"' + ? T_STRING_LITERAL + : T_CHAR_LITERAL; + + while (_yychar && _yychar != quote) { + if (_yychar == '\n') + break; + else if (_yychar != '\\') + yyinp(); + else { + yyinp(); // skip `\\' + + if (_yychar) + yyinp(); + } + } + // assert(_yychar == quote); + + if (_yychar == quote) + yyinp(); + } break; + + case '{': + tok->f.kind = T_LBRACE; + break; + + case '}': + tok->f.kind = T_RBRACE; + break; + + case '[': + tok->f.kind = T_LBRACKET; + break; + + case ']': + tok->f.kind = T_RBRACKET; + break; + + case '#': + if (_yychar == '#') { + tok->f.kind = T_POUND_POUND; + yyinp(); + } else { + tok->f.kind = T_POUND; + } + break; + + case '(': + tok->f.kind = T_LPAREN; + break; + + case ')': + tok->f.kind = T_RPAREN; + break; + + case ';': + tok->f.kind = T_SEMICOLON; + break; + + case ':': + if (_yychar == ':') { + yyinp(); + tok->f.kind = T_COLON_COLON; + } else { + tok->f.kind = T_COLON; + } + break; + + case '.': + if (_yychar == '*') { + yyinp(); + tok->f.kind = T_DOT_STAR; + } else if (_yychar == '.') { + yyinp(); + // ### assert(_yychar); + if (_yychar == '.') { + yyinp(); + tok->f.kind = T_DOT_DOT_DOT; + } else { + tok->f.kind = T_ERROR; + } + } else if (std::isdigit(_yychar)) { + do { + if (_yychar == 'e' || _yychar == 'E') { + yyinp(); + if (_yychar == '-' || _yychar == '+') { + yyinp(); + // ### assert(std::isdigit(_yychar)); + } + } else if (std::isalnum(_yychar) || _yychar == '.') { + yyinp(); + } else { + break; + } + } while (_yychar); + tok->f.kind = T_NUMERIC_LITERAL; + } else { + tok->f.kind = T_DOT; + } + break; + + case '?': + tok->f.kind = T_QUESTION; + break; + + case '+': + if (_yychar == '+') { + yyinp(); + tok->f.kind = T_PLUS_PLUS; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_PLUS_EQUAL; + } else { + tok->f.kind = T_PLUS; + } + break; + + case '-': + if (_yychar == '-') { + yyinp(); + tok->f.kind = T_MINUS_MINUS; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_MINUS_EQUAL; + } else if (_yychar == '>') { + yyinp(); + if (_yychar == '*') { + yyinp(); + tok->f.kind = T_ARROW_STAR; + } else { + tok->f.kind = T_ARROW; + } + } else { + tok->f.kind = T_MINUS; + } + break; + + case '*': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_STAR_EQUAL; + } else { + tok->f.kind = T_STAR; + } + break; + + case '/': + if (_yychar == '/') { + yyinp(); + + bool doxy = false; + + if (_yychar == '/' || _yychar == '!') { + yyinp(); + + if (_yychar == '<') + yyinp(); + + if (_yychar != '\n' && std::isspace(_yychar)) + doxy = true; + } + + while (_yychar && _yychar != '\n') + yyinp(); + + if (! f._scanCommentTokens) + goto _Lagain; + + tok->f.kind = doxy ? T_CPP_DOXY_COMMENT : T_CPP_COMMENT; + + } else if (_yychar == '*') { + yyinp(); + + bool doxy = false; + + if (_yychar == '*' || _yychar == '!') { + const char ch = _yychar; + + yyinp(); + + if (ch == '*' && _yychar == '/') + goto _Ldone; + + if (_yychar == '<') + yyinp(); + + if (! _yychar || std::isspace(_yychar)) + doxy = true; + } + + while (_yychar) { + if (_yychar != '*') { + yyinp(); + } else { + yyinp(); + if (_yychar == '/') + break; + } + } + + _Ldone: + if (_yychar) + yyinp(); + else + _state = doxy ? State_MultiLineDoxyComment : State_MultiLineComment; + + if (! f._scanCommentTokens) + goto _Lagain; + + tok->f.kind = doxy ? T_DOXY_COMMENT : T_COMMENT; + + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_SLASH_EQUAL; + } else { + tok->f.kind = T_SLASH; + } + break; + + case '%': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_PERCENT_EQUAL; + } else { + tok->f.kind = T_PERCENT; + } + break; + + case '^': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_CARET_EQUAL; + } else { + tok->f.kind = T_CARET; + } + break; + + case '&': + if (_yychar == '&') { + yyinp(); + tok->f.kind = T_AMPER_AMPER; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_AMPER_EQUAL; + } else { + tok->f.kind = T_AMPER; + } + break; + + case '|': + if (_yychar == '|') { + yyinp(); + tok->f.kind = T_PIPE_PIPE; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_PIPE_EQUAL; + } else { + tok->f.kind = T_PIPE; + } + break; + + case '~': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_TILDE_EQUAL; + } else { + tok->f.kind = T_TILDE; + } + break; + + case '!': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_EXCLAIM_EQUAL; + } else { + tok->f.kind = T_EXCLAIM; + } + break; + + case '=': + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_EQUAL_EQUAL; + } else { + tok->f.kind = T_EQUAL; + } + break; + + case '<': + if (f._scanAngleStringLiteralTokens) { + //const char *yytext = _currentChar; + while (_yychar && _yychar != '>') + yyinp(); + //int yylen = _currentChar - yytext; + // ### assert(_yychar == '>'); + if (_yychar == '>') + yyinp(); + tok->f.kind = T_ANGLE_STRING_LITERAL; + } else if (_yychar == '<') { + yyinp(); + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_LESS_LESS_EQUAL; + } else + tok->f.kind = T_LESS_LESS; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_LESS_EQUAL; + } else { + tok->f.kind = T_LESS; + } + break; + + case '>': + if (_yychar == '>') { + yyinp(); + if (_yychar == '=') { + yyinp(); + tok->f.kind = T_GREATER_GREATER_EQUAL; + } else + tok->f.kind = T_LESS_LESS; + tok->f.kind = T_GREATER_GREATER; + } else if (_yychar == '=') { + yyinp(); + tok->f.kind = T_GREATER_EQUAL; + } else { + tok->f.kind = T_GREATER; + } + break; + + case ',': + tok->f.kind = T_COMMA; + break; + + default: { + if (f._objCEnabled) { + if (ch == '@' && _yychar >= 'a' && _yychar <= 'z') { + //const char *yytext = _currentChar; + + do { + yyinp(); + if (! (isalnum(_yychar) || _yychar == '_' || _yychar == '$')) + break; + } while (_yychar); + + // const int yylen = _currentChar - yytext; + //tok->f.kind = classifyObjCAtKeyword(yytext, yylen); /// ### FIXME + break; + } else if (ch == '@' && _yychar == '"') { + // objc @string literals + ch = _yychar; + yyinp(); + tok->f.kind = T_AT_STRING_LITERAL; + + //const char *yytext = _currentChar; + + while (_yychar && _yychar != '"') { + if (_yychar != '\\') + yyinp(); + else { + yyinp(); // skip `\\' + + if (_yychar) + yyinp(); + } + } + // assert(_yychar == '"'); + + //int yylen = _currentChar - yytext; + + if (_yychar == '"') + yyinp(); + + break; + } + } + + if (ch == 'L' && (_yychar == '"' || _yychar == '\'')) { + // wide char/string literals + ch = _yychar; + yyinp(); + + const char quote = ch; + + tok->f.kind = quote == '"' + ? T_WIDE_STRING_LITERAL + : T_WIDE_CHAR_LITERAL; + + //const char *yytext = _currentChar; + + while (_yychar && _yychar != quote) { + if (_yychar != '\\') + yyinp(); + else { + yyinp(); // skip `\\' + + if (_yychar) + yyinp(); + } + } + // assert(_yychar == quote); + + //int yylen = _currentChar - yytext; + + if (_yychar == quote) + yyinp(); + + } else if (std::isalpha(ch) || ch == '_' || ch == '$') { + //const char *yytext = _currentChar - 1; + while (std::isalnum(_yychar) || _yychar == '_' || _yychar == '$') + yyinp(); + //int yylen = _currentChar - yytext; + tok->f.kind = T_IDENTIFIER; + break; + } else if (std::isdigit(ch)) { + //const char *yytext = _currentChar - 1; + while (_yychar) { + if (_yychar == 'e' || _yychar == 'E') { + yyinp(); + if (_yychar == '-' || _yychar == '+') { + yyinp(); + // ### assert(std::isdigit(_yychar)); + } + } else if (std::isalnum(_yychar) || _yychar == '.') { + yyinp(); + } else { + break; + } + } + //int yylen = _currentChar - yytext; + tok->f.kind = T_NUMERIC_LITERAL; + break; + } else { + tok->f.kind = T_ERROR; + break; + } + } // default + + } // switch +} + +} diff --git a/src/plugins/scanner/cpp/Lexer.h b/src/plugins/scanner/cpp/Lexer.h new file mode 100644 index 00000000..2b843a14 --- /dev/null +++ b/src/plugins/scanner/cpp/Lexer.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CPLUSPLUS_LEXER_H +#define CPLUSPLUS_LEXER_H + +#include "CPlusPlusForwardDeclarations.h" +#include "Token.h" + + +namespace CPlusPlus { + +class CPLUSPLUS_EXPORT Lexer +{ + Lexer(const Lexer &other); + void operator =(const Lexer &other); + +public: + enum State { + State_Default, + State_MultiLineComment, + State_MultiLineDoxyComment + }; + + Lexer(const char *firstChar, const char *lastChar); + ~Lexer(); + + bool qtMocRunEnabled() const; + void setQtMocRunEnabled(bool onoff); + + bool cxx0xEnabled() const; + void setCxxOxEnabled(bool onoff); + + bool objCEnabled() const; + void setObjCEnabled(bool onoff); + + void scan(Token *tok); + + inline void operator()(Token *tok) + { scan(tok); } + + unsigned tokenOffset() const; + unsigned tokenLength() const; + const char *tokenBegin() const; + const char *tokenEnd() const; + unsigned currentLine() const; + + bool scanCommentTokens() const; + void setScanCommentTokens(bool onoff); + + bool scanAngleStringLiteralTokens() const; + void setScanAngleStringLiteralTokens(bool onoff); + + void setStartWithNewline(bool enabled); + + int state() const; + void setState(int state); + + bool isIncremental() const; + void setIncremental(bool isIncremental); + +private: + void scan_helper(Token *tok); + void setSource(const char *firstChar, const char *lastChar); + static int classify(const char *string, int length, bool q, bool cxx0x); + static int classifyObjCAtKeyword(const char *s, int n); + static int classifyOperator(const char *string, int length); + + inline void yyinp() + { + if (++_currentChar == _lastChar) + _yychar = 0; + else { + _yychar = *_currentChar; + if (_yychar == '\n') + pushLineStartOffset(); + } + } + + void pushLineStartOffset(); + +private: + struct Flags { + unsigned _isIncremental: 1; + unsigned _scanCommentTokens: 1; + unsigned _scanAngleStringLiteralTokens: 1; + unsigned _qtMocRunEnabled: 1; + unsigned _cxx0xEnabled: 1; + unsigned _objCEnabled: 1; + }; + + const char *_firstChar; + const char *_currentChar; + const char *_lastChar; + const char *_tokenStart; + unsigned char _yychar; + int _state; + union { + unsigned _flags; + Flags f; + }; + unsigned _currentLine; +}; + +} // end of namespace CPlusPlus + + +#endif // CPLUSPLUS_LEXER_H diff --git a/src/plugins/scanner/cpp/Token.cpp b/src/plugins/scanner/cpp/Token.cpp new file mode 100644 index 00000000..ee125652 --- /dev/null +++ b/src/plugins/scanner/cpp/Token.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "Token.h" +#ifndef CPLUSPLUS_NO_PARSER +# include "Literals.h" +#endif + +using namespace CPlusPlus; + +static const char *token_names[] = { + (""), (""), + + (""), (""), + (""), (""), + + (""), (""), (""), + (""), (""), (""), + ("<@string literal>"), (""), + + ("&"), ("&&"), ("&="), ("->"), ("->*"), ("^"), ("^="), (":"), ("::"), + (","), ("/"), ("/="), ("."), ("..."), (".*"), ("="), ("=="), ("!"), + ("!="), (">"), (">="), (">>"), (">>="), ("{"), ("["), ("<"), ("<="), + ("<<"), ("<<="), ("("), ("-"), ("-="), ("--"), ("%"), ("%="), ("|"), + ("|="), ("||"), ("+"), ("+="), ("++"), ("#"), ("##"), ("?"), ("}"), + ("]"), (")"), (";"), ("*"), ("*="), ("~"), ("~="), + + ("asm"), ("auto"), ("bool"), ("break"), ("case"), ("catch"), ("char"), + ("class"), ("const"), ("const_cast"), ("continue"), ("default"), + ("delete"), ("do"), ("double"), ("dynamic_cast"), ("else"), ("enum"), + ("explicit"), ("export"), ("extern"), ("false"), ("float"), ("for"), + ("friend"), ("goto"), ("if"), ("inline"), ("int"), ("long"), + ("mutable"), ("namespace"), ("new"), ("operator"), ("private"), + ("protected"), ("public"), ("register"), ("reinterpret_cast"), + ("return"), ("short"), ("signed"), ("sizeof"), ("static"), + ("static_cast"), ("struct"), ("switch"), ("template"), ("this"), + ("throw"), ("true"), ("try"), ("typedef"), ("typeid"), ("typename"), + ("union"), ("unsigned"), ("using"), ("virtual"), ("void"), + ("volatile"), ("wchar_t"), ("while"), + + // gnu + ("__attribute__"), ("__typeof__"), + + // objc @keywords + ("@catch"), ("@class"), ("@compatibility_alias"), ("@defs"), ("@dynamic"), + ("@encode"), ("@end"), ("@finally"), ("@implementation"), ("@interface"), + ("@not_keyword"), ("@optional"), ("@package"), ("@private"), ("@property"), + ("@protected"), ("@protocol"), ("@public"), ("@required"), ("@selector"), + ("@synchronized"), ("@synthesize"), ("@throw"), ("@try"), + + // Qt keywords + ("SIGNAL"), ("SLOT"), ("Q_SIGNAL"), ("Q_SLOT"), ("signals"), ("slots"), + ("Q_FOREACH"), ("Q_D"), ("Q_Q"), + ("Q_INVOKABLE"), ("Q_PROPERTY"), ("Q_INTERFACES"), ("Q_ENUMS"), ("Q_FLAGS"), + ("Q_PRIVATE_SLOT"), ("Q_DECLARE_INTERFACE"), ("Q_OBJECT"), ("Q_GADGET"), + ("Q_NAMESPACE"), + +}; + +Token::Token() : + flags(0), offset(0), ptr(0) +{ +} + +Token::~Token() +{ +} + +void Token::reset() +{ + flags = 0; + offset = 0; + ptr = 0; +} + +const char *Token::name(int kind) +{ return token_names[kind]; } + +#ifndef CPLUSPLUS_NO_PARSER +const char *Token::spell() const +{ + switch (f.kind) { + case T_IDENTIFIER: + return identifier->chars(); + + case T_NUMERIC_LITERAL: + case T_CHAR_LITERAL: + case T_STRING_LITERAL: + case T_AT_STRING_LITERAL: + case T_ANGLE_STRING_LITERAL: + case T_WIDE_CHAR_LITERAL: + case T_WIDE_STRING_LITERAL: + return literal->chars(); + + default: + return token_names[f.kind]; + } // switch +} +#endif + + diff --git a/src/plugins/scanner/cpp/Token.h b/src/plugins/scanner/cpp/Token.h new file mode 100644 index 00000000..286c71a4 --- /dev/null +++ b/src/plugins/scanner/cpp/Token.h @@ -0,0 +1,374 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2008 Roberto Raggi +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef CPLUSPLUS_TOKEN_H +#define CPLUSPLUS_TOKEN_H + +#include "CPlusPlusForwardDeclarations.h" + +namespace CPlusPlus { + +enum Kind { + T_EOF_SYMBOL = 0, + T_ERROR, + + T_CPP_COMMENT, + T_CPP_DOXY_COMMENT, + T_COMMENT, + T_DOXY_COMMENT, + T_IDENTIFIER, + + T_FIRST_LITERAL, + T_NUMERIC_LITERAL = T_FIRST_LITERAL, + T_CHAR_LITERAL, + T_WIDE_CHAR_LITERAL, + T_STRING_LITERAL, + T_WIDE_STRING_LITERAL, + T_AT_STRING_LITERAL, + T_ANGLE_STRING_LITERAL, + T_LAST_LITERAL = T_ANGLE_STRING_LITERAL, + + T_FIRST_OPERATOR, + T_AMPER = T_FIRST_OPERATOR, + T_AMPER_AMPER, + T_AMPER_EQUAL, + T_ARROW, + T_ARROW_STAR, + T_CARET, + T_CARET_EQUAL, + T_COLON, + T_COLON_COLON, + T_COMMA, + T_SLASH, + T_SLASH_EQUAL, + T_DOT, + T_DOT_DOT_DOT, + T_DOT_STAR, + T_EQUAL, + T_EQUAL_EQUAL, + T_EXCLAIM, + T_EXCLAIM_EQUAL, + T_GREATER, + T_GREATER_EQUAL, + T_GREATER_GREATER, + T_GREATER_GREATER_EQUAL, + T_LBRACE, + T_LBRACKET, + T_LESS, + T_LESS_EQUAL, + T_LESS_LESS, + T_LESS_LESS_EQUAL, + T_LPAREN, + T_MINUS, + T_MINUS_EQUAL, + T_MINUS_MINUS, + T_PERCENT, + T_PERCENT_EQUAL, + T_PIPE, + T_PIPE_EQUAL, + T_PIPE_PIPE, + T_PLUS, + T_PLUS_EQUAL, + T_PLUS_PLUS, + T_POUND, + T_POUND_POUND, + T_QUESTION, + T_RBRACE, + T_RBRACKET, + T_RPAREN, + T_SEMICOLON, + T_STAR, + T_STAR_EQUAL, + T_TILDE, + T_TILDE_EQUAL, + T_LAST_OPERATOR = T_TILDE_EQUAL, + + T_FIRST_KEYWORD, + T_ASM = T_FIRST_KEYWORD, + T_AUTO, + T_BOOL, + T_BREAK, + T_CASE, + T_CATCH, + T_CHAR, + T_CLASS, + T_CONST, + T_CONST_CAST, + T_CONTINUE, + T_DEFAULT, + T_DELETE, + T_DO, + T_DOUBLE, + T_DYNAMIC_CAST, + T_ELSE, + T_ENUM, + T_EXPLICIT, + T_EXPORT, + T_EXTERN, + T_FALSE, + T_FLOAT, + T_FOR, + T_FRIEND, + T_GOTO, + T_IF, + T_INLINE, + T_INT, + T_LONG, + T_MUTABLE, + T_NAMESPACE, + T_NEW, + T_OPERATOR, + T_PRIVATE, + T_PROTECTED, + T_PUBLIC, + T_REGISTER, + T_REINTERPRET_CAST, + T_RETURN, + T_SHORT, + T_SIGNED, + T_SIZEOF, + T_STATIC, + T_STATIC_CAST, + T_STRUCT, + T_SWITCH, + T_TEMPLATE, + T_THIS, + T_THROW, + T_TRUE, + T_TRY, + T_TYPEDEF, + T_TYPEID, + T_TYPENAME, + T_UNION, + T_UNSIGNED, + T_USING, + T_VIRTUAL, + T_VOID, + T_VOLATILE, + T_WCHAR_T, + T_WHILE, + + T___ATTRIBUTE__, + T___TYPEOF__, + + // obj c++ @ keywords + T_FIRST_OBJC_AT_KEYWORD, + + T_AT_CATCH = T_FIRST_OBJC_AT_KEYWORD, + T_AT_CLASS, + T_AT_COMPATIBILITY_ALIAS, + T_AT_DEFS, + T_AT_DYNAMIC, + T_AT_ENCODE, + T_AT_END, + T_AT_FINALLY, + T_AT_IMPLEMENTATION, + T_AT_INTERFACE, + T_AT_NOT_KEYWORD, + T_AT_OPTIONAL, + T_AT_PACKAGE, + T_AT_PRIVATE, + T_AT_PROPERTY, + T_AT_PROTECTED, + T_AT_PROTOCOL, + T_AT_PUBLIC, + T_AT_REQUIRED, + T_AT_SELECTOR, + T_AT_SYNCHRONIZED, + T_AT_SYNTHESIZE, + T_AT_THROW, + T_AT_TRY, + + T_LAST_OBJC_AT_KEYWORD = T_AT_TRY, + + T_FIRST_QT_KEYWORD, + + // Qt keywords + T_SIGNAL = T_FIRST_QT_KEYWORD, + T_SLOT, + T_Q_SIGNAL, + T_Q_SLOT, + T_Q_SIGNALS, + T_Q_SLOTS, + T_Q_FOREACH, + T_Q_D, + T_Q_Q, + T_Q_INVOKABLE, + T_Q_PROPERTY, + T_Q_INTERFACES, + T_Q_ENUMS, + T_Q_FLAGS, + T_Q_PRIVATE_SLOT, + T_Q_DECLARE_INTERFACE, + T_Q_OBJECT, + T_Q_GADGET, + T_Q_NAMESPACE, + T_LAST_KEYWORD = T_Q_NAMESPACE, + + // aliases + T_OR = T_PIPE_PIPE, + T_AND = T_AMPER_AMPER, + T_NOT = T_EXCLAIM, + T_XOR = T_CARET, + T_BITOR = T_PIPE, + T_COMPL = T_TILDE, + T_OR_EQ = T_PIPE_EQUAL, + T_AND_EQ = T_AMPER_EQUAL, + T_BITAND = T_AMPER, + T_NOT_EQ = T_EXCLAIM_EQUAL, + T_XOR_EQ = T_CARET_EQUAL, + + T___ASM = T_ASM, + T___ASM__ = T_ASM, + + T_TYPEOF = T___TYPEOF__, + T___TYPEOF = T___TYPEOF__, + + T___INLINE = T_INLINE, + T___INLINE__ = T_INLINE, + + T___CONST = T_CONST, + T___CONST__ = T_CONST, + + T___VOLATILE = T_VOLATILE, + T___VOLATILE__ = T_VOLATILE, + + T___ATTRIBUTE = T___ATTRIBUTE__ +}; + +class CPLUSPLUS_EXPORT Token +{ +public: + Token(); + ~Token(); + + inline bool is(unsigned k) const { return f.kind == k; } + inline bool isNot(unsigned k) const { return f.kind != k; } +#ifndef CPLUSPLUS_NO_PARSER + const char *spell() const; +#endif + void reset(); + + inline unsigned kind() const { return f.kind; } + inline bool newline() const { return f.newline; } + inline bool whitespace() const { return f.whitespace; } + inline bool joined() const { return f.joined; } + inline bool expanded() const { return f.expanded; } + inline bool generated() const { return f.generated; } + inline unsigned length() const { return f.length; } + + inline unsigned begin() const + { return offset; } + + inline unsigned end() const + { return offset + f.length; } + + inline bool isLiteral() const + { return f.kind >= T_FIRST_LITERAL && f.kind <= T_LAST_LITERAL; } + + inline bool isOperator() const + { return f.kind >= T_FIRST_OPERATOR && f.kind <= T_LAST_OPERATOR; } + + inline bool isKeyword() const + { return f.kind >= T_FIRST_KEYWORD && f.kind < T_FIRST_QT_KEYWORD; } + + inline bool isComment() const + { return f.kind == T_COMMENT || f.kind == T_DOXY_COMMENT || + f.kind == T_CPP_COMMENT || f.kind == T_CPP_DOXY_COMMENT; } + + inline bool isObjCAtKeyword() const + { return f.kind >= T_FIRST_OBJC_AT_KEYWORD && f.kind <= T_LAST_OBJC_AT_KEYWORD; } + + static const char *name(int kind); + +public: + struct Flags { + unsigned kind : 8; + unsigned newline : 1; + unsigned whitespace : 1; + unsigned joined : 1; + unsigned expanded : 1; + unsigned generated : 1; + unsigned pad : 3; + unsigned length : 16; + }; + union { + unsigned flags; + Flags f; + }; + + unsigned offset; + + union { + void *ptr; +#ifndef CPLUSPLUS_NO_PARSER + const Literal *literal; + const NumericLiteral *number; + const StringLiteral *string; + const Identifier *identifier; +#endif + unsigned close_brace; + unsigned lineno; + }; +}; + +} // end of namespace CPlusPlus + + +#endif // CPLUSPLUS_TOKEN_H diff --git a/src/plugins/scanner/cpp/cpp.pro b/src/plugins/scanner/cpp/cpp.pro new file mode 100644 index 00000000..cf8e6c34 --- /dev/null +++ b/src/plugins/scanner/cpp/cpp.pro @@ -0,0 +1,11 @@ +include(../../plugins.pri) +DEFINES += CPLUSPLUS_NO_PARSER + +TARGET = qbs_cpp_scanner + +QT = core + +HEADERS += CPlusPlusForwardDeclarations.h Lexer.h Token.h ../scanner.h \ + cpp_global.h +SOURCES += Lexer.cpp Token.cpp \ + cppscanner.cpp diff --git a/src/plugins/scanner/cpp/cpp.qbs b/src/plugins/scanner/cpp/cpp.qbs new file mode 100644 index 00000000..ce15c59f --- /dev/null +++ b/src/plugins/scanner/cpp/cpp.qbs @@ -0,0 +1,18 @@ +import qbs 1.0 +import "../scannerplugin.qbs" as ScannerPlugin + +ScannerPlugin { + cpp.defines: ["CPLUSPLUS_NO_PARSER"] + name: "qbs_cpp_scanner" + files: [ + "../scanner.h", + "CPlusPlusForwardDeclarations.h", + "Lexer.cpp", + "Lexer.h", + "Token.cpp", + "Token.h", + "cpp_global.h", + "cppscanner.cpp" + ] +} + diff --git a/src/plugins/scanner/cpp/cpp_global.h b/src/plugins/scanner/cpp/cpp_global.h new file mode 100644 index 00000000..7ffa6e46 --- /dev/null +++ b/src/plugins/scanner/cpp/cpp_global.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CPP_GLOBAL_H +#define CPP_GLOBAL_H + +#if defined(WIN32) || defined(_WIN32) +#define CPPSCANNER_EXPORT __declspec(dllexport) +#else +#define CPPSCANNER_EXPORT +#endif + +#endif // CPP_GLOBAL_H diff --git a/src/plugins/scanner/cpp/cppscanner.cpp b/src/plugins/scanner/cpp/cppscanner.cpp new file mode 100644 index 00000000..a0f91c3a --- /dev/null +++ b/src/plugins/scanner/cpp/cppscanner.cpp @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../scanner.h" +#include "cpp_global.h" +#include "Lexer.h" + +using namespace CPlusPlus; + +#ifdef Q_OS_UNIX +#include +#include +#include +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + +struct ScanResult +{ + char *fileName; + unsigned int size; + int flags; +}; + +struct Opaq +{ + enum FileType + { + FT_UNKNOWN, FT_HPP, FT_CPP, FT_C, FT_OBJC, FT_OBJCPP, FT_RC + }; + + Opaq() + : +#ifdef Q_OS_UNIX + fd(0), + mapl(0), +#endif + fileContent(0), + fileType(FT_UNKNOWN), + hasQObjectMacro(false), + hasPluginMetaDataMacro(false), + currentResultIndex(0) + {} + + ~Opaq() + { +#ifdef Q_OS_UNIX + if (fileContent) + munmap(fileContent, mapl); + if (fd) + close(fd); +#endif + } + +#ifdef Q_OS_WIN + QFile file; +#endif +#ifdef Q_OS_UNIX + int fd; + size_t mapl; +#endif + + QString fileName; + char *fileContent; + FileType fileType; + QList includedFiles; + bool hasQObjectMacro; + bool hasPluginMetaDataMacro; + int currentResultIndex; +}; + +class TokenComparator +{ + const char * const m_fileContent; +public: + TokenComparator(const char *fileContent) + : m_fileContent(fileContent) + { + } + + bool equals(const Token &tk, const QLatin1Literal &literal) const + { + return static_cast(tk.length()) == literal.size() + && memcmp(m_fileContent + tk.begin(), literal.data(), literal.size()) == 0; + } +}; + +static void scanCppFile(void *opaq, CPlusPlus::Lexer &yylex, bool scanForFileTags, + bool scanForDependencies) +{ + const QLatin1Literal includeLiteral("include"); + const QLatin1Literal importLiteral("import"); + const QLatin1Literal defineLiteral("define"); + const QLatin1Literal qobjectLiteral("Q_OBJECT"); + const QLatin1Literal qgadgetLiteral("Q_GADGET"); + const QLatin1Literal qnamespaceLiteral("Q_NAMESPACE"); + const QLatin1Literal pluginMetaDataLiteral("Q_PLUGIN_METADATA"); + Opaq *opaque = static_cast(opaq); + const TokenComparator tc(opaque->fileContent); + Token tk; + Token oldTk; + ScanResult scanResult; + + yylex(&tk); + + while (tk.isNot(T_EOF_SYMBOL)) { + if (tk.newline() && tk.is(T_POUND)) { + yylex(&tk); + + if (scanForDependencies && !tk.newline() && tk.is(T_IDENTIFIER)) { + if (tc.equals(tk, includeLiteral) || tc.equals(tk, importLiteral)) + { + yylex.setScanAngleStringLiteralTokens(true); + yylex(&tk); + yylex.setScanAngleStringLiteralTokens(false); + + if (!tk.newline() && (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL))) { + scanResult.size = tk.length() - 2; + if (tk.is(T_STRING_LITERAL)) + scanResult.flags = SC_LOCAL_INCLUDE_FLAG; + else + scanResult.flags = SC_GLOBAL_INCLUDE_FLAG; + scanResult.fileName = opaque->fileContent + tk.begin() + 1; + opaque->includedFiles.append(scanResult); + } + } + } + } else if (tk.is(T_IDENTIFIER)) { + if (scanForFileTags) { + if (oldTk.is(T_IDENTIFIER) && tc.equals(oldTk, defineLiteral)) { + // Someone was clever and redefined Q_OBJECT or Q_PLUGIN_METADATA. + // Example: iplugin.h in Qt Creator. + } else { + if (tc.equals(tk, qobjectLiteral) || tc.equals(tk, qgadgetLiteral) || + tc.equals(tk, qnamespaceLiteral)) + { + opaque->hasQObjectMacro = true; + } else if (opaque->fileType == Opaq::FT_HPP + && tc.equals(tk, pluginMetaDataLiteral)) + { + opaque->hasPluginMetaDataMacro = true; + } + if (!scanForDependencies && opaque->hasQObjectMacro + && (opaque->hasPluginMetaDataMacro + || opaque->fileType == Opaq::FT_CPP + || opaque->fileType == Opaq::FT_OBJCPP)) + break; + } + } + + } + oldTk = tk; + yylex(&tk); + } +} + +static Opaq *openScanner(const unsigned short *filePath, Opaq::FileType fileType, int flags) +{ + QScopedPointer opaque(new Opaq); + opaque->fileName = QString::fromUtf16(filePath); + opaque->fileType = fileType; + + size_t mapl = 0; +#ifdef Q_OS_UNIX + QString filePathS = opaque->fileName; + + opaque->fd = open(qPrintable(filePathS), O_RDONLY); + if (opaque->fd == -1) { + opaque->fd = 0; + return 0; + } + + struct stat s; + int r = fstat(opaque->fd, &s); + if (r != 0) + return 0; + mapl = s.st_size; + opaque->mapl = mapl; + + void *vmap = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, opaque->fd, 0); + if (vmap == MAP_FAILED) + return 0; +#else + opaque->file.setFileName(opaque->fileName); + if (!opaque->file.open(QFile::ReadOnly)) + return 0; + + uchar *vmap = opaque->file.map(0, opaque->file.size()); + mapl = opaque->file.size(); +#endif + if (!vmap) + return 0; + + opaque->fileContent = reinterpret_cast(vmap); + CPlusPlus::Lexer lex(opaque->fileContent, opaque->fileContent + mapl); + scanCppFile(opaque.data(), lex, flags & ScanForFileTagsFlag, flags & ScanForDependenciesFlag); + return opaque.take(); +} + +template +static void *openScannerT(const unsigned short *filePath, int flags) +{ + return openScanner(filePath, t, flags); +} + +static void closeScanner(void *ptr) +{ + Opaq *opaque = static_cast(ptr); + delete opaque; +} + +static const char *next(void *opaq, int *size, int *flags) +{ + Opaq *opaque = static_cast(opaq); + if (opaque->currentResultIndex < opaque->includedFiles.count()) { + const ScanResult &result = opaque->includedFiles.at(opaque->currentResultIndex); + ++opaque->currentResultIndex; + *size = result.size; + *flags = result.flags; + return result.fileName; + } + *size = 0; + *flags = 0; + return 0; +} + +static const char **additionalFileTags(void *opaq, int *size) +{ + static const char *thMocCpp[] = { "moc_cpp" }; + static const char *thMocHpp[] = { "moc_hpp" }; + static const char *thMocPluginHpp[] = { "moc_hpp_plugin" }; + + Opaq *opaque = static_cast(opaq); + if (opaque->hasQObjectMacro) { + *size = 1; + switch (opaque->fileType) { + case Opaq::FT_CPP: + case Opaq::FT_OBJCPP: + return thMocCpp; + case Opaq::FT_HPP: + return opaque->hasPluginMetaDataMacro ? thMocPluginHpp : thMocHpp; + default: + break; + } + } + *size = 0; + return 0; +} + +extern "C" { + +ScannerPlugin hppScanner = +{ + "include_scanner", + "hpp", + openScannerT, + closeScanner, + next, + additionalFileTags, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin cppScanner = +{ + "include_scanner", + "cpp", + openScannerT, + closeScanner, + next, + additionalFileTags, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin pchCppScanner = +{ + "include_scanner", + "cpp_pch_src", + openScannerT, + closeScanner, + next, + additionalFileTags, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin cScanner = +{ + "include_scanner", + "c", + openScannerT, + closeScanner, + next, + 0, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin pchCScanner = +{ + "include_scanner", + "c_pch_src", + openScannerT, + closeScanner, + next, + additionalFileTags, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin objcppScanner = +{ + "include_scanner", + "objcpp", + openScannerT, + closeScanner, + next, + additionalFileTags, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin pchObjcppScanner = +{ + "include_scanner", + "objcpp_pch_src", + openScannerT, + closeScanner, + next, + additionalFileTags, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin objcScanner = +{ + "include_scanner", + "objc", + openScannerT, + closeScanner, + next, + additionalFileTags, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin pchObjcScanner = +{ + "include_scanner", + "objc_pch_src", + openScannerT, + closeScanner, + next, + additionalFileTags, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin rcScanner = +{ + "include_scanner", + "rc", + openScannerT, + closeScanner, + next, + 0, + ScannerUsesCppIncludePaths | ScannerRecursiveDependencies +}; + +ScannerPlugin *theScanners[] = { + &hppScanner, &pchCppScanner, &pchCScanner, &pchObjcppScanner, &pchObjcScanner, + &cppScanner, &cScanner, &objcppScanner, &objcScanner, &rcScanner, NULL +}; + +CPPSCANNER_EXPORT ScannerPlugin **getScanners() +{ + return theScanners; +} + +} // extern "C" diff --git a/src/plugins/scanner/qt/qt.pro b/src/plugins/scanner/qt/qt.pro new file mode 100644 index 00000000..d90f61b2 --- /dev/null +++ b/src/plugins/scanner/qt/qt.pro @@ -0,0 +1,9 @@ +include(../../plugins.pri) + +TARGET = qbs_qt_scanner + +QT = core + +HEADERS += ../scanner.h +SOURCES += \ + qtscanner.cpp diff --git a/src/plugins/scanner/qt/qt.qbs b/src/plugins/scanner/qt/qt.qbs new file mode 100644 index 00000000..ecc71f8c --- /dev/null +++ b/src/plugins/scanner/qt/qt.qbs @@ -0,0 +1,11 @@ +import qbs 1.0 +import "../scannerplugin.qbs" as ScannerPlugin + +ScannerPlugin { + name: "qbs_qt_scanner" + files: [ + "../scanner.h", + "qtscanner.cpp" + ] +} + diff --git a/src/plugins/scanner/qt/qtscanner.cpp b/src/plugins/scanner/qt/qtscanner.cpp new file mode 100644 index 00000000..75f33de3 --- /dev/null +++ b/src/plugins/scanner/qt/qtscanner.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined(WIN32) || defined(_WIN32) +#define SCANNER_EXPORT __declspec(dllexport) +#else +#define SCANNER_EXPORT +#endif + +#include "../scanner.h" + +#include + +#ifdef Q_OS_UNIX +#include +#include +#include +#include +#include +#else +#include +#endif + +#include +#include +#include + +struct OpaqQrc +{ +#ifdef Q_OS_UNIX + int fd; + int mapl; +#else + QFile *file; +#endif + + char *map; + QXmlStreamReader *xml; + QByteArray current; + OpaqQrc() +#ifdef Q_OS_UNIX + : fd (0), +#else + : file(0), +#endif + map(0), + xml(0) + {} + + ~OpaqQrc() + { +#ifdef Q_OS_UNIX + if (map) + munmap (map, mapl); + if (fd) + close (fd); +#else + delete file; +#endif + delete xml; + } +}; + +static void *openScannerQrc(const unsigned short *filePath, int flags) +{ + Q_UNUSED(flags); + QScopedPointer opaque(new OpaqQrc); + +#ifdef Q_OS_UNIX + QString filePathS = QString::fromUtf16(filePath); + opaque->fd = open(qPrintable(filePathS), O_RDONLY); + if (opaque->fd == -1) { + opaque->fd = 0; + return 0; + } + + struct stat s; + int r = fstat(opaque->fd, &s); + if (r != 0) + return 0; + opaque->mapl = s.st_size; + + void *map = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, opaque->fd, 0); + if (map == 0) + return 0; +#else + opaque->file = new QFile(QString::fromUtf16(filePath)); + if (!opaque->file->open(QFile::ReadOnly)) + return 0; + + uchar *map = opaque->file->map(0, opaque->file->size()); + if (!map) + return 0; +#endif + + opaque->map = reinterpret_cast(map); + opaque->xml = new QXmlStreamReader(opaque->map); + + return static_cast(opaque.take()); +} + +static void closeScannerQrc(void *ptr) +{ + OpaqQrc *opaque = static_cast(ptr); + delete opaque; +} + +static const char *nextQrc(void *opaq, int *size, int *flags) +{ + OpaqQrc *o= static_cast(opaq); + while (!o->xml->atEnd()) { + o->xml->readNext(); + switch (o->xml->tokenType()) { + case QXmlStreamReader::StartElement: + if (o->xml->name() == QLatin1String("file")) { + o->current = o->xml->readElementText(QXmlStreamReader::ErrorOnUnexpectedElement).toUtf8(); + *flags = SC_LOCAL_INCLUDE_FLAG; + *size = o->current.size(); + return o->current.data(); + } + break; + case QXmlStreamReader::EndDocument: + return 0; + default: + break; + } + } + return 0; +} + +static const char **additionalFileTagsQrc(void *, int *size) +{ + *size = 0; + return 0; +} + +extern "C" { + +ScannerPlugin qrcScanner = +{ + "qt_qrc_scanner", + "qrc", + openScannerQrc, + closeScannerQrc, + nextQrc, + additionalFileTagsQrc, + NoScannerFlags +}; + +ScannerPlugin *theScanners[] = {&qrcScanner, NULL}; + +SCANNER_EXPORT ScannerPlugin **getScanners() +{ + return theScanners; +} + +} // extern "C" diff --git a/src/plugins/scanner/scanner.h b/src/plugins/scanner/scanner.h new file mode 100644 index 00000000..f97ebf81 --- /dev/null +++ b/src/plugins/scanner/scanner.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/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 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef SCANNER_H +#define SCANNER_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define SC_LOCAL_INCLUDE_FLAG 0x1 +#define SC_GLOBAL_INCLUDE_FLAG 0x2 + +enum OpenScannerFlags +{ + ScanForDependenciesFlag = 0x01, + ScanForFileTagsFlag = 0x02 +}; + +/** + * Open a file that's going to be scanned. + * The file path encoding is UTF-16 on all platforms. + * + * Returns a scanner handle. + */ +typedef void *(*scanOpen_f) (const unsigned short *filePath, int flags); + +/** + * Closes the given scanner handle. + */ +typedef void (*scanClose_f) (void *opaq); + +/** + * Return the next result (filename) of the scan. + */ +typedef const char *(*scanNext_f) (void *opaq, int *size, int *flags); + +/** + * Returns a list of type hints for the scanned file. + * May return null. + * + * Example: if a C++ header file contains Q_OBJECT, + * the type hint 'moc_hpp' is returned. + */ +typedef const char** (*scanAdditionalFileTags_f) (void *opaq, int *size); + +enum ScannerFlags +{ + NoScannerFlags = 0x00, + ScannerUsesCppIncludePaths = 0x01, + ScannerRecursiveDependencies = 0x02 +}; + +class ScannerPlugin +{ +public: + const char *name; + const char *fileTag; + scanOpen_f open; + scanClose_f close; + scanNext_f next; + scanAdditionalFileTags_f additionalFileTags; + int flags; +}; + +typedef ScannerPlugin **(*getScanners_f)(); + +#ifdef __cplusplus +} +#endif +#endif // SCANNER_H diff --git a/src/plugins/scanner/scanner.pro b/src/plugins/scanner/scanner.pro new file mode 100644 index 00000000..68acae7d --- /dev/null +++ b/src/plugins/scanner/scanner.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = cpp qt + diff --git a/src/plugins/scanner/scannerplugin.qbs b/src/plugins/scanner/scannerplugin.qbs new file mode 100644 index 00000000..2b34926b --- /dev/null +++ b/src/plugins/scanner/scannerplugin.qbs @@ -0,0 +1,15 @@ +import qbs 1.0 + +DynamicLibrary { + Depends { name: "cpp" } + Depends { name: "Qt.core" } + Depends { name: "qbsbuildconfig" } + cpp.cxxLanguageVersion: "c++11" + destinationDirectory: qbsbuildconfig.libDirName + "/qbs/plugins" + Group { + fileTagsFilter: ["dynamiclibrary"] + qbs.install: true + qbs.installDir: qbsbuildconfig.pluginsInstallDir + "/qbs/plugins" + } + bundle.isBundle: false +} diff --git a/src/src.qbs b/src/src.qbs new file mode 100644 index 00000000..c501cb43 --- /dev/null +++ b/src/src.qbs @@ -0,0 +1,10 @@ +import qbs + +Project { + references: [ + "app/apps.qbs", + "lib/libs.qbs", + "libexec/libexec.qbs", + "plugins/plugins.qbs" + ] +} diff --git a/static.pro b/static.pro new file mode 100644 index 00000000..b4f15c78 --- /dev/null +++ b/static.pro @@ -0,0 +1,41 @@ +TEMPLATE = aux + +DATA_DIRS = share/qbs/imports share/qbs/modules +win32:DATA_FILES = $$PWD/bin/ibmsvc.xml $$PWD/bin/ibqbs.bat + +# For use in custom compilers which just copy files +defineReplace(stripSrcDir) { + return($$relative_path($$absolute_path($$1, $$OUT_PWD), $$_PRO_FILE_PWD_)) +} + +for(data_dir, DATA_DIRS) { + files = $$files($$PWD/$$data_dir/*, true) + for(file, files):!exists($$file/*):FILES += $$file +} +FILES += $$DATA_FILES + +OTHER_FILES += $$FILES + +!isEqual(PWD, $$OUT_PWD)|!isEmpty(QBS_RESOURCES_BUILD_DIR) { + copy2build.input = FILES + !isEmpty(QBS_RESOURCES_BUILD_DIR): \ + copy2build.output = $${QBS_RESOURCES_BUILD_DIR}/${QMAKE_FUNC_FILE_IN_stripSrcDir} + else: \ + copy2build.output = ${QMAKE_FUNC_FILE_IN_stripSrcDir} + copy2build.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} + copy2build.name = COPY ${QMAKE_FILE_IN} + copy2build.CONFIG += no_link target_predeps + QMAKE_EXTRA_COMPILERS += copy2build +} + +include(src/install_prefix.pri) + +share.files = share/qbs +!isEmpty(QBS_RESOURCES_INSTALL_DIR): \ + installPrefix = $${QBS_RESOURCES_INSTALL_DIR} +else: \ + installPrefix = $${QBS_INSTALL_PREFIX} +share.path = $${installPrefix}/share +examples.files = examples +examples.path = $${share.path}/qbs +INSTALLS += share examples diff --git a/sync.profile b/sync.profile new file mode 100644 index 00000000..0a211643 --- /dev/null +++ b/sync.profile @@ -0,0 +1,4 @@ +%dependencies = ( + "qtbase" => "5.7", + "qtscript" => "5.7", +); diff --git a/tests/auto/api/api.pro b/tests/auto/api/api.pro new file mode 100644 index 00000000..e78d111d --- /dev/null +++ b/tests/auto/api/api.pro @@ -0,0 +1,25 @@ +TARGET = tst_api + +HEADERS = tst_api.h +SOURCES = tst_api.cpp + +include(../../../src/library_dirname.pri) +isEmpty(QBS_RELATIVE_LIBEXEC_PATH):QBS_RELATIVE_LIBEXEC_PATH=../libexec/qbs +isEmpty(QBS_RELATIVE_PLUGINS_PATH):QBS_RELATIVE_PLUGINS_PATH=../$${QBS_LIBRARY_DIRNAME} +isEmpty(QBS_RELATIVE_SEARCH_PATH):QBS_RELATIVE_SEARCH_PATH=.. +DEFINES += QBS_RELATIVE_LIBEXEC_PATH=\\\"$${QBS_RELATIVE_LIBEXEC_PATH}\\\" +DEFINES += QBS_RELATIVE_PLUGINS_PATH=\\\"$${QBS_RELATIVE_PLUGINS_PATH}\\\" +DEFINES += QBS_RELATIVE_SEARCH_PATH=\\\"$${QBS_RELATIVE_SEARCH_PATH}\\\" +qbs_enable_project_file_updates:DEFINES += QBS_ENABLE_PROJECT_FILE_UPDATES + +include(../auto.pri) + +DATA_DIRS = testdata + +for(data_dir, DATA_DIRS) { + files = $$files($$PWD/$$data_dir/*, true) + win32:files ~= s|\\\\|/|g + for(file, files):!exists($$file/*):FILES += $$file +} + +OTHER_FILES += $$FILES diff --git a/tests/auto/api/api.qbs b/tests/auto/api/api.qbs new file mode 100644 index 00000000..68e7cde4 --- /dev/null +++ b/tests/auto/api/api.qbs @@ -0,0 +1,19 @@ +import qbs + +QbsAutotest { + testName: "api" + files: ["../shared.h", "tst_api.h", "tst_api.cpp"] + cpp.defines: base.concat([ + 'SRCDIR="' + path + '"', + 'QBS_RELATIVE_LIBEXEC_PATH="' + qbsbuildconfig.relativeLibexecPath + '"', + 'QBS_RELATIVE_SEARCH_PATH="' + qbsbuildconfig.relativeSearchPath + '"', + 'QBS_RELATIVE_PLUGINS_PATH="' + qbsbuildconfig.relativePluginsPath + '"' + ]).concat(qbsbuildconfig.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : []) + + Group { + name: "testdata" + prefix: "testdata/" + files: ["**/*"] + fileTags: [] + } +} diff --git a/tests/auto/api/testdata/QBS-728/project.qbs b/tests/auto/api/testdata/QBS-728/project.qbs new file mode 100644 index 00000000..6ab244c8 --- /dev/null +++ b/tests/auto/api/testdata/QBS-728/project.qbs @@ -0,0 +1,7 @@ +import qbs + +Product { + property bool isBlubbOS: qbs.targetOS.contains("blubb-OS") + profiles: isBlubbOS ? ["blubb-profile"] : [project.profile] + qbs.architecture: "blubb-arch" +} diff --git a/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/main.cpp b/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/main.cpp new file mode 100644 index 00000000..39f308dc --- /dev/null +++ b/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/main.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "object.h" + +int main() +{ + Object o; + o.f(); +} diff --git a/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/object.cpp b/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/object.cpp new file mode 100644 index 00000000..55ceb9e7 --- /dev/null +++ b/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/object.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "object.h" + +#include + +// class InternalClass : public QObject +// { +// Q_OBJECT +// }; + +void Object::f() { } + + +// #include "object.moc" diff --git a/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/object.h b/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/object.h new file mode 100644 index 00000000..8cb60ead --- /dev/null +++ b/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/object.h @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +class Object { +public: + void f(); +}; diff --git a/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/project.qbs b/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/project.qbs new file mode 100644 index 00000000..40c5395e --- /dev/null +++ b/tests/auto/api/testdata/add-qobject-macro-to-cpp-file/project.qbs @@ -0,0 +1,6 @@ +import qbs + +QtApplication { + files: ["main.cpp", "object.h", "object.cpp"] +} + diff --git a/tests/auto/api/testdata/added-file-persistent/file.cpp b/tests/auto/api/testdata/added-file-persistent/file.cpp new file mode 100644 index 00000000..833057ed --- /dev/null +++ b/tests/auto/api/testdata/added-file-persistent/file.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f() { } diff --git a/tests/auto/api/testdata/added-file-persistent/main.cpp b/tests/auto/api/testdata/added-file-persistent/main.cpp new file mode 100644 index 00000000..17dbeb05 --- /dev/null +++ b/tests/auto/api/testdata/added-file-persistent/main.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f(); + +int main() { f(); } diff --git a/tests/auto/api/testdata/added-file-persistent/project.qbs b/tests/auto/api/testdata/added-file-persistent/project.qbs new file mode 100644 index 00000000..67288664 --- /dev/null +++ b/tests/auto/api/testdata/added-file-persistent/project.qbs @@ -0,0 +1,8 @@ +import qbs + +CppApplication { + files: [ + 'main.cpp', + /* 'file.cpp' */ + ] +} diff --git a/tests/auto/api/testdata/app-without-sources/a.c b/tests/auto/api/testdata/app-without-sources/a.c new file mode 100644 index 00000000..fab68c6a --- /dev/null +++ b/tests/auto/api/testdata/app-without-sources/a.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int foo() { return 42; } diff --git a/tests/auto/api/testdata/app-without-sources/b.c b/tests/auto/api/testdata/app-without-sources/b.c new file mode 100644 index 00000000..31c64c75 --- /dev/null +++ b/tests/auto/api/testdata/app-without-sources/b.c @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int foo(); // defined in a.cpp + +int main() +{ + return foo(); +} + diff --git a/tests/auto/api/testdata/app-without-sources/project.qbs b/tests/auto/api/testdata/app-without-sources/project.qbs new file mode 100644 index 00000000..72672633 --- /dev/null +++ b/tests/auto/api/testdata/app-without-sources/project.qbs @@ -0,0 +1,39 @@ +import qbs 1.0 + +Project { + StaticLibrary { + name: "a" + + Depends { name: "cpp" } + + files: [ + "a.c", + ] + } + + StaticLibrary { + name: "b" + + Depends { name: "a" } + Depends { name: "cpp" } + + files: [ + "b.c", + ] + } + + CppApplication { + name: "appWithoutSources" + + // HACK: cpp.entryPoint currently not working 100% with gcc + Properties { + condition: qbs.toolchain.contains("msvc") + cpp.entryPoint: "main" + } + cpp.entryPoint: undefined + bundle.isBundle: false + + Depends { name: "a" } + Depends { name: "b" } + } +} diff --git a/tests/auto/api/testdata/base-properties/imports/Bar.qbs b/tests/auto/api/testdata/base-properties/imports/Bar.qbs new file mode 100644 index 00000000..07ab724e --- /dev/null +++ b/tests/auto/api/testdata/base-properties/imports/Bar.qbs @@ -0,0 +1,6 @@ +import qbs 1.0 + +Product { + Depends { name: "cpp" } + cpp.defines: ["FROM_BAR"] +} diff --git a/tests/auto/api/testdata/base-properties/imports/Foo.qbs b/tests/auto/api/testdata/base-properties/imports/Foo.qbs new file mode 100644 index 00000000..f177a17c --- /dev/null +++ b/tests/auto/api/testdata/base-properties/imports/Foo.qbs @@ -0,0 +1,8 @@ +import qbs 1.0 + +Bar { + type: "application" + consoleApplication: true + cpp.defines: base.concat(["FROM_FOO"]) +} + diff --git a/tests/auto/api/testdata/base-properties/main.cpp b/tests/auto/api/testdata/base-properties/main.cpp new file mode 100644 index 00000000..9581f76b --- /dev/null +++ b/tests/auto/api/testdata/base-properties/main.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef FROM_FOO +#error FROM_FOO missing! +#endif +#ifndef FROM_BAR +#error FROM_BAR missing! +#endif +#ifndef FROM_PRJ +#error FROM_PRJ missing! +#endif + +int main() +{ + return 0; +} + diff --git a/tests/auto/api/testdata/base-properties/prj.qbs b/tests/auto/api/testdata/base-properties/prj.qbs new file mode 100644 index 00000000..a43b930d --- /dev/null +++ b/tests/auto/api/testdata/base-properties/prj.qbs @@ -0,0 +1,10 @@ +import qbs 1.0 +import "imports/Foo.qbs" as Foo + +Project { + Foo { + cpp.defines: base.concat(["FROM_PRJ"]); + files: "main.cpp" + } +} + diff --git a/tests/auto/api/testdata/build-properties-source/main.cpp b/tests/auto/api/testdata/build-properties-source/main.cpp new file mode 100644 index 00000000..f830ee1f --- /dev/null +++ b/tests/auto/api/testdata/build-properties-source/main.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifndef WORLD +# error WORLD is not defined +#endif + +int main() +{ + puts("Hello " WORLD "!"); +} diff --git a/tests/auto/api/testdata/build-properties-source/project.qbs b/tests/auto/api/testdata/build-properties-source/project.qbs new file mode 100644 index 00000000..49f565b2 --- /dev/null +++ b/tests/auto/api/testdata/build-properties-source/project.qbs @@ -0,0 +1,17 @@ +import qbs 1.0 + +Project { + Product { + type: "application" + consoleApplication: true + name: "HelloWorld" + + Depends { name: 'cpp' } + + Group { + cpp.defines: ['WORLD="BANANA"'] + files : [ "main.cpp" ] + } + } +} + diff --git a/tests/auto/api/testdata/build-single-file/compiled.cpp b/tests/auto/api/testdata/build-single-file/compiled.cpp new file mode 100644 index 00000000..474897da --- /dev/null +++ b/tests/auto/api/testdata/build-single-file/compiled.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f() {} diff --git a/tests/auto/api/testdata/build-single-file/ignored1.cpp b/tests/auto/api/testdata/build-single-file/ignored1.cpp new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/build-single-file/ignored2.cpp b/tests/auto/api/testdata/build-single-file/ignored2.cpp new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/build-single-file/project.qbs b/tests/auto/api/testdata/build-single-file/project.qbs new file mode 100644 index 00000000..58faaa03 --- /dev/null +++ b/tests/auto/api/testdata/build-single-file/project.qbs @@ -0,0 +1,10 @@ +import qbs + +CppApplication { + files: ["ignored1.cpp", "ignored2.cpp", "compiled.cpp"] + + Group { + fileTagsFilter: ["application"] + qbs.install: true + } +} diff --git a/tests/auto/api/testdata/buildgraph-locking/project.qbs b/tests/auto/api/testdata/buildgraph-locking/project.qbs new file mode 100644 index 00000000..e08b008b --- /dev/null +++ b/tests/auto/api/testdata/buildgraph-locking/project.qbs @@ -0,0 +1,4 @@ +import qbs + +Project { +} diff --git a/tests/auto/api/testdata/change-dependent-lib/change-dependent-lib.qbs b/tests/auto/api/testdata/change-dependent-lib/change-dependent-lib.qbs new file mode 100644 index 00000000..4037ae86 --- /dev/null +++ b/tests/auto/api/testdata/change-dependent-lib/change-dependent-lib.qbs @@ -0,0 +1,24 @@ +import qbs 1.0 + +Project { + Application { + name : "HelloWorld" + Group { + files : [ "main.cpp" ] + } + Depends { name: "cpp" } + Depends { name: "mylib" } + } + + DynamicLibrary { + name : "mylib" + version: "1.2.3" + Group { + files : [ "mylib.cpp" ] + } + Depends { name: "cpp" } + cpp.defines: ["XXXX"] + bundle.isBundle: false + } +} + diff --git a/tests/auto/api/testdata/change-dependent-lib/main.cpp b/tests/auto/api/testdata/change-dependent-lib/main.cpp new file mode 100644 index 00000000..d60d4b23 --- /dev/null +++ b/tests/auto/api/testdata/change-dependent-lib/main.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if defined(_WIN32) || defined(WIN32) +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT +# define IMPORT +#endif + +IMPORT int mylib_hello(); + +int main() +{ + puts("application says hello!"); + return mylib_hello(); +} + diff --git a/tests/auto/api/testdata/change-dependent-lib/mylib.cpp b/tests/auto/api/testdata/change-dependent-lib/mylib.cpp new file mode 100644 index 00000000..f24f77ee --- /dev/null +++ b/tests/auto/api/testdata/change-dependent-lib/mylib.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if defined(_WIN32) || defined(WIN32) +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT +# define IMPORT +#endif + +EXPORT int mylib_hello() +{ + puts("mylib says hello!"); + return 0; +} diff --git a/tests/auto/api/testdata/check-outputs/foo.txt b/tests/auto/api/testdata/check-outputs/foo.txt new file mode 100644 index 00000000..76e81970 --- /dev/null +++ b/tests/auto/api/testdata/check-outputs/foo.txt @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/tests/auto/api/testdata/check-outputs/project.qbs b/tests/auto/api/testdata/check-outputs/project.qbs new file mode 100644 index 00000000..27eada48 --- /dev/null +++ b/tests/auto/api/testdata/check-outputs/project.qbs @@ -0,0 +1,37 @@ +import qbs +import qbs.File + +Project { + Product { + type: 'application' + consoleApplication: true + Group { + files: 'foo.txt' + fileTags: ['text'] + } + Depends { name: 'cpp' } + } + + Rule { + inputs: ['text'] + Artifact { + fileTags: ['cpp'] + filePath: input.baseName + '.cpp' + } + Artifact { + fileTags: ['ghost'] + filePath: input.baseName + '.ghost' + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.inp = inputs["text"][0].filePath; + cmd.out = outputs["cpp"][0].filePath; + cmd.description = "generating " + outputs["cpp"][0].fileName; + cmd.sourceCode = function() { + File.copy(inp, out); + }; + return cmd; + } + } +} diff --git a/tests/auto/api/testdata/codegen/foo.txt b/tests/auto/api/testdata/codegen/foo.txt new file mode 100644 index 00000000..557db03d --- /dev/null +++ b/tests/auto/api/testdata/codegen/foo.txt @@ -0,0 +1 @@ +Hello World diff --git a/tests/auto/api/testdata/codegen/project.qbs b/tests/auto/api/testdata/codegen/project.qbs new file mode 100644 index 00000000..282ee4a7 --- /dev/null +++ b/tests/auto/api/testdata/codegen/project.qbs @@ -0,0 +1,73 @@ +import qbs 1.0 +import qbs.FileInfo + +Project { + property string name: 'codegen' + property string osSpecificName: name.toUpperCase() + '_' + qbs.targetOS[0].toUpperCase() + + Product { + type: 'application' + consoleApplication: true + name: project.name + property var replacements: ({ + NUMBERTYPE: "int", + STRINGTYPE: "char **", + FUNCTIONNAME: "main" + }) + Group { + files: 'foo.txt' + fileTags: ['text'] + } + Depends { name: 'cpp' } + Depends { name: 'Qt.core' } + } + + Rule { + inputs: ['text'] + Artifact { + fileTags: ['cpp'] + filePath: input.baseName + '.cpp' + } + prepare: { + function expandMacros(str, table) + { + var rex = /\$\w+/; + var m = rex.exec(str); + while (m != null) { + str = str.substr(0, m.index) + + table[m[0].substr(1)] + + str.substr(m.index + m[0].length); + m = rex.exec(str); + } + return str; + } + + // check whether multipart module name translation is working + var actual = product.moduleProperty("Qt.core", "mocName"); + if (!actual || !actual.contains("moc")) + throw "multipart module name translation is broken"; + + // check whether we can access project properties here + var expected = "CODEGEN_" + product.moduleProperty("qbs", "targetOS")[0].toUpperCase(); + if (project.osSpecificName !== expected) + throw "Wrong project property value: " + project.osSpecificName + + "\nexpected: " + expected; + + var code = '$NUMBERTYPE $FUNCTIONNAME($NUMBERTYPE, $STRINGTYPE) { return 0; }'; + code = expandMacros(code, product.replacements); + var args = ['echo ' + code + '>' + output.filePath] + var cmd + if (product.moduleProperty("qbs", "hostOS").contains('windows')) { + cmd = new Command('cmd.exe', ['/C'].concat(args)); + } else { + args[0] = args[0].replace(/\(/g, '\\(') + args[0] = args[0].replace(/\)/g, '\\)') + args[0] = args[0].replace(/;/g, '\\;') + cmd = new Command('/bin/sh', ['-c'].concat(args)) + } + cmd.description = 'generate\t' + FileInfo.fileName(output.filePath); + cmd.highlight = 'codegen'; + return cmd; + } + } +} diff --git a/tests/auto/api/testdata/command-extraction/main.cpp b/tests/auto/api/testdata/command-extraction/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/api/testdata/command-extraction/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/api/testdata/command-extraction/project.qbs b/tests/auto/api/testdata/command-extraction/project.qbs new file mode 100644 index 00000000..73b66aaa --- /dev/null +++ b/tests/auto/api/testdata/command-extraction/project.qbs @@ -0,0 +1,5 @@ +import qbs + +CppApplication { + files: "main.cpp" +} diff --git a/tests/auto/api/testdata/disabled-product/disabledProduct.qbs b/tests/auto/api/testdata/disabled-product/disabledProduct.qbs new file mode 100644 index 00000000..c57615c1 --- /dev/null +++ b/tests/auto/api/testdata/disabled-product/disabledProduct.qbs @@ -0,0 +1,10 @@ +import qbs + +CppApplication { + condition: false + files: "main.cpp" + Group { + condition: qbs.targetOS.contains("stuff") + qbs.install: false + } +} diff --git a/tests/auto/api/testdata/disabled-product/main.cpp b/tests/auto/api/testdata/disabled-product/main.cpp new file mode 100644 index 00000000..1f5a432d --- /dev/null +++ b/tests/auto/api/testdata/disabled-product/main.cpp @@ -0,0 +1 @@ +thiswillnotcompile diff --git a/tests/auto/api/testdata/disabled-project/disabled_project.qbs b/tests/auto/api/testdata/disabled-project/disabled_project.qbs new file mode 100644 index 00000000..1461b70a --- /dev/null +++ b/tests/auto/api/testdata/disabled-project/disabled_project.qbs @@ -0,0 +1,7 @@ +import qbs +import qbs.File + +Project { + condition: File.exists("blubb"); + references: "blubb/nosuchfile.qbs" +} diff --git a/tests/auto/api/testdata/disabled_install_group/main.cpp b/tests/auto/api/testdata/disabled_install_group/main.cpp new file mode 100644 index 00000000..e14f806b --- /dev/null +++ b/tests/auto/api/testdata/disabled_install_group/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { } diff --git a/tests/auto/api/testdata/disabled_install_group/project.qbs b/tests/auto/api/testdata/disabled_install_group/project.qbs new file mode 100644 index 00000000..6364506b --- /dev/null +++ b/tests/auto/api/testdata/disabled_install_group/project.qbs @@ -0,0 +1,11 @@ +import qbs + +CppApplication { + files: "main.cpp" + Group { + condition: false + qbs.install: true + fileTagsFilter: product.type + } + bundle.isBundle: false +} diff --git a/tests/auto/api/testdata/duplicate-product-names/explicit.qbs b/tests/auto/api/testdata/duplicate-product-names/explicit.qbs new file mode 100644 index 00000000..0183b411 --- /dev/null +++ b/tests/auto/api/testdata/duplicate-product-names/explicit.qbs @@ -0,0 +1,7 @@ +import qbs + +Project { + Product { name: "blubb" } + Product { name: "blubb" } + Product { name: "blubb" } +} diff --git a/tests/auto/api/testdata/duplicate-product-names/implicit-indirect.qbs b/tests/auto/api/testdata/duplicate-product-names/implicit-indirect.qbs new file mode 100644 index 00000000..7164aa1c --- /dev/null +++ b/tests/auto/api/testdata/duplicate-product-names/implicit-indirect.qbs @@ -0,0 +1,5 @@ +import qbs + +Project { + references: ["subdir1/subproject.qbs", "subdir2/subproject.qbs"] +} diff --git a/tests/auto/api/testdata/duplicate-product-names/implicit.qbs b/tests/auto/api/testdata/duplicate-product-names/implicit.qbs new file mode 100644 index 00000000..f39f8062 --- /dev/null +++ b/tests/auto/api/testdata/duplicate-product-names/implicit.qbs @@ -0,0 +1,7 @@ +import qbs + +Project { + Product { } + Product { } + Product { } +} diff --git a/tests/auto/api/testdata/duplicate-product-names/subdir1/subproject.qbs b/tests/auto/api/testdata/duplicate-product-names/subdir1/subproject.qbs new file mode 100644 index 00000000..6d16a3c5 --- /dev/null +++ b/tests/auto/api/testdata/duplicate-product-names/subdir1/subproject.qbs @@ -0,0 +1,3 @@ +import qbs + +Product { } diff --git a/tests/auto/api/testdata/duplicate-product-names/subdir2/subproject.qbs b/tests/auto/api/testdata/duplicate-product-names/subdir2/subproject.qbs new file mode 100644 index 00000000..6d16a3c5 --- /dev/null +++ b/tests/auto/api/testdata/duplicate-product-names/subdir2/subproject.qbs @@ -0,0 +1,3 @@ +import qbs + +Product { } diff --git a/tests/auto/api/testdata/dynamic-libs/lib1.cpp b/tests/auto/api/testdata/dynamic-libs/lib1.cpp new file mode 100644 index 00000000..ad40b829 --- /dev/null +++ b/tests/auto/api/testdata/dynamic-libs/lib1.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if defined(_WIN32) || defined(WIN32) +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT +# define IMPORT +#endif + +IMPORT void lib2_hello(); + +EXPORT int lib1_hello() +{ + puts("lib1 says hello!"); + lib2_hello(); + return 0; +} diff --git a/tests/auto/api/testdata/dynamic-libs/lib2.cpp b/tests/auto/api/testdata/dynamic-libs/lib2.cpp new file mode 100644 index 00000000..4c24b2b7 --- /dev/null +++ b/tests/auto/api/testdata/dynamic-libs/lib2.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if defined(_WIN32) || defined(WIN32) +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT __attribute__((visibility("default"))) +# define IMPORT +#endif + +IMPORT void lib3_hello(); + +EXPORT void lib2_hello() +{ + puts("lib2 says hello!"); + lib3_hello(); +} + diff --git a/tests/auto/api/testdata/dynamic-libs/lib3.cpp b/tests/auto/api/testdata/dynamic-libs/lib3.cpp new file mode 100644 index 00000000..591b1627 --- /dev/null +++ b/tests/auto/api/testdata/dynamic-libs/lib3.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if defined(_WIN32) || defined(WIN32) +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT __attribute__((visibility("default"))) +# define IMPORT +#endif + +EXPORT void lib3_hello() +{ + puts("lib3 says hello!"); +} + +EXPORT char* lib3_greeting() +{ + static char greeting[] = "hello"; + return greeting; +} + diff --git a/tests/auto/api/testdata/dynamic-libs/lib4.cpp b/tests/auto/api/testdata/dynamic-libs/lib4.cpp new file mode 100644 index 00000000..2d66e40f --- /dev/null +++ b/tests/auto/api/testdata/dynamic-libs/lib4.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lib4.h" + +TestMe::TestMe() +{ +} + +void TestMe::hello1() const +{ + puts("lib4 says hello!"); +} + +void TestMe::hello2Impl() const +{ + puts("lib4 says hello inline!"); +} diff --git a/tests/auto/api/testdata/dynamic-libs/lib4.h b/tests/auto/api/testdata/dynamic-libs/lib4.h new file mode 100644 index 00000000..2b28d31e --- /dev/null +++ b/tests/auto/api/testdata/dynamic-libs/lib4.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LIB4_H +#define LIB4_H +#include + +#ifdef TEST_LIB +# if defined(_WIN32) || defined(WIN32) +# define LIB_EXPORT __declspec(dllexport) +# define LIB_NO_EXPORT +# else +# define LIB_EXPORT __attribute__((visibility("default"))) +# endif +#else +# define LIB_EXPORT +#endif + +class LIB_EXPORT TestMe +{ +public: + TestMe(); + void hello1() const; + inline void hello2() const { hello2Impl(); } +private: + void hello2Impl() const; +}; + +#endif // LIB4_H diff --git a/tests/auto/api/testdata/dynamic-libs/link_dynamiclib.qbs b/tests/auto/api/testdata/dynamic-libs/link_dynamiclib.qbs new file mode 100644 index 00000000..fce0f8e4 --- /dev/null +++ b/tests/auto/api/testdata/dynamic-libs/link_dynamiclib.qbs @@ -0,0 +1,66 @@ +import qbs 1.0 + +Project { + Application { + name : "HelloWorld" + destinationDirectory: "bin" + Group { + files : [ "main.cpp" ] + } + Depends { name: "cpp" } + Depends { name: "lib1" } + Depends { name: "lib4" } + } + + DynamicLibrary { + name : "lib1" + destinationDirectory: "bin" + Group { + files : [ "lib1.cpp" ] + } + Depends { name: "cpp" } + Depends { name: "lib2" } + bundle.isBundle: false + } + + DynamicLibrary { + name : "lib2" + destinationDirectory: "bin" + cpp.visibility: 'default' + Group { + files : [ "lib2.cpp" ] + } + Depends { name: "cpp" } + Depends { name: "lib3" } + bundle.isBundle: false + } + + DynamicLibrary { + name : "lib3" + destinationDirectory: "bin" + cpp.visibility: 'hidden' + Group { + files : [ "lib3.cpp" ] + } + Depends { name: "cpp" } + bundle.isBundle: false + } + + DynamicLibrary { + name : "lib4" + destinationDirectory: "bin" + cpp.visibility: 'hiddenInlines' + cpp.defines: "TEST_LIB" + Group { + files : [ "lib4.h", "lib4.cpp" ] + } + Depends { name: "cpp" } + bundle.isBundle: false + + Export { + Depends { name: "cpp" } + cpp.includePaths: [product.sourceDirectory] + } + } +} + diff --git a/tests/auto/api/testdata/dynamic-libs/main.cpp b/tests/auto/api/testdata/dynamic-libs/main.cpp new file mode 100644 index 00000000..20ea972a --- /dev/null +++ b/tests/auto/api/testdata/dynamic-libs/main.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#if defined(_WIN32) || defined(WIN32) +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT +# define IMPORT +#endif + +IMPORT int lib1_hello(); + +int main() +{ + puts("application says hello!"); + TestMe test; + test.hello1(); + test.hello2(); + return lib1_hello(); +} + diff --git a/tests/auto/api/testdata/empty-filetag-list/dontcompilethis.cpp b/tests/auto/api/testdata/empty-filetag-list/dontcompilethis.cpp new file mode 100644 index 00000000..bac3b631 --- /dev/null +++ b/tests/auto/api/testdata/empty-filetag-list/dontcompilethis.cpp @@ -0,0 +1 @@ +This is not C++. diff --git a/tests/auto/api/testdata/empty-filetag-list/project.qbs b/tests/auto/api/testdata/empty-filetag-list/project.qbs new file mode 100644 index 00000000..29461612 --- /dev/null +++ b/tests/auto/api/testdata/empty-filetag-list/project.qbs @@ -0,0 +1,8 @@ +import qbs + +CppApplication { + Group { + files: "dontcompilethis.cpp" + fileTags: [] + } +} diff --git a/tests/auto/api/testdata/empty-submodules-list/project.qbs b/tests/auto/api/testdata/empty-submodules-list/project.qbs new file mode 100644 index 00000000..88b5e317 --- /dev/null +++ b/tests/auto/api/testdata/empty-submodules-list/project.qbs @@ -0,0 +1,8 @@ +import qbs + +CppApplication { + Depends { + name: "dummy" + submodules: [] + } +} diff --git a/tests/auto/api/testdata/enable-and-disable-product/main.cpp b/tests/auto/api/testdata/enable-and-disable-product/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/api/testdata/enable-and-disable-product/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/api/testdata/enable-and-disable-product/project.qbs b/tests/auto/api/testdata/enable-and-disable-product/project.qbs new file mode 100644 index 00000000..4afa1e25 --- /dev/null +++ b/tests/auto/api/testdata/enable-and-disable-product/project.qbs @@ -0,0 +1,8 @@ +import qbs + +CppApplication { + property string prop: undefined // Influences source artifact properties and the product condition + condition: prop + cpp.visibility: prop + files: "main.cpp" +} diff --git a/tests/auto/api/testdata/error-in-setup-run-environment/error-in-setup-run-environment.qbs b/tests/auto/api/testdata/error-in-setup-run-environment/error-in-setup-run-environment.qbs new file mode 100644 index 00000000..f3247f6f --- /dev/null +++ b/tests/auto/api/testdata/error-in-setup-run-environment/error-in-setup-run-environment.qbs @@ -0,0 +1,5 @@ +import qbs + +CppApplication { + Depends { name: "mymodule" } +} diff --git a/tests/auto/api/testdata/error-in-setup-run-environment/modules/mymodule/mymodule.qbs b/tests/auto/api/testdata/error-in-setup-run-environment/modules/mymodule/mymodule.qbs new file mode 100644 index 00000000..e698b45b --- /dev/null +++ b/tests/auto/api/testdata/error-in-setup-run-environment/modules/mymodule/mymodule.qbs @@ -0,0 +1,7 @@ +import qbs + +Module { + setupRunEnvironment: { + trallala + } +} diff --git a/tests/auto/api/testdata/explicitly-depends-on/dependency.txt b/tests/auto/api/testdata/explicitly-depends-on/dependency.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/explicitly-depends-on/project.qbs b/tests/auto/api/testdata/explicitly-depends-on/project.qbs new file mode 100644 index 00000000..eda4e27d --- /dev/null +++ b/tests/auto/api/testdata/explicitly-depends-on/project.qbs @@ -0,0 +1,30 @@ +import qbs +import qbs.TextFile + +Product { + type: "mytype" + files: "dependency.txt" + FileTagger { + patterns: "*.txt" + fileTags: ["txt"] + } + Rule { + multiplex: true + explicitlyDependsOn: "txt" + Artifact { + filePath: "test.mytype" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating output artifact"; + cmd.highlight = "codegen"; + cmd.sourceCode = function() { + var file = new TextFile(output.filePath, TextFile.WriteOnly); + file.truncate(); + file.close(); + } + return cmd; + } + } +} diff --git a/tests/auto/api/testdata/export-item-with-group/main.cpp b/tests/auto/api/testdata/export-item-with-group/main.cpp new file mode 100644 index 00000000..e14f806b --- /dev/null +++ b/tests/auto/api/testdata/export-item-with-group/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { } diff --git a/tests/auto/api/testdata/export-item-with-group/project.qbs b/tests/auto/api/testdata/export-item-with-group/project.qbs new file mode 100644 index 00000000..2dffc280 --- /dev/null +++ b/tests/auto/api/testdata/export-item-with-group/project.qbs @@ -0,0 +1,17 @@ +import qbs + +Project { + Product { + name: "dep" + Export { + Depends { name: "cpp" } + Group { files: ["main.cpp"] } + } + } + + Application { + name: "app" + bundle.isBundle: false + Depends { name: "dep" } + } +} diff --git a/tests/auto/api/testdata/export-simple/lib1.cpp b/tests/auto/api/testdata/export-simple/lib1.cpp new file mode 100644 index 00000000..7ad4c27a --- /dev/null +++ b/tests/auto/api/testdata/export-simple/lib1.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#if defined(_WIN32) || defined(WIN32) +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT +# define IMPORT +#endif + +EXPORT int lib1_hello() +{ + puts("lib1 says hello!"); + return 0; +} diff --git a/tests/auto/api/testdata/export-simple/main.cpp b/tests/auto/api/testdata/export-simple/main.cpp new file mode 100644 index 00000000..bb905e53 --- /dev/null +++ b/tests/auto/api/testdata/export-simple/main.cpp @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +#if defined(_WIN32) || defined(WIN32) +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT +# define IMPORT +#endif + +IMPORT int lib1_hello(); + +int main() +{ + puts("application says hello!"); + return lib1_hello(); +} + diff --git a/tests/auto/api/testdata/export-simple/project.qbs b/tests/auto/api/testdata/export-simple/project.qbs new file mode 100644 index 00000000..6eca71e8 --- /dev/null +++ b/tests/auto/api/testdata/export-simple/project.qbs @@ -0,0 +1,50 @@ +import qbs 1.0 + +Project { + Application { + name : "HelloWorld" + destinationDirectory: "bin" + Group { + files : [ "main.cpp" ] + } + Depends { name: "cpp" } + Depends { name: 'dummy' } + } + + Product { + name: 'dummy' + Group { + files: 'main.cpp' + qbs.install: true + } + Export { + Depends { name: 'dummy2' } + Properties { // QBS-550 + condition: false + qbs.optimization: "ludicrous speed" + } + } + } + + Product { + name: 'dummy2' + Group { + files: 'lib1.cpp' + qbs.install: true + } + Export { + Depends { name: 'lib1' } + } + } + + DynamicLibrary { + name : "lib1" + destinationDirectory: "bin" + Group { + files : [ "lib1.cpp" ] + } + Depends { name: "cpp" } + bundle.isBundle: false + } +} + diff --git a/tests/auto/api/testdata/export-with-recursive-depends/main1.cpp b/tests/auto/api/testdata/export-with-recursive-depends/main1.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/api/testdata/export-with-recursive-depends/main1.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/api/testdata/export-with-recursive-depends/main2.cpp b/tests/auto/api/testdata/export-with-recursive-depends/main2.cpp new file mode 100644 index 00000000..6ea3da44 --- /dev/null +++ b/tests/auto/api/testdata/export-with-recursive-depends/main2.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() +{ +#ifndef HAS_FOO + blubb(); +#endif +} diff --git a/tests/auto/api/testdata/export-with-recursive-depends/modules/module1/module1.qbs b/tests/auto/api/testdata/export-with-recursive-depends/modules/module1/module1.qbs new file mode 100644 index 00000000..c1c4ad35 --- /dev/null +++ b/tests/auto/api/testdata/export-with-recursive-depends/modules/module1/module1.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + Depends { name: "module2" } +} diff --git a/tests/auto/api/testdata/export-with-recursive-depends/modules/module2/module2.qbs b/tests/auto/api/testdata/export-with-recursive-depends/modules/module2/module2.qbs new file mode 100644 index 00000000..75a27463 --- /dev/null +++ b/tests/auto/api/testdata/export-with-recursive-depends/modules/module2/module2.qbs @@ -0,0 +1,6 @@ +import qbs + +Module { + Depends { name: "cpp" } + cpp.defines: ["HAS_FOO"] +} diff --git a/tests/auto/api/testdata/export-with-recursive-depends/project.qbs b/tests/auto/api/testdata/export-with-recursive-depends/project.qbs new file mode 100644 index 00000000..d5f5f79e --- /dev/null +++ b/tests/auto/api/testdata/export-with-recursive-depends/project.qbs @@ -0,0 +1,15 @@ +import qbs + +Project { + CppApplication { + name: "app1" + files: "main1.cpp" + Export { Depends { name: "module1" } } + } + + CppApplication { + name: "app2" + Depends { name: "app1" } + files: "main2.cpp" + } +} diff --git a/tests/auto/api/testdata/file-tagger/bla.txt b/tests/auto/api/testdata/file-tagger/bla.txt new file mode 100644 index 00000000..26185684 --- /dev/null +++ b/tests/auto/api/testdata/file-tagger/bla.txt @@ -0,0 +1,15 @@ +#include + +class MyObject : public QObject +{ + Q_OBJECT +}; + +int main() +{ + MyObject obj; + return 0; +} + +#include "bla.moc" + diff --git a/tests/auto/api/testdata/file-tagger/moc_cpp.qbs b/tests/auto/api/testdata/file-tagger/moc_cpp.qbs new file mode 100644 index 00000000..fd07730d --- /dev/null +++ b/tests/auto/api/testdata/file-tagger/moc_cpp.qbs @@ -0,0 +1,43 @@ +import qbs 1.0 +import qbs.TextFile +import qbs.FileInfo + +Project { + Product { + type: "application" + consoleApplication: true + name: "moc_cpp" + + Depends { + name: "Qt.core" + } + + Group { + files: 'bla.txt' + fileTags: ['text'] + } + } + + Rule { + inputs: ['text'] + Artifact { + fileTags: ['cpp'] + filePath: input.baseName + '.cpp' + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.sourceCode = function () { + var file = new TextFile(input.filePath, TextFile.ReadOnly); + var text = file.readAll(); + file.close(); + file = new TextFile(output.filePath, TextFile.WriteOnly); + file.truncate(); + file.write(text); + file.close(); + } + cmd.description = 'generating ' + FileInfo.fileName(output.filePath); + cmd.highlight = 'codegen'; + return cmd; + } + } +} diff --git a/tests/auto/api/testdata/filetagsfilter_override/InstalledApp.qbs b/tests/auto/api/testdata/filetagsfilter_override/InstalledApp.qbs new file mode 100644 index 00000000..633410c3 --- /dev/null +++ b/tests/auto/api/testdata/filetagsfilter_override/InstalledApp.qbs @@ -0,0 +1,11 @@ +import qbs + +CppApplication { + type: "application" + consoleApplication: true + Group { + fileTagsFilter: product.type + qbs.install: true + qbs.installDir: "hurz" + } +} diff --git a/tests/auto/api/testdata/filetagsfilter_override/main.cpp b/tests/auto/api/testdata/filetagsfilter_override/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/api/testdata/filetagsfilter_override/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/api/testdata/filetagsfilter_override/project.qbs b/tests/auto/api/testdata/filetagsfilter_override/project.qbs new file mode 100644 index 00000000..a6cec7d0 --- /dev/null +++ b/tests/auto/api/testdata/filetagsfilter_override/project.qbs @@ -0,0 +1,11 @@ +import qbs +import "InstalledApp.qbs" as InstalledApp + +InstalledApp { + files: "main.cpp" + Group { + fileTagsFilter: product.type + qbs.install: true + qbs.installDir: "habicht" + } +} diff --git a/tests/auto/api/testdata/generated-files-list/generated-files-list.qbs b/tests/auto/api/testdata/generated-files-list/generated-files-list.qbs new file mode 100644 index 00000000..be6ad63e --- /dev/null +++ b/tests/auto/api/testdata/generated-files-list/generated-files-list.qbs @@ -0,0 +1,15 @@ +import qbs + +CppApplication { + Depends { name: "Qt.widgets" } + consoleApplication: true + cpp.debugInformation: false + cpp.separateDebugInformation: false + files: [ + "main.cpp", + "mainwindow.cpp", + "mainwindow.h", + "mainwindow.ui" + ] +} + diff --git a/tests/auto/api/testdata/generated-files-list/main.cpp b/tests/auto/api/testdata/generated-files-list/main.cpp new file mode 100644 index 00000000..9713be60 --- /dev/null +++ b/tests/auto/api/testdata/generated-files-list/main.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} diff --git a/tests/auto/api/testdata/generated-files-list/mainwindow.cpp b/tests/auto/api/testdata/generated-files-list/mainwindow.cpp new file mode 100644 index 00000000..92a5e0a7 --- /dev/null +++ b/tests/auto/api/testdata/generated-files-list/mainwindow.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mainwindow.h" +#include "ui_mainwindow.h" + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/tests/auto/api/testdata/generated-files-list/mainwindow.h b/tests/auto/api/testdata/generated-files-list/mainwindow.h new file mode 100644 index 00000000..5460460e --- /dev/null +++ b/tests/auto/api/testdata/generated-files-list/mainwindow.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private: + Ui::MainWindow *ui; +}; + +#endif // MAINWINDOW_H diff --git a/tests/auto/api/testdata/generated-files-list/mainwindow.ui b/tests/auto/api/testdata/generated-files-list/mainwindow.ui new file mode 100644 index 00000000..6050363f --- /dev/null +++ b/tests/auto/api/testdata/generated-files-list/mainwindow.ui @@ -0,0 +1,24 @@ + + MainWindow + + + + 0 + 0 + 400 + 300 + + + + MainWindow + + + + + + + + + + + diff --git a/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs b/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs new file mode 100644 index 00000000..592c602e --- /dev/null +++ b/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs @@ -0,0 +1,21 @@ +import qbs + +Product { + type: "mytype" + Rule { + multiplex: true + Artifact { + filePath: "output.txt" + fileTags: "mytype" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Running infinite loop"; + cmd.sourceCode = function() { + while (true) + ; + } + return cmd; + } + } +} diff --git a/tests/auto/api/testdata/infinite-loop-process/infinite-loop.qbs b/tests/auto/api/testdata/infinite-loop-process/infinite-loop.qbs new file mode 100644 index 00000000..d441f58a --- /dev/null +++ b/tests/auto/api/testdata/infinite-loop-process/infinite-loop.qbs @@ -0,0 +1,28 @@ +import qbs + +Project { + QtApplication { + type: "application" + consoleApplication: true // suppress bundle generation + files: "main.cpp" + name: "infinite-loop" + } + + Product { + type: "mytype" + name: "caller" + Depends { name: "infinite-loop" } + Rule { + inputsFromDependencies: "application" + Artifact { + filePath: "dummy" + fileTags: "mytype" + } + prepare: { + var cmd = new Command(inputs["application"][0].filePath); + cmd.description = "Calling application that runs forever"; + return cmd; + } + } + } +} diff --git a/tests/auto/api/testdata/infinite-loop-process/main.cpp b/tests/auto/api/testdata/infinite-loop-process/main.cpp new file mode 100644 index 00000000..e767e5cf --- /dev/null +++ b/tests/auto/api/testdata/infinite-loop-process/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class MyThread : public QThread +{ +public: + static void mySleep(unsigned long secs) { sleep(secs); } // sleep() is protected in Qt 4. +}; + +int main() +{ + MyThread::mySleep(60); +} diff --git a/tests/auto/api/testdata/infinite-loop-resolving/project.qbs b/tests/auto/api/testdata/infinite-loop-resolving/project.qbs new file mode 100644 index 00000000..b690f504 --- /dev/null +++ b/tests/auto/api/testdata/infinite-loop-resolving/project.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + type: { while (true); return "Haha!"; } +} diff --git a/tests/auto/api/testdata/inherit-qbs-search-paths/imports/Foo.qbs b/tests/auto/api/testdata/inherit-qbs-search-paths/imports/Foo.qbs new file mode 100644 index 00000000..9493b04b --- /dev/null +++ b/tests/auto/api/testdata/inherit-qbs-search-paths/imports/Foo.qbs @@ -0,0 +1,8 @@ +import qbs 1.0 + +Product { + type: "application" + consoleApplication: true + Depends { name: 'bli' } +} + diff --git a/tests/auto/api/testdata/inherit-qbs-search-paths/main.cpp b/tests/auto/api/testdata/inherit-qbs-search-paths/main.cpp new file mode 100644 index 00000000..98e6c8c3 --- /dev/null +++ b/tests/auto/api/testdata/inherit-qbs-search-paths/main.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HAVE_BLI +#error HAVE_BLI missing! +#endif + +int main() +{ + return 0; +} + diff --git a/tests/auto/api/testdata/inherit-qbs-search-paths/prj.qbs b/tests/auto/api/testdata/inherit-qbs-search-paths/prj.qbs new file mode 100644 index 00000000..0e816280 --- /dev/null +++ b/tests/auto/api/testdata/inherit-qbs-search-paths/prj.qbs @@ -0,0 +1,13 @@ +import qbs 1.0 +import "imports/Foo.qbs" as Foo + +Project { + qbsSearchPaths: "subdir" + Project { + qbsSearchPaths: "subdir2" + Foo { + files: "main.cpp" + } + } +} + diff --git a/tests/auto/api/testdata/inherit-qbs-search-paths/subdir/modules/bli/m.qbs b/tests/auto/api/testdata/inherit-qbs-search-paths/subdir/modules/bli/m.qbs new file mode 100644 index 00000000..b4b93678 --- /dev/null +++ b/tests/auto/api/testdata/inherit-qbs-search-paths/subdir/modules/bli/m.qbs @@ -0,0 +1,7 @@ +import qbs 1.0 + +Module { + Depends {name : "cpp" } + cpp.defines: ["HAVE_BLI"] +} + diff --git a/tests/auto/api/testdata/inherit-qbs-search-paths/subdir2/modules/bla/m.qbs b/tests/auto/api/testdata/inherit-qbs-search-paths/subdir2/modules/bla/m.qbs new file mode 100644 index 00000000..0676d0c4 --- /dev/null +++ b/tests/auto/api/testdata/inherit-qbs-search-paths/subdir2/modules/bla/m.qbs @@ -0,0 +1,7 @@ +import qbs 1.0 + +Module { + Depends {name : "cpp" } + cpp.defines: ["HAVE_BLA"] +} + diff --git a/tests/auto/api/testdata/installed-artifact/installed_artifact.qbs b/tests/auto/api/testdata/installed-artifact/installed_artifact.qbs new file mode 100644 index 00000000..7f7de9bc --- /dev/null +++ b/tests/auto/api/testdata/installed-artifact/installed_artifact.qbs @@ -0,0 +1,27 @@ +import qbs 1.0 + + +Project { + CppApplication { + name: "other app" + files: ["main.cpp"] + } + + CppApplication { + name: "installedApp" + type: "application" + consoleApplication: true + Depends { name: "other app" } + Group { + files: "main.cpp" + qbs.install: true + qbs.installDir: "src" + } + qbs.installPrefix: "/usr" + Group { + fileTagsFilter: "application" + qbs.install: true + qbs.installDir: "bin" + } + } +} diff --git a/tests/auto/api/testdata/installed-artifact/main.cpp b/tests/auto/api/testdata/installed-artifact/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/api/testdata/installed-artifact/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/api/testdata/is-runnable/project.qbs b/tests/auto/api/testdata/is-runnable/project.qbs new file mode 100644 index 00000000..47e217a6 --- /dev/null +++ b/tests/auto/api/testdata/is-runnable/project.qbs @@ -0,0 +1,11 @@ +import qbs + +Project { + CppApplication { + name: "app" + } + DynamicLibrary { + name: "lib" + bundle.isBundle: false + } +} diff --git a/tests/auto/api/testdata/lib-same-source/main.cpp b/tests/auto/api/testdata/lib-same-source/main.cpp new file mode 100644 index 00000000..47e1013f --- /dev/null +++ b/tests/auto/api/testdata/lib-same-source/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + puts("Hello WOrld!"); +} diff --git a/tests/auto/api/testdata/lib-same-source/project.qbs b/tests/auto/api/testdata/lib-same-source/project.qbs new file mode 100644 index 00000000..dc5785e9 --- /dev/null +++ b/tests/auto/api/testdata/lib-same-source/project.qbs @@ -0,0 +1,23 @@ +import qbs 1.0 + +Project { + Product { + type: "application" + consoleApplication: true + name : "HelloWorldApp" + Depends { name: 'cpp' } + Group { + files : [ "main.cpp" ] + } + } + + Product { + type: "staticlibrary" + name : "HelloWorldLib" + Depends { name: 'cpp' } + Group { + files : [ "main.cpp" ] + } + } +} + diff --git a/tests/auto/api/testdata/link-static-lib/helper1/helper1.cpp b/tests/auto/api/testdata/link-static-lib/helper1/helper1.cpp new file mode 100644 index 00000000..178cdbf1 --- /dev/null +++ b/tests/auto/api/testdata/link-static-lib/helper1/helper1.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "helper1.h" +#include + +int getSomeNumber() +{ + return 12 * getOddNumber(); +} + diff --git a/tests/auto/api/testdata/link-static-lib/helper1/helper1.h b/tests/auto/api/testdata/link-static-lib/helper1/helper1.h new file mode 100644 index 00000000..eec93c85 --- /dev/null +++ b/tests/auto/api/testdata/link-static-lib/helper1/helper1.h @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef HELPER1_H +#define HELPER1_H + +extern int getSomeNumber(); + +#endif + diff --git a/tests/auto/api/testdata/link-static-lib/helper2/helper2.cpp b/tests/auto/api/testdata/link-static-lib/helper2/helper2.cpp new file mode 100644 index 00000000..b1ddfa9e --- /dev/null +++ b/tests/auto/api/testdata/link-static-lib/helper2/helper2.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "helper2.h" + +int getOddNumber() +{ + return 13; +} + diff --git a/tests/auto/api/testdata/link-static-lib/helper2/helper2.h b/tests/auto/api/testdata/link-static-lib/helper2/helper2.h new file mode 100644 index 00000000..e246033c --- /dev/null +++ b/tests/auto/api/testdata/link-static-lib/helper2/helper2.h @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef HELPER2_H +#define HELPER2_H + +extern int getOddNumber(); + +#endif + diff --git a/tests/auto/api/testdata/link-static-lib/main.cpp b/tests/auto/api/testdata/link-static-lib/main.cpp new file mode 100644 index 00000000..5f6aed0b --- /dev/null +++ b/tests/auto/api/testdata/link-static-lib/main.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int bla(); + +int main() +{ + return bla(); +} + diff --git a/tests/auto/api/testdata/link-static-lib/mystaticlib.cpp b/tests/auto/api/testdata/link-static-lib/mystaticlib.cpp new file mode 100644 index 00000000..71777ef0 --- /dev/null +++ b/tests/auto/api/testdata/link-static-lib/mystaticlib.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int bla() +{ + int n = getSomeNumber(); + printf("Hello World! The magic number is %d.", n); + return n; +} diff --git a/tests/auto/api/testdata/link-static-lib/mystaticlibhelper.cpp b/tests/auto/api/testdata/link-static-lib/mystaticlibhelper.cpp new file mode 100644 index 00000000..96ccb1b4 --- /dev/null +++ b/tests/auto/api/testdata/link-static-lib/mystaticlibhelper.cpp @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int helper_function() +{ + return 156; +} + diff --git a/tests/auto/api/testdata/link-static-lib/project.qbs b/tests/auto/api/testdata/link-static-lib/project.qbs new file mode 100644 index 00000000..6840a4cc --- /dev/null +++ b/tests/auto/api/testdata/link-static-lib/project.qbs @@ -0,0 +1,47 @@ +import qbs 1.0 + +Project { + Product { + type: "application" + consoleApplication: true + name: "HelloWorld" + files : [ "main.cpp" ] + Depends { name: "cpp" } + Depends { name: "mystaticlib" } + } + + StaticLibrary { + name : "mystaticlib" + files : [ "mystaticlib.cpp" ] + Depends { name: "cpp" } + Depends { name: "helper1" } + } + + StaticLibrary { + name : "helper1" + files : [ + "helper1/helper1.h", + "helper1/helper1.cpp" + ] + Depends { name: "cpp" } + Depends { name: "helper2" } + Export { + Depends { name: "cpp" } + cpp.includePaths: [product.sourceDirectory + '/helper1'] + } + } + + StaticLibrary { + name : "helper2" + files : [ + "helper2/helper2.h", + "helper2/helper2.cpp" + ] + Depends { name: "cpp" } + Export { + Depends { name: "cpp" } + cpp.includePaths: [product.sourceDirectory + '/helper2'] + } + } +} + diff --git a/tests/auto/api/testdata/lots-of-dots/dotty.matrix.ui b/tests/auto/api/testdata/lots-of-dots/dotty.matrix.ui new file mode 100644 index 00000000..4b7d6a45 --- /dev/null +++ b/tests/auto/api/testdata/lots-of-dots/dotty.matrix.ui @@ -0,0 +1,21 @@ + + + + + Form + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + diff --git a/tests/auto/api/testdata/lots-of-dots/m.a.i.n.cpp b/tests/auto/api/testdata/lots-of-dots/m.a.i.n.cpp new file mode 100644 index 00000000..427f9907 --- /dev/null +++ b/tests/auto/api/testdata/lots-of-dots/m.a.i.n.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "object.narf.h" +#include +#include + +int main() +{ + ObjectNarf obj; + puts("..."); +} + diff --git a/tests/auto/api/testdata/lots-of-dots/object.narf.cpp b/tests/auto/api/testdata/lots-of-dots/object.narf.cpp new file mode 100644 index 00000000..3b09b744 --- /dev/null +++ b/tests/auto/api/testdata/lots-of-dots/object.narf.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "object.narf.h" +#include + +ObjectNarf::ObjectNarf(QObject *parent) + : QObject(parent) +{} + diff --git a/tests/auto/api/testdata/lots-of-dots/object.narf.h b/tests/auto/api/testdata/lots-of-dots/object.narf.h new file mode 100644 index 00000000..2fdebcd3 --- /dev/null +++ b/tests/auto/api/testdata/lots-of-dots/object.narf.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECT_H +#define OBJECT_H +#include + +class ObjectNarf : public QObject +{ + Q_OBJECT +public: + ObjectNarf(QObject *parent = 0); +}; + +#endif + diff --git a/tests/auto/api/testdata/lots-of-dots/polka.dots.qrc b/tests/auto/api/testdata/lots-of-dots/polka.dots.qrc new file mode 100644 index 00000000..815b1100 --- /dev/null +++ b/tests/auto/api/testdata/lots-of-dots/polka.dots.qrc @@ -0,0 +1,5 @@ + + + m.a.i.n.cpp + + diff --git a/tests/auto/api/testdata/lots-of-dots/project.qbs b/tests/auto/api/testdata/lots-of-dots/project.qbs new file mode 100644 index 00000000..5e033bd3 --- /dev/null +++ b/tests/auto/api/testdata/lots-of-dots/project.qbs @@ -0,0 +1,18 @@ +import qbs 1.0 + +Project { + QtGuiApplication { + type: "application" + consoleApplication: true + name: "lots.of.dots" + + files : [ + "m.a.i.n.cpp", + "object.narf.h", + "object.narf.cpp", + "polka.dots.qrc", + "dotty.matrix.ui" + ] + } +} + diff --git a/tests/auto/api/testdata/missing-qobject-header/main.cpp b/tests/auto/api/testdata/missing-qobject-header/main.cpp new file mode 100644 index 00000000..5e371580 --- /dev/null +++ b/tests/auto/api/testdata/missing-qobject-header/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "myobject.h" + +int main() +{ + MyObject().func(); +} diff --git a/tests/auto/api/testdata/missing-qobject-header/myobject.cpp b/tests/auto/api/testdata/missing-qobject-header/myobject.cpp new file mode 100644 index 00000000..5fc1d328 --- /dev/null +++ b/tests/auto/api/testdata/missing-qobject-header/myobject.cpp @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "myobject.h" + +void MyObject::func() +{ +} diff --git a/tests/auto/api/testdata/missing-qobject-header/myobject.h b/tests/auto/api/testdata/missing-qobject-header/myobject.h new file mode 100644 index 00000000..a2a97db9 --- /dev/null +++ b/tests/auto/api/testdata/missing-qobject-header/myobject.h @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class MyObject : public QObject +{ + Q_OBJECT + +public: + void func(); + +}; diff --git a/tests/auto/api/testdata/moc-cpp/bla.cpp b/tests/auto/api/testdata/moc-cpp/bla.cpp new file mode 100644 index 00000000..2b73d97e --- /dev/null +++ b/tests/auto/api/testdata/moc-cpp/bla.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class MyObject : public QObject +{ + Q_OBJECT +}; + +int main() +{ + MyObject obj; + return 0; +} + +#include "bla.moc" + diff --git a/tests/auto/api/testdata/moc-cpp/project.qbs b/tests/auto/api/testdata/moc-cpp/project.qbs new file mode 100644 index 00000000..f8ee0736 --- /dev/null +++ b/tests/auto/api/testdata/moc-cpp/project.qbs @@ -0,0 +1,15 @@ +import qbs 1.0 + +Project { + Product { + type: "application" + consoleApplication: true + name: "moc_cpp" + + Depends { + name: "Qt.core" + } + + files: ["bla.cpp"] + } +} diff --git a/tests/auto/api/testdata/moc-hpp-included/object.cpp b/tests/auto/api/testdata/moc-hpp-included/object.cpp new file mode 100644 index 00000000..4f1502af --- /dev/null +++ b/tests/auto/api/testdata/moc-hpp-included/object.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "object.h" + +Object::Object(QObject *parent) + : QObject(parent) +{} + +#include "moc_object.cpp" +#include + +int main() +{ + Object obj; + printf("Hello World\n"); +} + diff --git a/tests/auto/api/testdata/moc-hpp-included/object.h b/tests/auto/api/testdata/moc-hpp-included/object.h new file mode 100644 index 00000000..95292c11 --- /dev/null +++ b/tests/auto/api/testdata/moc-hpp-included/object.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECT_H +#define OBJECT_H +#include + +class Object : public QObject +{ + Q_OBJECT +public: + Object(QObject *parent = 0); +}; + +#endif + diff --git a/tests/auto/api/testdata/moc-hpp-included/object2.h b/tests/auto/api/testdata/moc-hpp-included/object2.h new file mode 100644 index 00000000..e0fca966 --- /dev/null +++ b/tests/auto/api/testdata/moc-hpp-included/object2.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECT2_H +#define OBJECT2_H +#include + +class Object2 : public QObject +{ + Q_OBJECT +public: + Object2(QObject *parent = 0); +}; + +#endif + diff --git a/tests/auto/api/testdata/moc-hpp-included/object2.mm b/tests/auto/api/testdata/moc-hpp-included/object2.mm new file mode 100644 index 00000000..6fd95d31 --- /dev/null +++ b/tests/auto/api/testdata/moc-hpp-included/object2.mm @@ -0,0 +1,16 @@ +#include "object2.h" + +Object2::Object2(QObject *parent) + : QObject(parent) +{} + +#include "moc_object2.cpp" +#include + +int main2() +{ + Object2 obj; + printf("Hello World\n"); + return 0; +} + diff --git a/tests/auto/api/testdata/moc-hpp-included/project.qbs b/tests/auto/api/testdata/moc-hpp-included/project.qbs new file mode 100644 index 00000000..554b0a2b --- /dev/null +++ b/tests/auto/api/testdata/moc-hpp-included/project.qbs @@ -0,0 +1,19 @@ +import qbs 1.0 + +Project { + Product { + type: "application" + consoleApplication: true + name: "moc_hpp_included" + + Depends { name: "Qt.core" } + + files: ["object.cpp", "object.h"] + + Group { + condition: qbs.targetOS.contains("darwin") + files: ["object2.mm", "object2.h"] + } + } +} + diff --git a/tests/auto/api/testdata/moc-hpp/object.cpp b/tests/auto/api/testdata/moc-hpp/object.cpp new file mode 100644 index 00000000..601893c3 --- /dev/null +++ b/tests/auto/api/testdata/moc-hpp/object.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "object.h" +#include + +Object::Object(QObject *parent) + : QObject(parent) +{} + +int main() +{ + Object obj; + printf("Hello World\n"); +} + diff --git a/tests/auto/api/testdata/moc-hpp/object.h b/tests/auto/api/testdata/moc-hpp/object.h new file mode 100644 index 00000000..95292c11 --- /dev/null +++ b/tests/auto/api/testdata/moc-hpp/object.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef OBJECT_H +#define OBJECT_H +#include + +class Object : public QObject +{ + Q_OBJECT +public: + Object(QObject *parent = 0); +}; + +#endif + diff --git a/tests/auto/api/testdata/moc-hpp/project.qbs b/tests/auto/api/testdata/moc-hpp/project.qbs new file mode 100644 index 00000000..54451a50 --- /dev/null +++ b/tests/auto/api/testdata/moc-hpp/project.qbs @@ -0,0 +1,17 @@ +import qbs 1.0 + +Project { + Product { + type: "application" + consoleApplication: true + name: "moc_hpp" + + Depends { name: "Qt.core" } + + files : [ + "object.h", + "object.cpp" + ] + } +} + diff --git a/tests/auto/api/testdata/multi-arch/host+target.input b/tests/auto/api/testdata/multi-arch/host+target.input new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/multi-arch/host-tool.input b/tests/auto/api/testdata/multi-arch/host-tool.input new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/multi-arch/project.qbs b/tests/auto/api/testdata/multi-arch/project.qbs new file mode 100644 index 00000000..d88c84ba --- /dev/null +++ b/tests/auto/api/testdata/multi-arch/project.qbs @@ -0,0 +1,54 @@ +import qbs +import qbs.FileInfo +import qbs.TextFile + +Project { + property string hostProfile + property string targetProfile + Product { + name: "p1" + type: "output" + profiles: [project.targetProfile, project.hostProfile] + Group { + files: "host+target.input" + fileTags: "input" + } + Group { + fileTagsFilter: "output" + qbs.install: true + qbs.installDir: profile + } + } + Product { + name: "p2" + type: "output" + profiles: project.hostProfile + Group { + files: "host-tool.input" + fileTags: "input" + } + Group { + fileTagsFilter: "output" + qbs.install: true + qbs.installDir: profile + } + } + + Rule { + inputs: "input" + Artifact { + filePath: FileInfo.baseName(input.fileName) + ".output" + fileTags: "output" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating " + output.fileName; + cmd.sourceCode = function() { + var file = new TextFile(output.filePath, TextFile.WriteOnly); + file.write(product.moduleProperty("qbs", "architecture")); + file.close(); + } + return cmd; + } + } +} diff --git a/tests/auto/api/testdata/new-output-artifact-in-dependency/lib.cpp b/tests/auto/api/testdata/new-output-artifact-in-dependency/lib.cpp new file mode 100644 index 00000000..7e43bdcc --- /dev/null +++ b/tests/auto/api/testdata/new-output-artifact-in-dependency/lib.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +Q_DECL_EXPORT void f() {} diff --git a/tests/auto/api/testdata/new-output-artifact-in-dependency/main.cpp b/tests/auto/api/testdata/new-output-artifact-in-dependency/main.cpp new file mode 100644 index 00000000..1d53a1b2 --- /dev/null +++ b/tests/auto/api/testdata/new-output-artifact-in-dependency/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//void f(); + +int main() +{ +// f(); +} diff --git a/tests/auto/api/testdata/new-output-artifact-in-dependency/project.qbs b/tests/auto/api/testdata/new-output-artifact-in-dependency/project.qbs new file mode 100644 index 00000000..ed08e831 --- /dev/null +++ b/tests/auto/api/testdata/new-output-artifact-in-dependency/project.qbs @@ -0,0 +1,17 @@ +import qbs + +Project { + DynamicLibrary { + //Depends { name: "cpp" } + //Depends { name: "Qt.core" } + name: "lib" + files: "lib.cpp" + bundle.isBundle: false + } + + CppApplication { + name: "app" + files: "main.cpp" + Depends { name: "lib" } + } +} diff --git a/tests/auto/api/testdata/new-pattern-match/project.qbs b/tests/auto/api/testdata/new-pattern-match/project.qbs new file mode 100644 index 00000000..18975da7 --- /dev/null +++ b/tests/auto/api/testdata/new-pattern-match/project.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + files: "*.txt" +} diff --git a/tests/auto/api/testdata/nonexistingprojectproperties/invalidaccessfromproduct.qbs b/tests/auto/api/testdata/nonexistingprojectproperties/invalidaccessfromproduct.qbs new file mode 100644 index 00000000..c024b3c2 --- /dev/null +++ b/tests/auto/api/testdata/nonexistingprojectproperties/invalidaccessfromproduct.qbs @@ -0,0 +1,3 @@ +import qbs + +Project { Product { type: project.blubb } } diff --git a/tests/auto/api/testdata/nonexistingprojectproperties/project.qbs b/tests/auto/api/testdata/nonexistingprojectproperties/project.qbs new file mode 100644 index 00000000..7d453a67 --- /dev/null +++ b/tests/auto/api/testdata/nonexistingprojectproperties/project.qbs @@ -0,0 +1,3 @@ +import qbs + +Project { } diff --git a/tests/auto/api/testdata/objc/main.mm b/tests/auto/api/testdata/objc/main.mm new file mode 100644 index 00000000..90cf06bf --- /dev/null +++ b/tests/auto/api/testdata/objc/main.mm @@ -0,0 +1,14 @@ +#import +#include + +int main(int argc, char **argv) +{ + // We support both C++ + QCoreApplication app(argc, argv); + Q_UNUSED(app); + // And Objective-C + NSDictionary *version = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; + NSString *productVersion = [version objectForKey:@"ProductVersion"]; + NSLog(@"Hello, macOS %@!", productVersion); + // So it's Objective-C++ +} diff --git a/tests/auto/api/testdata/objc/objc.qbs b/tests/auto/api/testdata/objc/objc.qbs new file mode 100644 index 00000000..0fa9eb62 --- /dev/null +++ b/tests/auto/api/testdata/objc/objc.qbs @@ -0,0 +1,9 @@ +import qbs 1.0 + +Project { + QtApplication { + condition: qbs.targetOS.contains("macos") + files: "main.mm" + cpp.frameworks: [ "Foundation" ] + } +} diff --git a/tests/auto/api/testdata/precompiled-header-dynamic/autogen.h.in b/tests/auto/api/testdata/precompiled-header-dynamic/autogen.h.in new file mode 100644 index 00000000..c5c182c8 --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header-dynamic/autogen.h.in @@ -0,0 +1 @@ +inline void f() { } diff --git a/tests/auto/api/testdata/precompiled-header-dynamic/main.cpp b/tests/auto/api/testdata/precompiled-header-dynamic/main.cpp new file mode 100644 index 00000000..45aace45 --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header-dynamic/main.cpp @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() +{ + f(); +} diff --git a/tests/auto/api/testdata/precompiled-header-dynamic/pch.h b/tests/auto/api/testdata/precompiled-header-dynamic/pch.h new file mode 100644 index 00000000..8e7c7e5e --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header-dynamic/pch.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "autogen.h" diff --git a/tests/auto/api/testdata/precompiled-header-dynamic/project.qbs b/tests/auto/api/testdata/precompiled-header-dynamic/project.qbs new file mode 100644 index 00000000..3e1e676b --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header-dynamic/project.qbs @@ -0,0 +1,31 @@ +import qbs +import qbs.File + +CppApplication { + name: "MyApp" + consoleApplication: true + cpp.includePaths: [product.buildDirectory] + cpp.useCxxPrecompiledHeader: true + Group { + files: ["pch.h"] + fileTags: ["cpp_pch_src"] + } + Group { + files: ["autogen.h.in"] + fileTags: ["hpp.in"] + } + files: ["main.cpp"] + Rule { + inputs: ["hpp.in"] + Artifact { + filePath: "autogen.h" + fileTags: ["hpp"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Generating " + output.fileName; + cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); } + return [cmd]; + } + } +} diff --git a/tests/auto/api/testdata/precompiled-header-new/main.cpp b/tests/auto/api/testdata/precompiled-header-new/main.cpp new file mode 100644 index 00000000..b719b42e --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header-new/main.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "myobject.h" +#include +#include +#include + +using namespace std; + +int main() +{ + MyObject obj; + list lst; + lst.push_back(1); + lst.push_back(2); + lst.push_back(3); + lst.push_back(4); + lst.push_back(5); + lst.push_back(6); + lst.push_back(7); + lst.push_back(8); + lst.push_back(9); + reverse(lst.begin(), lst.end()); + for (list::iterator it=lst.begin(); it != lst.end(); ++it) + cout << *it << ", "; + cout << endl; + return 0; +} + diff --git a/tests/auto/api/testdata/precompiled-header-new/myobject.cpp b/tests/auto/api/testdata/precompiled-header-new/myobject.cpp new file mode 100644 index 00000000..018b91e9 --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header-new/myobject.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "myobject.h" + +MyObject::MyObject() +{ + std::cout << "MyObject::MyObject()\n"; +} + +MyObject::~MyObject() +{ + std::cout << "MyObject::~MyObject()" << std::endl; +} + diff --git a/tests/auto/api/testdata/precompiled-header-new/myobject.h b/tests/auto/api/testdata/precompiled-header-new/myobject.h new file mode 100644 index 00000000..a8453600 --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header-new/myobject.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MYOBJECT_H +#define MYOBJECT_H + +#include + +class MyObject : public QObject +{ + Q_OBJECT +public: + MyObject(); + ~MyObject(); +}; + +#endif + diff --git a/tests/auto/api/testdata/precompiled-header-new/project.qbs b/tests/auto/api/testdata/precompiled-header-new/project.qbs new file mode 100644 index 00000000..5c73c967 --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header-new/project.qbs @@ -0,0 +1,13 @@ +import qbs 1.0 + +QtApplication { + name: "MyApp" + consoleApplication: true + cpp.useCxxPrecompiledHeader: true + Group { + name: "precompiled headers" + files: ["stable.h"] + fileTags: ["cpp_pch_src"] + } + files: ["myobject.h", "main.cpp", "myobject.cpp"] +} diff --git a/tests/auto/api/testdata/precompiled-header-new/stable.h b/tests/auto/api/testdata/precompiled-header-new/stable.h new file mode 100644 index 00000000..d9a6222d --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header-new/stable.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* Add C includes here */ + +#if defined __cplusplus +/* Add C++ includes here */ + +# include +# include +#endif + diff --git a/tests/auto/api/testdata/precompiled-header/main.cpp b/tests/auto/api/testdata/precompiled-header/main.cpp new file mode 100644 index 00000000..b719b42e --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header/main.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "myobject.h" +#include +#include +#include + +using namespace std; + +int main() +{ + MyObject obj; + list lst; + lst.push_back(1); + lst.push_back(2); + lst.push_back(3); + lst.push_back(4); + lst.push_back(5); + lst.push_back(6); + lst.push_back(7); + lst.push_back(8); + lst.push_back(9); + reverse(lst.begin(), lst.end()); + for (list::iterator it=lst.begin(); it != lst.end(); ++it) + cout << *it << ", "; + cout << endl; + return 0; +} + diff --git a/tests/auto/api/testdata/precompiled-header/myobject.cpp b/tests/auto/api/testdata/precompiled-header/myobject.cpp new file mode 100644 index 00000000..018b91e9 --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header/myobject.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "myobject.h" + +MyObject::MyObject() +{ + std::cout << "MyObject::MyObject()\n"; +} + +MyObject::~MyObject() +{ + std::cout << "MyObject::~MyObject()" << std::endl; +} + diff --git a/tests/auto/api/testdata/precompiled-header/myobject.h b/tests/auto/api/testdata/precompiled-header/myobject.h new file mode 100644 index 00000000..a8453600 --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header/myobject.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MYOBJECT_H +#define MYOBJECT_H + +#include + +class MyObject : public QObject +{ + Q_OBJECT +public: + MyObject(); + ~MyObject(); +}; + +#endif + diff --git a/tests/auto/api/testdata/precompiled-header/project.qbs b/tests/auto/api/testdata/precompiled-header/project.qbs new file mode 100644 index 00000000..5f7b4b61 --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header/project.qbs @@ -0,0 +1,15 @@ +import qbs 1.0 + +Product { + type: "application" + consoleApplication: true + name: "MyApp" + files: ["stable.h", + "myobject.h", + "main.cpp", + "myobject.cpp"] + Depends { name: "cpp" } + cpp.precompiledHeader: "stable.h" + Depends { name: "Qt.core" } +} + diff --git a/tests/auto/api/testdata/precompiled-header/stable.h b/tests/auto/api/testdata/precompiled-header/stable.h new file mode 100644 index 00000000..d9a6222d --- /dev/null +++ b/tests/auto/api/testdata/precompiled-header/stable.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* Add C includes here */ + +#if defined __cplusplus +/* Add C++ includes here */ + +# include +# include +#endif + diff --git a/tests/auto/api/testdata/process-result/main.cpp b/tests/auto/api/testdata/process-result/main.cpp new file mode 100644 index 00000000..7b29c120 --- /dev/null +++ b/tests/auto/api/testdata/process-result/main.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int main(int argc, char *argv[]) +{ + std::cout << "stdout"; + std::cerr << "stderr"; + return atoi(argv[1]); +} diff --git a/tests/auto/api/testdata/process-result/process-result.qbs b/tests/auto/api/testdata/process-result/process-result.qbs new file mode 100644 index 00000000..ee1ded76 --- /dev/null +++ b/tests/auto/api/testdata/process-result/process-result.qbs @@ -0,0 +1,33 @@ +import qbs + +Project { + CppApplication { + name: "app" + files: ["main.cpp"] + } + Product { + name: "app-caller" + type: "mytype" + Depends { name: "app" } + property bool redirectStdout + property bool redirectStderr + property int argument + Rule { + inputsFromDependencies: ["application"] + Artifact { + filePath: "dummy" + fileTags: product.type + alwaysUpdated: false + } + prepare: { + var cmd = new Command(inputs["application"][0].filePath, [product.argument]); + if (product.redirectStdout) + cmd.stdoutFilePath = product.buildDirectory + "/stdout.txt"; + if (product.redirectStderr) + cmd.stderrFilePath = product.buildDirectory + "/stderr.txt"; + cmd.description = "Building app-caller"; + return [cmd]; + } + } + } +} diff --git a/tests/auto/api/testdata/productNameWithDots/app.cpp b/tests/auto/api/testdata/productNameWithDots/app.cpp new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/api/testdata/productNameWithDots/app.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/api/testdata/productNameWithDots/lib.cpp b/tests/auto/api/testdata/productNameWithDots/lib.cpp new file mode 100644 index 00000000..a28368d9 --- /dev/null +++ b/tests/auto/api/testdata/productNameWithDots/lib.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void foo() {} diff --git a/tests/auto/api/testdata/productNameWithDots/project.qbs b/tests/auto/api/testdata/productNameWithDots/project.qbs new file mode 100644 index 00000000..f0cf5813 --- /dev/null +++ b/tests/auto/api/testdata/productNameWithDots/project.qbs @@ -0,0 +1,17 @@ +import qbs +Project { + Product { + Depends { name: "cpp" } + Depends { name: "bundle" } + type: ["application"] + name: "myapp" + Depends { name: "foo.bar.bla" } + files: ["app.cpp"] + bundle.isBundle: false + } + StaticLibrary { + Depends { name: "cpp" } + name: "foo.bar.bla" + files: ["lib.cpp"] + } +} diff --git a/tests/auto/api/testdata/project-data-after-product-invalidation/file.cpp b/tests/auto/api/testdata/project-data-after-product-invalidation/file.cpp new file mode 100644 index 00000000..833057ed --- /dev/null +++ b/tests/auto/api/testdata/project-data-after-product-invalidation/file.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f() { } diff --git a/tests/auto/api/testdata/project-data-after-product-invalidation/main.cpp b/tests/auto/api/testdata/project-data-after-product-invalidation/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/api/testdata/project-data-after-product-invalidation/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/api/testdata/project-data-after-product-invalidation/project-data-after-product-invalidation.qbs b/tests/auto/api/testdata/project-data-after-product-invalidation/project-data-after-product-invalidation.qbs new file mode 100644 index 00000000..be68cac4 --- /dev/null +++ b/tests/auto/api/testdata/project-data-after-product-invalidation/project-data-after-product-invalidation.qbs @@ -0,0 +1,9 @@ +import qbs + +CppApplication { + name: "theProduct" + files: [ + "file.cpp", + "main.cpp", + ] +} diff --git a/tests/auto/api/testdata/project-editing/existingfile1.txt b/tests/auto/api/testdata/project-editing/existingfile1.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/project-editing/existingfile2.txt b/tests/auto/api/testdata/project-editing/existingfile2.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/project-editing/existingfile3.txt b/tests/auto/api/testdata/project-editing/existingfile3.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/project-editing/file.cpp b/tests/auto/api/testdata/project-editing/file.cpp new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/api/testdata/project-editing/file.cpp @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/api/testdata/project-editing/file.h b/tests/auto/api/testdata/project-editing/file.h new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/api/testdata/project-editing/file.h @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/api/testdata/project-editing/main.cpp b/tests/auto/api/testdata/project-editing/main.cpp new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/api/testdata/project-editing/main.cpp @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/api/testdata/project-editing/newfile1.txt b/tests/auto/api/testdata/project-editing/newfile1.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/project-editing/newfile2.txt b/tests/auto/api/testdata/project-editing/newfile2.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/project-editing/newfile3.txt b/tests/auto/api/testdata/project-editing/newfile3.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/project-editing/newfile4.txt b/tests/auto/api/testdata/project-editing/newfile4.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/project-editing/project-with-no-files.qbs b/tests/auto/api/testdata/project-editing/project-with-no-files.qbs new file mode 100644 index 00000000..824ae6dc --- /dev/null +++ b/tests/auto/api/testdata/project-editing/project-with-no-files.qbs @@ -0,0 +1,7 @@ +import qbs + +CppApplication { + Group { + files: "file.cpp" + } +} diff --git a/tests/auto/api/testdata/project-editing/project.qbs b/tests/auto/api/testdata/project-editing/project.qbs new file mode 100644 index 00000000..57cb3035 --- /dev/null +++ b/tests/auto/api/testdata/project-editing/project.qbs @@ -0,0 +1,39 @@ +import qbs + +CppApplication { + Group { + name: "Existing Group 1" + files: ["existingfile1.txt"] + } + property string aFile: "existingfile2.txt" + Group { + name: "Existing Group 2" + files: product.aFile + } + Group { + name: "Existing Group 3" + files: { + var file = "existingfile3.txt"; + return file; + } + } + Group { + name: "Existing Group 4" + prefix: "subdir/" + files: [] + } + Group { + name: "Existing Group 5" + prefix: "blubb" + files: [] + } + Group { + name: "Group with wildcards" + files: "*.klaus" + } + Group { + name: "Other group with wildcards" + files: "*.wildcard" + } + files: "main.cpp" +} diff --git a/tests/auto/api/testdata/project-editing/subdir/file.txt b/tests/auto/api/testdata/project-editing/subdir/file.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/project-editing/test.wildcard b/tests/auto/api/testdata/project-editing/test.wildcard new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/project-invalidation/project.early-error.qbs b/tests/auto/api/testdata/project-invalidation/project.early-error.qbs new file mode 100644 index 00000000..ecc76c81 --- /dev/null +++ b/tests/auto/api/testdata/project-invalidation/project.early-error.qbs @@ -0,0 +1,6 @@ +import qbs + +Product { + type: "mytype" + files: "nosuchfile.txt" +} diff --git a/tests/auto/api/testdata/project-invalidation/project.late-error.qbs b/tests/auto/api/testdata/project-invalidation/project.late-error.qbs new file mode 100644 index 00000000..6bce6f2d --- /dev/null +++ b/tests/auto/api/testdata/project-invalidation/project.late-error.qbs @@ -0,0 +1,14 @@ +import qbs + +Product { + type: "mytype" + + Rule { + inputs: ["mytype"] + Artifact { + filePath: "blubb" + fileTags: "mytype" + } + prepare: [] + } +} diff --git a/tests/auto/api/testdata/project-invalidation/project.no-error.qbs b/tests/auto/api/testdata/project-invalidation/project.no-error.qbs new file mode 100644 index 00000000..099553ff --- /dev/null +++ b/tests/auto/api/testdata/project-invalidation/project.no-error.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + type: "mytype" +} diff --git a/tests/auto/api/testdata/project-locking/project.qbs b/tests/auto/api/testdata/project-locking/project.qbs new file mode 100644 index 00000000..e08b008b --- /dev/null +++ b/tests/auto/api/testdata/project-locking/project.qbs @@ -0,0 +1,4 @@ +import qbs + +Project { +} diff --git a/tests/auto/api/testdata/project-properties-by-name/main1.cpp b/tests/auto/api/testdata/project-properties-by-name/main1.cpp new file mode 100644 index 00000000..ddc4a766 --- /dev/null +++ b/tests/auto/api/testdata/project-properties-by-name/main1.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SUB1 +#error "Missing define" +#endif +#ifdef SUB2 +#error "Extraneous define" +#endif + +int main() +{ +} diff --git a/tests/auto/api/testdata/project-properties-by-name/main2.cpp b/tests/auto/api/testdata/project-properties-by-name/main2.cpp new file mode 100644 index 00000000..7cc7fdc5 --- /dev/null +++ b/tests/auto/api/testdata/project-properties-by-name/main2.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SUB2 +#error "Missing define" +#endif +#ifdef SUB1 +#error "Extraneous define" +#endif + +int main() +{ +} diff --git a/tests/auto/api/testdata/project-properties-by-name/project.qbs b/tests/auto/api/testdata/project-properties-by-name/project.qbs new file mode 100644 index 00000000..b90068f8 --- /dev/null +++ b/tests/auto/api/testdata/project-properties-by-name/project.qbs @@ -0,0 +1,22 @@ +import qbs + +Project { + name: "toplevel" + property stringList theDefines: [] + Project { + name: "subproject1" + CppApplication { + name: "subproduct1" + files: ["main1.cpp"] + cpp.defines: project.theDefines + } + } + Project { + name: "subproject2" + CppApplication { + name: "subproduct2" + files: ["main2.cpp"] + cpp.defines: project.theDefines + } + } +} diff --git a/tests/auto/api/testdata/project-with-properties-item/project.qbs b/tests/auto/api/testdata/project-with-properties-item/project.qbs new file mode 100644 index 00000000..91d5de42 --- /dev/null +++ b/tests/auto/api/testdata/project-with-properties-item/project.qbs @@ -0,0 +1,12 @@ +import qbs 1.0 + +Project { + property string binPath: "/usr/bin" + property string libPath: "/usr/lib" + + Properties { + condition: qbs.targetOS.contains("macos") + binPath: "/Users/boo" + libPath: "/Libraries/foo" + } +} diff --git a/tests/auto/api/testdata/projectd b/tests/auto/api/testdata/projectd new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/properties-blocks/main.cpp b/tests/auto/api/testdata/properties-blocks/main.cpp new file mode 100644 index 00000000..5473bffa --- /dev/null +++ b/tests/auto/api/testdata/properties-blocks/main.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifndef HAVE_MAIN_CPP +# error missing define HAVE_MAIN_CPP +#endif +#ifndef DEFINE_IN_PROPERTIES +# error missing define DEFINE_IN_PROPERTIES +#endif + +int main() +{ +#ifdef _DEBUG + puts("Hello World! (debug version)"); +#else + puts("Hello World! (release version)"); +#endif +} + diff --git a/tests/auto/api/testdata/properties-blocks/propertiesblocks.qbs b/tests/auto/api/testdata/properties-blocks/propertiesblocks.qbs new file mode 100644 index 00000000..1b4c3680 --- /dev/null +++ b/tests/auto/api/testdata/properties-blocks/propertiesblocks.qbs @@ -0,0 +1,29 @@ +import qbs 1.0 + +Product { + Depends { name: 'cpp' } + + Properties { + condition: true + type: 'application' + consoleApplication: true + name: 'HelloWorld' + } + + Properties { + condition: name == 'HelloWorld' + cpp.defines: ['DEFINE_IN_PROPERTIES'] + } + + Properties { + condition: qbs.targetOS.contains("weird") + cpp.staticLibraries: "abc" + } + + Group { + cpp.defines: outer.concat(['HAVE_MAIN_CPP', cpp.debugInformation ? '_DEBUG' : '_RELEASE']) + files: ['main.cpp'] + } +} + + diff --git a/tests/auto/api/testdata/qt5-plugin/echointerface.h b/tests/auto/api/testdata/qt5-plugin/echointerface.h new file mode 100644 index 00000000..b414f247 --- /dev/null +++ b/tests/auto/api/testdata/qt5-plugin/echointerface.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ECHOINTERFACE_H +#define ECHOINTERFACE_H + +#include + +//! [0] +class EchoInterface +{ +public: + virtual ~EchoInterface() {} + virtual QString echo(const QString &message) = 0; +}; + + +QT_BEGIN_NAMESPACE + +#define EchoInterface_iid "org.qt-project.Qt.Examples.EchoInterface" + +Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid) +QT_END_NAMESPACE + +//! [0] +#endif diff --git a/tests/auto/api/testdata/qt5-plugin/echoplugin.cpp b/tests/auto/api/testdata/qt5-plugin/echoplugin.cpp new file mode 100644 index 00000000..0d4065fd --- /dev/null +++ b/tests/auto/api/testdata/qt5-plugin/echoplugin.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "echoplugin.h" + +QString EchoPlugin::echo(const QString &message) +{ + return message; +} diff --git a/tests/auto/api/testdata/qt5-plugin/echoplugin.h b/tests/auto/api/testdata/qt5-plugin/echoplugin.h new file mode 100644 index 00000000..b8281c0b --- /dev/null +++ b/tests/auto/api/testdata/qt5-plugin/echoplugin.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef ECHOPLUGIN_H +#define ECHOPLUGIN_H + +#include +#include +#include "echointerface.h" + +class EchoPlugin : public QObject, EchoInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.EchoInterface" FILE "echoplugin.json") + Q_INTERFACES(EchoInterface) + +public: + QString echo(const QString &message); +}; + +#endif diff --git a/tests/auto/api/testdata/qt5-plugin/echoplugin.json.source b/tests/auto/api/testdata/qt5-plugin/echoplugin.json.source new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/tests/auto/api/testdata/qt5-plugin/echoplugin.json.source @@ -0,0 +1 @@ +{} diff --git a/tests/auto/api/testdata/qt5-plugin/echoplugin_dummy.cpp b/tests/auto/api/testdata/qt5-plugin/echoplugin_dummy.cpp new file mode 100644 index 00000000..bee4e9b2 --- /dev/null +++ b/tests/auto/api/testdata/qt5-plugin/echoplugin_dummy.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void dummyFunc() {} diff --git a/tests/auto/api/testdata/qt5-plugin/project.qbs b/tests/auto/api/testdata/qt5-plugin/project.qbs new file mode 100644 index 00000000..834c386a --- /dev/null +++ b/tests/auto/api/testdata/qt5-plugin/project.qbs @@ -0,0 +1,48 @@ +import qbs.base +import qbs.File +import qbs.FileInfo + +DynamicLibrary { + name: "echoplugin" + + Depends { name: "Qt.core" } + Depends { name: "cpp" } + bundle.isBundle: false + + Group { + condition: Qt.core.versionMajor >= 5 + files: [ + "echoplugin.h", + "echoplugin.cpp", + ] + } + Group { + condition: Qt.core.versionMajor >= 5 + files: ["echoplugin.json.source"] + fileTags: ["json_in"] + } + + Group { + condition: Qt.core.versionMajor < 5 + files: "echoplugin_dummy.cpp" + } + + cpp.includePaths: buildDirectory + + Rule { + condition: Qt.core.versionMajor >= 5 + inputs: ["json_in"] + Artifact { + filePath: "echoplugin.json" + fileTags: ["qt_plugin_metadata"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating " + FileInfo.fileName(output.filePath); + cmd.sourceCode = function() { + File.copy(input.filePath, output.filePath); + } + return cmd; + } + } +} diff --git a/tests/auto/api/testdata/rc/main.cpp b/tests/auto/api/testdata/rc/main.cpp new file mode 100644 index 00000000..ed95605b --- /dev/null +++ b/tests/auto/api/testdata/rc/main.cpp @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() +{ + return 0; +} diff --git a/tests/auto/api/testdata/rc/rc.qbs b/tests/auto/api/testdata/rc/rc.qbs new file mode 100644 index 00000000..418ac274 --- /dev/null +++ b/tests/auto/api/testdata/rc/rc.qbs @@ -0,0 +1,15 @@ +import qbs 1.0 + +Application { + type: "application" + consoleApplication: true + name: "rc" + + Depends { name: 'cpp' } + + files: [ + "main.cpp", + "test.rc" + ] +} + diff --git a/tests/auto/api/testdata/rc/test.rc b/tests/auto/api/testdata/rc/test.rc new file mode 100644 index 00000000..a8e5fd39 --- /dev/null +++ b/tests/auto/api/testdata/rc/test.rc @@ -0,0 +1,22 @@ +#define IDR_VERSION1 1 + +IDR_VERSION1 VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +FILEOS 0x00000004 +FILETYPE 0x00000000 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "FFFF0000" + BEGIN + VALUE "FileVersion", "1.0.0.0\0" + VALUE "ProductVersion", "1.0.0.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xFFFF, 0x0000 + END +END + diff --git a/tests/auto/api/testdata/recursive-wildcards/dir/file1.txt b/tests/auto/api/testdata/recursive-wildcards/dir/file1.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/recursive-wildcards/dir/subdir/file2.txt b/tests/auto/api/testdata/recursive-wildcards/dir/subdir/file2.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/recursive-wildcards/recursive_wildcards.qbs b/tests/auto/api/testdata/recursive-wildcards/recursive_wildcards.qbs new file mode 100644 index 00000000..a1970e1c --- /dev/null +++ b/tests/auto/api/testdata/recursive-wildcards/recursive_wildcards.qbs @@ -0,0 +1,7 @@ +Product { + Group { + files: "dir/**" + qbs.install: true + qbs.installDir: "dir" + } +} diff --git a/tests/auto/api/testdata/referenced-file-errors/ambiguousdir/p1.qbs b/tests/auto/api/testdata/referenced-file-errors/ambiguousdir/p1.qbs new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/referenced-file-errors/ambiguousdir/p2.qbs b/tests/auto/api/testdata/referenced-file-errors/ambiguousdir/p2.qbs new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/referenced-file-errors/cycle.qbs b/tests/auto/api/testdata/referenced-file-errors/cycle.qbs new file mode 100644 index 00000000..72ad2b8b --- /dev/null +++ b/tests/auto/api/testdata/referenced-file-errors/cycle.qbs @@ -0,0 +1,7 @@ +import qbs + +Project { + property string productName: "p1" + Product { name: project.productName } + references: ["referenced-file-errors.qbs"] +} diff --git a/tests/auto/api/testdata/referenced-file-errors/emptydir/.gitignore b/tests/auto/api/testdata/referenced-file-errors/emptydir/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/referenced-file-errors/modules/brokenmodule/brokenmodule.qbs b/tests/auto/api/testdata/referenced-file-errors/modules/brokenmodule/brokenmodule.qbs new file mode 100644 index 00000000..861a7393 --- /dev/null +++ b/tests/auto/api/testdata/referenced-file-errors/modules/brokenmodule/brokenmodule.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + syntax error +} diff --git a/tests/auto/api/testdata/referenced-file-errors/okay.qbs b/tests/auto/api/testdata/referenced-file-errors/okay.qbs new file mode 100644 index 00000000..6ec8b1ed --- /dev/null +++ b/tests/auto/api/testdata/referenced-file-errors/okay.qbs @@ -0,0 +1,3 @@ +import qbs + +Product { name: "p2" } diff --git a/tests/auto/api/testdata/referenced-file-errors/okay2.qbs b/tests/auto/api/testdata/referenced-file-errors/okay2.qbs new file mode 100644 index 00000000..f3b92dd9 --- /dev/null +++ b/tests/auto/api/testdata/referenced-file-errors/okay2.qbs @@ -0,0 +1,3 @@ +import qbs + +Product { name: "p4" } diff --git a/tests/auto/api/testdata/referenced-file-errors/referenced-file-errors.qbs b/tests/auto/api/testdata/referenced-file-errors/referenced-file-errors.qbs new file mode 100644 index 00000000..b6b255ab --- /dev/null +++ b/tests/auto/api/testdata/referenced-file-errors/referenced-file-errors.qbs @@ -0,0 +1,32 @@ +import qbs + +Project { + references: [ + "ambiguousdir", + "cycle.qbs", + "emptydir", + "nosuchfile.qbs", + "okay.qbs", + "wrongtype.qbs", + ] + + SubProject { + filePath: "cycle.qbs" + Properties { + productName: "p3" + } + } + + SubProject { + filePath: "nosuchfile.qbs" + } + + SubProject { + filePath: "okay2.qbs" + } + + Product { + name: "p5" + Depends { name: "brokenmodule" } + } +} diff --git a/tests/auto/api/testdata/referenced-file-errors/wrongtype.qbs b/tests/auto/api/testdata/referenced-file-errors/wrongtype.qbs new file mode 100644 index 00000000..9322b53b --- /dev/null +++ b/tests/auto/api/testdata/referenced-file-errors/wrongtype.qbs @@ -0,0 +1,3 @@ +import qbs + +Module { } diff --git a/tests/auto/api/testdata/references/invalid1.qbs b/tests/auto/api/testdata/references/invalid1.qbs new file mode 100644 index 00000000..4bbb26d3 --- /dev/null +++ b/tests/auto/api/testdata/references/invalid1.qbs @@ -0,0 +1,5 @@ +import qbs + +Project { + references: "subdir-with-no-project" +} \ No newline at end of file diff --git a/tests/auto/api/testdata/references/invalid2.qbs b/tests/auto/api/testdata/references/invalid2.qbs new file mode 100644 index 00000000..1946e222 --- /dev/null +++ b/tests/auto/api/testdata/references/invalid2.qbs @@ -0,0 +1,5 @@ +import qbs + +Project { + references: "subdir-with-multiple-projects" +} \ No newline at end of file diff --git a/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject1.qbs b/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject1.qbs new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject2.qbs b/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject2.qbs new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject3.qbs b/tests/auto/api/testdata/references/subdir-with-multiple-projects/subproject3.qbs new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/references/subdir-with-no-project/test.txt b/tests/auto/api/testdata/references/subdir-with-no-project/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/references/subdir-with-one-project/p.qbs b/tests/auto/api/testdata/references/subdir-with-one-project/p.qbs new file mode 100644 index 00000000..7d453a67 --- /dev/null +++ b/tests/auto/api/testdata/references/subdir-with-one-project/p.qbs @@ -0,0 +1,3 @@ +import qbs + +Project { } diff --git a/tests/auto/api/testdata/references/subdir-with-one-project/test.txt b/tests/auto/api/testdata/references/subdir-with-one-project/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/references/valid.qbs b/tests/auto/api/testdata/references/valid.qbs new file mode 100644 index 00000000..43d728a4 --- /dev/null +++ b/tests/auto/api/testdata/references/valid.qbs @@ -0,0 +1,5 @@ +import qbs + +Project { + references: "subdir-with-one-project" +} diff --git a/tests/auto/api/testdata/relaxed-mode-recovery/relaxed-mode-recovery.qbs b/tests/auto/api/testdata/relaxed-mode-recovery/relaxed-mode-recovery.qbs new file mode 100644 index 00000000..b9e786a4 --- /dev/null +++ b/tests/auto/api/testdata/relaxed-mode-recovery/relaxed-mode-recovery.qbs @@ -0,0 +1,10 @@ +import qbs + +Project { + Product { + name: "dep" + Export { Depends { name: "blubb" } } + } + Product { name: "p1"; Depends { name: "dep" } } + Product { name: "p2"; Depends { name: "dep" } } +} diff --git a/tests/auto/api/testdata/remove-file-dependency/main.cpp b/tests/auto/api/testdata/remove-file-dependency/main.cpp new file mode 100644 index 00000000..5c0b0393 --- /dev/null +++ b/tests/auto/api/testdata/remove-file-dependency/main.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "someheader.h" +#include + +int main() +{ + printf("The magic value is %d.\n", magicValue()); + return 0; +} + diff --git a/tests/auto/api/testdata/remove-file-dependency/removeFileDependency.qbs b/tests/auto/api/testdata/remove-file-dependency/removeFileDependency.qbs new file mode 100644 index 00000000..8ed92776 --- /dev/null +++ b/tests/auto/api/testdata/remove-file-dependency/removeFileDependency.qbs @@ -0,0 +1,7 @@ +import qbs + +CppApplication { + files: ["main.cpp"] + // Do not reference header files here to force them to be FileDependency objects. +} + diff --git a/tests/auto/api/testdata/remove-file-dependency/someheader.h b/tests/auto/api/testdata/remove-file-dependency/someheader.h new file mode 100644 index 00000000..f0efc4e7 --- /dev/null +++ b/tests/auto/api/testdata/remove-file-dependency/someheader.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +inline int magicValue() { return 156; } diff --git a/tests/auto/api/testdata/rename-product/lib.cpp b/tests/auto/api/testdata/rename-product/lib.cpp new file mode 100644 index 00000000..1bdd05e4 --- /dev/null +++ b/tests/auto/api/testdata/rename-product/lib.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +MY_EXPORT void f() { } diff --git a/tests/auto/api/testdata/rename-product/main.cpp b/tests/auto/api/testdata/rename-product/main.cpp new file mode 100644 index 00000000..1351aad1 --- /dev/null +++ b/tests/auto/api/testdata/rename-product/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f(); + +int main() +{ + f(); +} diff --git a/tests/auto/api/testdata/rename-product/rename.qbs b/tests/auto/api/testdata/rename-product/rename.qbs new file mode 100644 index 00000000..c7811059 --- /dev/null +++ b/tests/auto/api/testdata/rename-product/rename.qbs @@ -0,0 +1,18 @@ +import qbs + +Project { + CppApplication { + Depends { name: "TheLib" } + cpp.defines: "MY_EXPORT=" + files: "main.cpp" + } + + DynamicLibrary { + name: "TheLib" + Depends { name: "cpp" } + Depends { name: "Qt.core" } + cpp.defines: "MY_EXPORT=Q_DECL_EXPORT" + files: "lib.cpp" + bundle.isBundle: false + } +} diff --git a/tests/auto/api/testdata/rename-target-artifact/lib.cpp b/tests/auto/api/testdata/rename-target-artifact/lib.cpp new file mode 100644 index 00000000..1bdd05e4 --- /dev/null +++ b/tests/auto/api/testdata/rename-target-artifact/lib.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +MY_EXPORT void f() { } diff --git a/tests/auto/api/testdata/rename-target-artifact/main.cpp b/tests/auto/api/testdata/rename-target-artifact/main.cpp new file mode 100644 index 00000000..1351aad1 --- /dev/null +++ b/tests/auto/api/testdata/rename-target-artifact/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f(); + +int main() +{ + f(); +} diff --git a/tests/auto/api/testdata/rename-target-artifact/rename.qbs b/tests/auto/api/testdata/rename-target-artifact/rename.qbs new file mode 100644 index 00000000..e94cc7b4 --- /dev/null +++ b/tests/auto/api/testdata/rename-target-artifact/rename.qbs @@ -0,0 +1,19 @@ +import qbs + +Project { + CppApplication { + Depends { name: "TheLib" } + cpp.defines: "MY_EXPORT=" + files: "main.cpp" + } + + DynamicLibrary { + name: "TheLib" + targetName: "the_lib" + Depends { name: "cpp" } + Depends { name: "Qt.core" } + cpp.defines: "MY_EXPORT=Q_DECL_EXPORT" + files: "lib.cpp" + bundle.isBundle: false + } +} diff --git a/tests/auto/api/testdata/restored-warnings/file.cpp b/tests/auto/api/testdata/restored-warnings/file.cpp new file mode 100644 index 00000000..56757a70 --- /dev/null +++ b/tests/auto/api/testdata/restored-warnings/file.cpp @@ -0,0 +1 @@ +void f() {} diff --git a/tests/auto/api/testdata/restored-warnings/main.cpp b/tests/auto/api/testdata/restored-warnings/main.cpp new file mode 100644 index 00000000..237c8ce1 --- /dev/null +++ b/tests/auto/api/testdata/restored-warnings/main.cpp @@ -0,0 +1 @@ +int main() {} diff --git a/tests/auto/api/testdata/restored-warnings/restored-warnings.qbs b/tests/auto/api/testdata/restored-warnings/restored-warnings.qbs new file mode 100644 index 00000000..2de943ed --- /dev/null +++ b/tests/auto/api/testdata/restored-warnings/restored-warnings.qbs @@ -0,0 +1,15 @@ +import qbs +import qbs.Process 1.5 + +CppApplication { + name: "theProduct" + + property bool moreFiles: false + cpp.blubb: true + + files: ["file.cpp", "main.cpp"] + Group { + condition: moreFiles + files: ["blubb.cpp"] + } +} diff --git a/tests/auto/api/testdata/rule-conflict/main.cpp b/tests/auto/api/testdata/rule-conflict/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/api/testdata/rule-conflict/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/api/testdata/rule-conflict/pch1.h b/tests/auto/api/testdata/rule-conflict/pch1.h new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/api/testdata/rule-conflict/pch1.h @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/api/testdata/rule-conflict/pch2.h b/tests/auto/api/testdata/rule-conflict/pch2.h new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/api/testdata/rule-conflict/pch2.h @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/api/testdata/rule-conflict/rule-conflict.qbs b/tests/auto/api/testdata/rule-conflict/rule-conflict.qbs new file mode 100644 index 00000000..d2e1b914 --- /dev/null +++ b/tests/auto/api/testdata/rule-conflict/rule-conflict.qbs @@ -0,0 +1,11 @@ +import qbs + +CppApplication { + cpp.useCxxPrecompiledHeader: true + files: "main.cpp" + Group { + name: "pch files" + files: ["pch1.h", "pch2.h"] + fileTags: "cpp_pch_src" + } +} diff --git a/tests/auto/api/testdata/same-base-name/lib.c b/tests/auto/api/testdata/same-base-name/lib.c new file mode 100644 index 00000000..1f889906 --- /dev/null +++ b/tests/auto/api/testdata/same-base-name/lib.c @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +extern void printHelloC() +{ + printf("Hello from C in " __FILE__ "\n"); +} diff --git a/tests/auto/api/testdata/same-base-name/lib.cpp b/tests/auto/api/testdata/same-base-name/lib.cpp new file mode 100644 index 00000000..e2d0e2e5 --- /dev/null +++ b/tests/auto/api/testdata/same-base-name/lib.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +extern "C" void printHelloCpp() +{ + std::cout << "Hello from C++ in " << __FILE__ << std::endl; +} diff --git a/tests/auto/api/testdata/same-base-name/lib.m b/tests/auto/api/testdata/same-base-name/lib.m new file mode 100644 index 00000000..17cd2dce --- /dev/null +++ b/tests/auto/api/testdata/same-base-name/lib.m @@ -0,0 +1,6 @@ +#import + +extern void printHelloObjc() +{ + NSLog(@"Hello from Objective-C in " __FILE__); +} diff --git a/tests/auto/api/testdata/same-base-name/lib.mm b/tests/auto/api/testdata/same-base-name/lib.mm new file mode 100644 index 00000000..ee284b08 --- /dev/null +++ b/tests/auto/api/testdata/same-base-name/lib.mm @@ -0,0 +1,8 @@ +#include +#import + +extern "C" void printHelloObjcpp() +{ + NSLog(@"Hello from Objective-C++..."); + std::cout << "...in " __FILE__ << std::endl; +} diff --git a/tests/auto/api/testdata/same-base-name/main.c b/tests/auto/api/testdata/same-base-name/main.c new file mode 100644 index 00000000..7f8cd22e --- /dev/null +++ b/tests/auto/api/testdata/same-base-name/main.c @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +extern void printHelloC(); +extern void printHelloCpp(); + +#ifdef __APPLE__ +extern void printHelloObjc(); +extern void printHelloObjcpp(); +#endif + +int main() +{ + printHelloC(); + printHelloCpp(); +#ifdef __APPLE__ + printHelloObjc(); + printHelloObjcpp(); +#endif + return 0; +} diff --git a/tests/auto/api/testdata/same-base-name/project.qbs b/tests/auto/api/testdata/same-base-name/project.qbs new file mode 100644 index 00000000..c6a7a6fa --- /dev/null +++ b/tests/auto/api/testdata/same-base-name/project.qbs @@ -0,0 +1,33 @@ +import qbs 1.0 + +Project { + CppApplication { + type: "application" + consoleApplication: true + Depends { name: "basenamelib" } + name: "basename" + files: "main.c" + } + + StaticLibrary { + Depends { name: "cpp" } + name: "basenamelib" + files: [ + "lib.c", + "lib.cpp" + ] + + Group { + condition: qbs.targetOS.contains("darwin") + files: [ + "lib.m", + "lib.mm" + ] + } + + Export { + Depends { name: "cpp" } + cpp.frameworks: qbs.targetOS.contains("darwin") ? "Foundation" : undefined + } + } +} diff --git a/tests/auto/api/testdata/simple-probe/main.cpp b/tests/auto/api/testdata/simple-probe/main.cpp new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/api/testdata/simple-probe/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/api/testdata/simple-probe/project.qbs b/tests/auto/api/testdata/simple-probe/project.qbs new file mode 100644 index 00000000..34700a99 --- /dev/null +++ b/tests/auto/api/testdata/simple-probe/project.qbs @@ -0,0 +1,32 @@ +import qbs 1.0 +import qbs.Probes + +CppApplication { + Probe { + id: probe1 + property string someString + configure: { + someString = "one"; + found = true; + } + } + Probe { + id: probe2 + configure: { + found = false; + } + } + type: ["application"] + name: "MyApp" + consoleApplication: { + if (!probe1.found) + throw "probe1 not found"; + if (probe2.found) + throw "probe2 unexpectedly found"; + if (probe1.someString !== "one") + throw "probe1.someString expected to be \"one\"." + return true + } + files: ["main.cpp"] +} + diff --git a/tests/auto/api/testdata/soft-dependency/main.cpp b/tests/auto/api/testdata/soft-dependency/main.cpp new file mode 100644 index 00000000..eb71ab61 --- /dev/null +++ b/tests/auto/api/testdata/soft-dependency/main.cpp @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() +{ + thisShouldNotLink(); +} diff --git a/tests/auto/api/testdata/soft-dependency/project.qbs b/tests/auto/api/testdata/soft-dependency/project.qbs new file mode 100644 index 00000000..bbf37fda --- /dev/null +++ b/tests/auto/api/testdata/soft-dependency/project.qbs @@ -0,0 +1,14 @@ +import qbs + +Application { + Depends { + name: "nosuchmodule" + required: false + } + Depends { + name: "cpp" + condition: nosuchmodule.present + } + + files: "main.cpp" +} diff --git a/tests/auto/api/testdata/source-file-in-build-dir/file.cpp b/tests/auto/api/testdata/source-file-in-build-dir/file.cpp new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/api/testdata/source-file-in-build-dir/file.cpp @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/api/testdata/source-file-in-build-dir/project.qbs b/tests/auto/api/testdata/source-file-in-build-dir/project.qbs new file mode 100644 index 00000000..2e25fd45 --- /dev/null +++ b/tests/auto/api/testdata/source-file-in-build-dir/project.qbs @@ -0,0 +1,8 @@ +import qbs + +CppApplication { + Group { + name: "the group" + files: "**/*.cpp" + } +} diff --git a/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/moc_blubb.cpp b/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/moc_blubb.cpp new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/qt-debug.bg b/tests/auto/api/testdata/source-file-in-build-dir/qt-debug/qt-debug.bg new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/static-lib-deps/a1.cpp b/tests/auto/api/testdata/static-lib-deps/a1.cpp new file mode 100644 index 00000000..460eb52e --- /dev/null +++ b/tests/auto/api/testdata/static-lib-deps/a1.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +void a1() +{ + std::cout << "a1" << std::endl; +} + diff --git a/tests/auto/api/testdata/static-lib-deps/a2.cpp b/tests/auto/api/testdata/static-lib-deps/a2.cpp new file mode 100644 index 00000000..4ab7404f --- /dev/null +++ b/tests/auto/api/testdata/static-lib-deps/a2.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +void a2() +{ + std::cout << "a2" << std::endl; +} + diff --git a/tests/auto/api/testdata/static-lib-deps/b.cpp b/tests/auto/api/testdata/static-lib-deps/b.cpp new file mode 100644 index 00000000..cec54420 --- /dev/null +++ b/tests/auto/api/testdata/static-lib-deps/b.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void a1(); + +void b() +{ + a1(); +} + diff --git a/tests/auto/api/testdata/static-lib-deps/c.cpp b/tests/auto/api/testdata/static-lib-deps/c.cpp new file mode 100644 index 00000000..0ca246c8 --- /dev/null +++ b/tests/auto/api/testdata/static-lib-deps/c.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void a2(); + +void c() +{ + a2(); +} + diff --git a/tests/auto/api/testdata/static-lib-deps/d.cpp b/tests/auto/api/testdata/static-lib-deps/d.cpp new file mode 100644 index 00000000..7c681283 --- /dev/null +++ b/tests/auto/api/testdata/static-lib-deps/d.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef WITH_PTHREAD +#include +#elif defined(WITH_LEX_YACC) +extern "C" int yywrap(void); +extern "C" void yyerror(char const *s); +#elif defined(WITH_SETUPAPI) +#include +#include +#endif + +void b(); +void c(); + +int d() +{ + b(); + c(); + +#ifdef WITH_PTHREAD + pthread_t self = pthread_self(); + return static_cast(self); +#elif defined(WITH_LEX_YACC) + yywrap(); + yyerror("no error"); + return 0; +#elif defined(WITH_SETUPAPI) + CABINET_INFO ci; + ci.SetId = 0; + SetupIterateCabinet(L"invalid-file-path", 0, NULL, NULL); + return ci.SetId; +#else + return 0; +#endif +} diff --git a/tests/auto/api/testdata/static-lib-deps/e.cpp b/tests/auto/api/testdata/static-lib-deps/e.cpp new file mode 100644 index 00000000..aea27921 --- /dev/null +++ b/tests/auto/api/testdata/static-lib-deps/e.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int d(); + +int e() +{ + return d(); +} + diff --git a/tests/auto/api/testdata/static-lib-deps/main.cpp b/tests/auto/api/testdata/static-lib-deps/main.cpp new file mode 100644 index 00000000..b42d35a2 --- /dev/null +++ b/tests/auto/api/testdata/static-lib-deps/main.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int e(); + +int main() +{ + return e(); +} + diff --git a/tests/auto/api/testdata/static-lib-deps/project.qbs b/tests/auto/api/testdata/static-lib-deps/project.qbs new file mode 100644 index 00000000..074acc8a --- /dev/null +++ b/tests/auto/api/testdata/static-lib-deps/project.qbs @@ -0,0 +1,104 @@ +import qbs 1.0 + +Project { + StaticLibrary { + name: "a" + + Depends { name: "cpp" } + + files: [ + "a1.cpp", + "a2.cpp", + ] + } + StaticLibrary { + name: "b" + + Depends { name: "cpp" } + + Depends { name: "a" } + + files: [ + "b.cpp", + ] + } + StaticLibrary { + name: "c" + + Depends { name: "cpp" } + + Depends { name: "a" } + + files: [ + "c.cpp", + ] + } + StaticLibrary { + name: "d" + + Depends { name: "cpp" } + + Depends { name: "b" } + Depends { name: "c" } + + files: [ + "d.cpp", + ] + + Properties { + condition: qbs.targetOS.contains("windows") + cpp.defines: ["WITH_SETUPAPI"] + } + Properties { + condition: qbs.targetOS.contains("macos") + cpp.defines: ["WITH_LEX_YACC"] + } + Properties { + condition: qbs.targetOS.contains("linux") + cpp.defines: ["WITH_PTHREAD"] + } + + Export { + Depends { name: "cpp" } + Properties { + condition: qbs.targetOS.contains("linux") + cpp.staticLibraries: ["pthread"] + } + Properties { + condition: qbs.targetOS.contains("macos") + cpp.staticLibraries: ["l", "y"] + } + Properties { + condition: qbs.targetOS.contains("windows") + cpp.staticLibraries: ["setupapi"] + } + } + } + StaticLibrary { + name: "e" + + Depends { name: "cpp" } + + Depends { name: "d" } + + files: [ + "e.cpp", + ] + } + CppApplication { + name: "staticLibDeps" + type: "application" + consoleApplication: true + + Depends { name: "e" } + + Properties { + condition: qbs.targetOS.contains("linux") + cpp.linkerFlags: ["-static"] + } + + files: [ + "main.cpp", + ] + } +} diff --git a/tests/auto/api/testdata/subprojects/resources/imports/LibraryType/type.js b/tests/auto/api/testdata/subprojects/resources/imports/LibraryType/type.js new file mode 100644 index 00000000..cb07f8e5 --- /dev/null +++ b/tests/auto/api/testdata/subprojects/resources/imports/LibraryType/type.js @@ -0,0 +1 @@ +function type() { return "dynamiclibrary"; } diff --git a/tests/auto/api/testdata/subprojects/resources/modules/QtCoreDepender/qtcoredepender.qbs b/tests/auto/api/testdata/subprojects/resources/modules/QtCoreDepender/qtcoredepender.qbs new file mode 100644 index 00000000..03d64930 --- /dev/null +++ b/tests/auto/api/testdata/subprojects/resources/modules/QtCoreDepender/qtcoredepender.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + Depends { name: "Qt.core" } +} diff --git a/tests/auto/api/testdata/subprojects/subproject1/main.cpp b/tests/auto/api/testdata/subprojects/subproject1/main.cpp new file mode 100644 index 00000000..1351aad1 --- /dev/null +++ b/tests/auto/api/testdata/subprojects/subproject1/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f(); + +int main() +{ + f(); +} diff --git a/tests/auto/api/testdata/subprojects/subproject2/subproject2.qbs b/tests/auto/api/testdata/subprojects/subproject2/subproject2.qbs new file mode 100644 index 00000000..3046fe4b --- /dev/null +++ b/tests/auto/api/testdata/subprojects/subproject2/subproject2.qbs @@ -0,0 +1,15 @@ +import qbs + +Project { + name: "subproject2" + property string libNamePrefix: "test" + SubProject { + filePath: "subproject3/subproject3.qbs" + inheritProperties: true + Properties { + name: "overridden name" + condition: qbs.targetOS.length > 0 + libNameSuffix: "Lib" + } + } +} diff --git a/tests/auto/api/testdata/subprojects/subproject2/subproject3/subproject3.qbs b/tests/auto/api/testdata/subprojects/subproject2/subproject3/subproject3.qbs new file mode 100644 index 00000000..acdf1d7e --- /dev/null +++ b/tests/auto/api/testdata/subprojects/subproject2/subproject3/subproject3.qbs @@ -0,0 +1,16 @@ +import qbs +import LibraryType + +Project { + condition: false + property string libNameSuffix: "blubb" + Product { + name: project.libNamePrefix + project.libNameSuffix + type: LibraryType.type() + Depends { name: "cpp" } + Depends { name: "QtCoreDepender" } + cpp.defines: "MY_EXPORT=Q_DECL_EXPORT" + files: "testlib.cpp" + Export { Depends { name: "Qt.core" } } + } +} diff --git a/tests/auto/api/testdata/subprojects/subproject2/subproject3/testlib.cpp b/tests/auto/api/testdata/subprojects/subproject2/subproject3/testlib.cpp new file mode 100644 index 00000000..dd573736 --- /dev/null +++ b/tests/auto/api/testdata/subprojects/subproject2/subproject3/testlib.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +MY_EXPORT void f() {} diff --git a/tests/auto/api/testdata/subprojects/toplevelproject.qbs b/tests/auto/api/testdata/subprojects/toplevelproject.qbs new file mode 100644 index 00000000..f167ccab --- /dev/null +++ b/tests/auto/api/testdata/subprojects/toplevelproject.qbs @@ -0,0 +1,19 @@ +import qbs + +Project { + name: "top level project" + references: ["subproject2"] + + Project { + condition: true + name: "app-project" + CppApplication { + name: "app" + Depends { name: "testLib" } + cpp.defines: "MY_EXPORT=" + files: "subproject1/main.cpp" + } + } + + qbsSearchPaths: ["resources"] +} diff --git a/tests/auto/api/testdata/transformers/main.cpp b/tests/auto/api/testdata/transformers/main.cpp new file mode 100644 index 00000000..03650959 --- /dev/null +++ b/tests/auto/api/testdata/transformers/main.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +using namespace std; + +bool displayTextFile(const string &dirPath, const string &fileName) +{ + string fullPath = dirPath + fileName; + ifstream istream(fullPath.c_str()); + if (!istream.is_open()) { + cout << "Cannot open " << fileName << endl; + return false; + } + cout << "---" << fileName << "---" << endl; + char buf[256]; + unsigned int i = 1; + while (istream.good()) { + istream.getline(buf, sizeof(buf)); + cout << i++ << ": " << buf << endl; + } + return true; +} + +int main(int, char **argv) +{ + string appPath(argv[0]); + size_t i = appPath.find_last_of('/'); + if (i == string::npos) + i = appPath.find_last_of('\\'); + if (i == string::npos) // No path, plain executable was called + appPath.clear(); + else + appPath.resize(i + 1); + if (!displayTextFile(appPath, "foo.txt")) + return 1; + if (!displayTextFile(appPath, "bar.txt")) + return 2; + cout << "-------------" << endl; + return 0; +} + diff --git a/tests/auto/api/testdata/transformers/transformers.qbs b/tests/auto/api/testdata/transformers/transformers.qbs new file mode 100644 index 00000000..1e5a0ec4 --- /dev/null +++ b/tests/auto/api/testdata/transformers/transformers.qbs @@ -0,0 +1,93 @@ +import qbs 1.0 +import qbs.File +import qbs.TextFile +import qbs.Xml +import qbs.FileInfo + +Project { + Product { + name: "HelloWorld" + type: "application" + consoleApplication: true + + Group { + files: ["main.cpp"] + fileTags: ["main"] + } + + Depends { name: "cpp" } + + Rule { + // no inputs -> just a generator + multiplex: true + Artifact { + filePath: "foo.txt" + fileTags: "text" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating foo.txt"; + cmd.highlight = "linker"; + cmd.sourceCode = function () { + File.remove(output.filePath); + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.write("Dear Sir/Madam,\n\n"); + f.write("this is a generated file.\n\n\n"); + f.write("Best Regards and Mellow Greetings,\nYour Build Tool.\n"); + f.close(); + } + return cmd; + } + } + + Rule { + multiplex: true + // no inputs -> just a generator + Artifact { + filePath: "foo.xml" + fileTags: "xml" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating foo.xml"; + cmd.highlight = "linker"; + cmd.sourceCode = function () { + File.remove(output.filePath); + var doc = new XmlDomDocument(); + var root = doc.createElement("root"); + doc.appendChild(root); + + var tag = doc.createElement("Greeting"); + root.appendChild(tag); + tag.appendChild(doc.createTextNode("text node")); + doc.save(output.filePath); + } + return cmd; + } + } + + Rule { + inputs: ["main"] + Artifact { + filePath: "bar.txt" + fileTags: "text" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating bar.txt"; + cmd.highlight = "linker"; + cmd.inputFileName = input.filePath; + cmd.sourceCode = function() { + File.remove(output.filePath); + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.write("Dear Sir/Madam,\n\n"); + f.write("this file was generated from " + inputFileName + ".\n\n\n"); + f.write("Best Regards and Mellow Greetings,\nYour Build Tool.\n"); + f.close(); + } + return cmd; + } + } + } +} + diff --git a/tests/auto/api/testdata/two-default-property-values/modules/mymodule/mymodule.qbs b/tests/auto/api/testdata/two-default-property-values/modules/mymodule/mymodule.qbs new file mode 100644 index 00000000..52e99e24 --- /dev/null +++ b/tests/auto/api/testdata/two-default-property-values/modules/mymodule/mymodule.qbs @@ -0,0 +1,24 @@ +import qbs +import qbs.TextFile + +Module { + property string direct + property string indirect: direct ? "set" : "unset" + + Rule { + inputs: ["txt"] + Artifact { + filePath: product.moduleProperty("mymodule", "indirect") + fileTags: ["mymodule"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating " + output.fileName; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.close(); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/api/testdata/two-default-property-values/modules/myothermodule/myothermodule.qbs b/tests/auto/api/testdata/two-default-property-values/modules/myothermodule/myothermodule.qbs new file mode 100644 index 00000000..e0ec699d --- /dev/null +++ b/tests/auto/api/testdata/two-default-property-values/modules/myothermodule/myothermodule.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + Depends { name: "mymodule" } +} diff --git a/tests/auto/api/testdata/two-default-property-values/project.qbs b/tests/auto/api/testdata/two-default-property-values/project.qbs new file mode 100644 index 00000000..c0bc720f --- /dev/null +++ b/tests/auto/api/testdata/two-default-property-values/project.qbs @@ -0,0 +1,13 @@ +import qbs + +Product { + name: "two-default-property-values" + type: "mymodule" + Depends { name: "mymodule" } + Depends { name: "myothermodule" } + mymodule.direct: "dummy" + Group { + files: ["test.txt"] + fileTags: ["txt"] + } +} diff --git a/tests/auto/api/testdata/two-default-property-values/test.txt b/tests/auto/api/testdata/two-default-property-values/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/api/testdata/type-change/main.cpp b/tests/auto/api/testdata/type-change/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/api/testdata/type-change/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/api/testdata/type-change/project.qbs b/tests/auto/api/testdata/type-change/project.qbs new file mode 100644 index 00000000..7661aa30 --- /dev/null +++ b/tests/auto/api/testdata/type-change/project.qbs @@ -0,0 +1,7 @@ +import qbs + +Product { + files: "main.cpp" + Depends { name: "cpp" } + // type: "application" +} diff --git a/tests/auto/api/testdata/uic/bla.cpp b/tests/auto/api/testdata/uic/bla.cpp new file mode 100644 index 00000000..eac879fa --- /dev/null +++ b/tests/auto/api/testdata/uic/bla.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bla.h" + +int main() +{ + Ui::MainWindow mainWindow; + Q_UNUSED(mainWindow); +} + diff --git a/tests/auto/api/testdata/uic/bla.h b/tests/auto/api/testdata/uic/bla.h new file mode 100644 index 00000000..50c1bc15 --- /dev/null +++ b/tests/auto/api/testdata/uic/bla.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "ui.h" diff --git a/tests/auto/api/testdata/uic/ui.h b/tests/auto/api/testdata/uic/ui.h new file mode 100644 index 00000000..9bbd5814 --- /dev/null +++ b/tests/auto/api/testdata/uic/ui.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "ui_ui.h" diff --git a/tests/auto/api/testdata/uic/ui.ui b/tests/auto/api/testdata/uic/ui.ui new file mode 100644 index 00000000..b07f62d0 --- /dev/null +++ b/tests/auto/api/testdata/uic/ui.ui @@ -0,0 +1,31 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + MainWindow + + + + + + 0 + 0 + 800 + 25 + + + + + + + + diff --git a/tests/auto/api/testdata/uic/uic.qbs b/tests/auto/api/testdata/uic/uic.qbs new file mode 100644 index 00000000..301cdad4 --- /dev/null +++ b/tests/auto/api/testdata/uic/uic.qbs @@ -0,0 +1,16 @@ +import qbs 1.0 + +Project { + QtGuiApplication { + type: "application" + consoleApplication: true + name: "ui" + + files: [ + "bla.cpp", + "bla.h", + "ui.ui", + "ui.h" + ] + } +} diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp new file mode 100644 index 00000000..b0905ea4 --- /dev/null +++ b/tests/auto/api/tst_api.cpp @@ -0,0 +1,2189 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_api.h" + +#include "../shared.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define VERIFY_NO_ERROR(errorInfo) \ + QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString())) + +#define WAIT_FOR_NEW_TIMESTAMP() waitForNewTimestamp(m_workingDataDir) + +class LogSink: public qbs::ILogSink +{ +public: + QString output; + + void doPrintWarning(const qbs::ErrorInfo &error) { + qDebug("%s", qPrintable(error.toString())); + warnings << error; + } + void doPrintMessage(qbs::LoggerLevel, const QString &message, const QString &) { + output += message; + } + + QList warnings; +}; + +class BuildDescriptionReceiver : public QObject +{ + Q_OBJECT +public: + QString descriptions; + + void handleDescription(const QString &, const QString &description) { + descriptions += description; + } +}; + +class ProcessResultReceiver : public QObject +{ + Q_OBJECT +public: + QString output; + QVector results; + + void handleProcessResult(const qbs::ProcessResult &result) { + results << result; + output += result.stdErr().join(QLatin1Char('\n')); + output += result.stdOut().join(QLatin1Char('\n')); + } +}; + +class TaskReceiver : public QObject +{ + Q_OBJECT +public: + QString taskDescriptions; + + void handleTaskStart(const QString &task) { taskDescriptions += task; } +}; + + +static void removeBuildDir(const qbs::SetupProjectParameters ¶ms) +{ + QString message; + const QString dir = params.buildRoot() + '/' + params.configurationName(); + if (!qbs::Internal::removeDirectoryWithContents(dir, &message)) + qFatal("Could not remove build dir: %s", qPrintable(message)); +} + +static bool waitForFinished(qbs::AbstractJob *job, int timeout = 0) +{ + if (job->state() == qbs::AbstractJob::StateFinished) + return true; + QEventLoop loop; + QObject::connect(job, &qbs::AbstractJob::finished, &loop, &QEventLoop::quit); + if (timeout > 0) { + QTimer timer; + QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + timer.setSingleShot(true); + timer.start(timeout); + loop.exec(); + return timer.isActive(); // Timer ended the loop <=> job did not finish. + } + loop.exec(); + return true; +} + + +TestApi::TestApi() + : m_logSink(new LogSink) + , m_sourceDataDir(QDir::cleanPath(SRCDIR "/testdata")) + , m_workingDataDir(testWorkDir(QStringLiteral("api"))) +{ +} + +TestApi::~TestApi() +{ + delete m_logSink; +} + +void TestApi::initTestCase() +{ + QString errorMessage; + qbs::Internal::removeDirectoryWithContents(m_workingDataDir, &errorMessage); + QVERIFY2(qbs::Internal::copyFileRecursion(m_sourceDataDir, + m_workingDataDir, false, true, &errorMessage), + qPrintable(errorMessage)); +} + +void TestApi::init() +{ + m_logSink->warnings.clear(); +} + +void TestApi::addQObjectMacroToCppFile() +{ + BuildDescriptionReceiver receiver; + qbs::ErrorInfo errorInfo = doBuildProject("add-qobject-macro-to-cpp-file/project.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY2(!receiver.descriptions.contains("moc"), qPrintable(receiver.descriptions)); + receiver.descriptions.clear(); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile cppFile("object.cpp"); + QVERIFY2(cppFile.open(QIODevice::ReadWrite), qPrintable(cppFile.errorString())); + QByteArray contents = cppFile.readAll(); + contents.replace("// ", ""); + cppFile.resize(0); + cppFile.write(contents); + cppFile.close(); + errorInfo = doBuildProject("add-qobject-macro-to-cpp-file/project.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY2(receiver.descriptions.contains("moc"), qPrintable(receiver.descriptions)); +} + +static bool isAboutUndefinedSymbols(const QString &_message) +{ + const QString message = _message.toLower(); + return message.contains("undefined") || message.contains("unresolved"); +} + +void TestApi::addedFilePersistent() +{ + // On the initial run, linking will fail. + const QString relProjectFilePath = "added-file-persistent/project.qbs"; + ProcessResultReceiver receiver; + qbs::ErrorInfo errorInfo = doBuildProject(relProjectFilePath, 0, &receiver); + QVERIFY(errorInfo.hasError()); + QVERIFY2(isAboutUndefinedSymbols(receiver.output), qPrintable((receiver.output))); + receiver.output.clear(); + + // Add a file. qbs must schedule it for rule application on the next build. + WAIT_FOR_NEW_TIMESTAMP(); + const qbs::SetupProjectParameters params = defaultSetupParameters(relProjectFilePath); + QFile projectFile(params.projectFilePath()); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + const QByteArray originalContent = projectFile.readAll(); + QByteArray addedFileContent = originalContent; + addedFileContent.replace("/* 'file.cpp' */", "'file.cpp'"); + projectFile.resize(0); + projectFile.write(addedFileContent); + projectFile.flush(); + QScopedPointer setupJob(qbs::Project().setupProject(params, m_logSink, + 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + setupJob.reset(0); + + // Remove the file again. qbs must unschedule the rule application again. + // Consequently, the linking step must fail as in the initial run. + WAIT_FOR_NEW_TIMESTAMP(); + projectFile.resize(0); + projectFile.write(originalContent); + projectFile.flush(); + errorInfo = doBuildProject(relProjectFilePath, 0, &receiver); + QVERIFY(errorInfo.hasError()); + QVERIFY2(isAboutUndefinedSymbols(receiver.output), qPrintable((receiver.output))); + + // Add the file again. qbs must schedule it for rule application on the next build. + WAIT_FOR_NEW_TIMESTAMP(); + projectFile.resize(0); + projectFile.write(addedFileContent); + projectFile.close(); + setupJob.reset(qbs::Project().setupProject(params, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + setupJob.reset(0); + + // qbs must remember that a file was scheduled for rule application. The build must then + // succeed, as now all necessary symbols are linked in. + errorInfo = doBuildProject(relProjectFilePath); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::baseProperties() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("base-properties/prj.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::buildGraphLocking() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("buildgraph-locking/project.qbs"); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + const qbs::Project project = setupJob->project(); + Q_UNUSED(project); + + // Case 1: Setting up a competing project from scratch. + setupJob.reset(qbs::Project().setupProject(setupParams, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY(setupJob->error().hasError()); + QVERIFY2(setupJob->error().toString().contains("lock"), + qPrintable(setupJob->error().toString())); + + // Case 2: Setting up a non-competing project and then making it competing. + qbs::SetupProjectParameters setupParams2 = setupParams; + setupParams2.setBuildRoot(setupParams.buildRoot() + "/2"); + setupJob.reset(qbs::Project().setupProject(setupParams2, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + const QString buildDirName = relativeBuildDir(setupParams2.configurationName()); + const QString lockFile = setupParams2.buildRoot() + '/' + buildDirName + '/' + buildDirName + + ".bg.lock"; + QVERIFY2(QFileInfo(lockFile).isFile(), qPrintable(lockFile)); + qbs::Project project2 = setupJob->project(); + QVERIFY(project2.isValid()); + setupJob.reset(project2.setupProject(setupParams, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY(setupJob->error().hasError()); + QVERIFY2(setupJob->error().toString().contains("lock"), + qPrintable(setupJob->error().toString())); + QVERIFY2(QFileInfo(lockFile).isFile(), qPrintable(lockFile)); + + // Case 3: Changing the build directory of an existing project to something con-competing. + qbs::SetupProjectParameters setupParams3 = setupParams2; + setupParams3.setBuildRoot(setupParams.buildRoot() + "/3"); + setupJob.reset(qbs::Project().setupProject(setupParams3, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + project2 = qbs::Project(); + QVERIFY2(!QFileInfo(lockFile).exists(), qPrintable(lockFile)); + const QString newLockFile = setupParams3.buildRoot() + '/' + buildDirName + '/' + + buildDirName + ".bg.lock"; + QVERIFY2(QFileInfo(newLockFile).isFile(), qPrintable(newLockFile)); +} + +void TestApi::buildProject() +{ + QFETCH(QString, projectSubDir); + QFETCH(QString, productFileName); + qbs::SetupProjectParameters params = defaultSetupParameters(projectSubDir + "/project.qbs"); + removeBuildDir(params); + qbs::ErrorInfo errorInfo = doBuildProject(projectSubDir + "/project.qbs"); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(regularFileExists(relativeBuildGraphFilePath())); + if (!productFileName.isEmpty()) { + QVERIFY2(regularFileExists(productFileName), qPrintable(productFileName)); + QVERIFY2(QFile::remove(productFileName), qPrintable(productFileName)); + } + + WAIT_FOR_NEW_TIMESTAMP(); + qbs::BuildOptions options; + options.setForceTimestampCheck(true); + errorInfo = doBuildProject(projectSubDir + "/project.qbs", 0, 0, 0, options); + VERIFY_NO_ERROR(errorInfo); + if (!productFileName.isEmpty()) + QVERIFY2(regularFileExists(productFileName), qPrintable(productFileName)); + QVERIFY(regularFileExists(relativeBuildGraphFilePath())); +} + +void TestApi::buildProject_data() +{ + QTest::addColumn("projectSubDir"); + QTest::addColumn("productFileName"); + QTest::newRow("BPs in Sources") + << QString("build-properties-source") + << relativeExecutableFilePath("HelloWorld"); + QTest::newRow("code generator") + << QString("codegen") + << relativeExecutableFilePath("codegen"); + QTest::newRow("link static libs") + << QString("link-static-lib") + << relativeExecutableFilePath("HelloWorld"); + QTest::newRow("precompiled header new") + << QString("precompiled-header-new") + << relativeExecutableFilePath("MyApp"); + QTest::newRow("precompiled header dynamic") + << QString("precompiled-header-dynamic") + << relativeExecutableFilePath("MyApp"); + QTest::newRow("lots of dots") + << QString("lots-of-dots") + << relativeExecutableFilePath("lots.of.dots"); + QTest::newRow("Qt5 plugin") + << QString("qt5-plugin") + << relativeProductBuildDir("echoplugin") + '/' + + qbs::Internal::HostOsInfo::dynamicLibraryName("echoplugin"); + QTest::newRow("Q_OBJECT in source") + << QString("moc-cpp") + << relativeExecutableFilePath("moc_cpp"); + QTest::newRow("Q_OBJECT in header") + << QString("moc-hpp") + << relativeExecutableFilePath("moc_hpp"); + QTest::newRow("Q_OBJECT in header, moc_XXX.cpp included") + << QString("moc-hpp-included") + << relativeExecutableFilePath("moc_hpp_included"); + QTest::newRow("app and lib with same source file") + << QString("lib-same-source") + << relativeExecutableFilePath("HelloWorldApp"); + QTest::newRow("source files with the same base name but different extensions") + << QString("same-base-name") + << relativeExecutableFilePath("basename"); + QTest::newRow("static library dependencies") + << QString("static-lib-deps") + << relativeExecutableFilePath("staticLibDeps"); + QTest::newRow("simple probes") + << QString("simple-probe") + << relativeExecutableFilePath("MyApp"); + QTest::newRow("application without sources") + << QString("app-without-sources") + << relativeExecutableFilePath("appWithoutSources"); + QTest::newRow("productNameWithDots") + << QString("productNameWithDots") + << relativeExecutableFilePath("myapp"); + QTest::newRow("only default properties") + << QString("two-default-property-values") + << relativeProductBuildDir("two-default-property-values") + "/set"; + QTest::newRow("Export item with Group") + << QString("export-item-with-group") + << relativeExecutableFilePath("app"); + QTest::newRow("QBS-728") + << QString("QBS-728") + << QString(); +} + +void TestApi::buildProjectDryRun() +{ + QFETCH(QString, projectSubDir); + QFETCH(QString, productFileName); + qbs::SetupProjectParameters params = defaultSetupParameters(projectSubDir + "/project.qbs"); + removeBuildDir(params); + qbs::BuildOptions options; + options.setDryRun(true); + const qbs::ErrorInfo errorInfo + = doBuildProject(projectSubDir + "/project.qbs", 0, 0, 0, options); + VERIFY_NO_ERROR(errorInfo); + QVERIFY2(!QFileInfo::exists(relativeBuildDir()), qPrintable(QDir(relativeBuildDir()) + .entryList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System).join(", "))); +} + +void TestApi::buildProjectDryRun_data() +{ + return buildProject_data(); +} + +void TestApi::buildSingleFile() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("build-single-file/project.qbs"); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + qbs::Project project = setupJob->project(); + qbs::BuildOptions options; + options.setFilesToConsider(QStringList(setupParams.buildRoot() + "/compiled.cpp")); + options.setActiveFileTags(QStringList("obj")); + m_logSink->setLogLevel(qbs::LoggerMaxLevel); + QScopedPointer buildJob(project.buildAllProducts(options)); + BuildDescriptionReceiver receiver; + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, &receiver, + &BuildDescriptionReceiver::handleDescription); + waitForFinished(buildJob.data()); + QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); + QCOMPARE(receiver.descriptions.count("compiling"), 1); + QVERIFY2(receiver.descriptions.contains("compiling compiled.cpp"), + qPrintable(receiver.descriptions)); +} + +void TestApi::canonicalToolchainList() +{ + // All the known toolchain lists should be equal + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "clang", "llvm", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "llvm", "gcc"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "gcc"})), + QStringList({"llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"mingw", "gcc"})), + QStringList({"mingw", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc"})), + QStringList({"gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"msvc"})), + QStringList({"msvc"})); + + // Single names should canonicalize to the known lists + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm"})), + QStringList({"llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"mingw"})), + QStringList({"mingw", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc"})), + QStringList({"gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"msvc"})), + QStringList({"msvc"})); + + // Missing some in the middle + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "llvm", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "clang", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "llvm"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "gcc"})), + QStringList({"clang", "llvm", "gcc"})); + + // Sorted wrong, missing some in the middle + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "llvm", "clang", "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "gcc", "llvm", "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "clang", "xcode", "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "llvm", "clang"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "clang", "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "llvm"})), + QStringList({"llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "mingw"})), + QStringList({"mingw", "gcc"})); + + // Duplicates + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "llvm", "clang", "xcode", "xcode", + "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "gcc", "llvm", "clang", "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "clang", "clang", "xcode", "xcode", + "gcc"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "clang", "gcc", "llvm", "clang"})), + QStringList({"clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"xcode", "gcc", "clang", "gcc", "clang", + "xcode"})), + QStringList({"xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "gcc", "llvm", "llvm"})), + QStringList({"llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain(QStringList({"gcc", "gcc", "gcc", "mingw"})), + QStringList({"mingw", "gcc"})); + + // Custom insanity + QCOMPARE(qbs::canonicalToolchain( + QStringList({"crazy", "gcc", "llvm", "clang", "xcode", "insane"})), + QStringList({"crazy", "insane", "xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain( + QStringList({"crazy", "gcc", "llvm", "clang", "xcode", "insane", "crazy"})), + QStringList({"crazy", "insane", "xcode", "clang", "llvm", "gcc"})); + QCOMPARE(qbs::canonicalToolchain( + QStringList({"crazy", "insane", "gcc", "trade", "llvm", "clang", "xcode", + "insane", "mark", "crazy"})), + QStringList({"crazy", "insane", "trade", "mark", "xcode", "clang", "llvm", "gcc"})); +} + +void TestApi::checkOutputs() +{ + QFETCH(bool, check); + qbs::SetupProjectParameters params = defaultSetupParameters("/check-outputs/project.qbs"); + qbs::BuildOptions options; + options.setForceOutputCheck(check); + removeBuildDir(params); + qbs::ErrorInfo errorInfo = doBuildProject("/check-outputs/project.qbs", 0, 0, 0, options); + if (check) + QVERIFY(errorInfo.hasError()); + else + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::checkOutputs_data() +{ + QTest::addColumn("check"); + QTest::newRow("checked outputs") << true; + QTest::newRow("unchecked outputs") << false; +} + +qbs::GroupData findGroup(const qbs::ProductData &product, const QString &name) +{ + foreach (const qbs::GroupData &g, product.groups()) { + if (g.name() == name) + return g; + } + return qbs::GroupData(); +} + +#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES + +static qbs::Project::ProductSelection defaultProducts() +{ + return qbs::Project::ProductSelectionDefaultOnly; +} + +static void printProjectData(const qbs::ProjectData &project) +{ + foreach (const qbs::ProductData &p, project.products()) { + qDebug(" Product '%s' at %s", qPrintable(p.name()), qPrintable(p.location().toString())); + foreach (const qbs::GroupData &g, p.groups()) { + qDebug(" Group '%s' at %s", qPrintable(g.name()), qPrintable(g.location().toString())); + qDebug(" Files: %s", qPrintable(g.allFilePaths().join(QLatin1String(", ")))); + } + } +} + +void TestApi::changeContent() +{ + qbs::SetupProjectParameters setupParams = defaultSetupParameters("project-editing/project.qbs"); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + qbs::Project project = job->project(); + qbs::ProjectData projectData = project.projectData(); + QCOMPARE(projectData.allProducts().count(), 1); + qbs::ProductData product = projectData.allProducts().first(); + QVERIFY(product.groups().count() >= 8); + + // Error handling: Invalid product. + qbs::ErrorInfo errorInfo = project.addGroup(qbs::ProductData(), "blubb"); + QVERIFY(errorInfo.hasError()); + QVERIFY(errorInfo.toString().contains("invalid")); + + // Error handling: Empty group name. + errorInfo = project.addGroup(product, QString()); + QVERIFY(errorInfo.hasError()); + QVERIFY(errorInfo.toString().contains("empty")); + + errorInfo = project.addGroup(product, "New Group 1"); + VERIFY_NO_ERROR(errorInfo); + + errorInfo = project.addGroup(product, "New Group 2"); + VERIFY_NO_ERROR(errorInfo); + + // Error handling: Group already inserted. + errorInfo = project.addGroup(product, "New Group 1"); + QVERIFY(errorInfo.hasError()); + QVERIFY(errorInfo.toString().contains("already")); + + // Error handling: Add list of files with double entries. + errorInfo = project.addFiles(product, qbs::GroupData(), QStringList() << "file.cpp" + << "file.cpp"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("more than once"), qPrintable(errorInfo.toString())); + + // Add files to empty array literal. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + QVERIFY(product.groups().count() >= 10); + qbs::GroupData group = findGroup(product, "New Group 1"); + QVERIFY(group.isValid()); + errorInfo = project.addFiles(product, group, QStringList() << "file.h" << "file.cpp"); + VERIFY_NO_ERROR(errorInfo); + + // Error handling: Add the same file again. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + QVERIFY(product.groups().count() >= 10); + group = findGroup(product, "New Group 1"); + QVERIFY(group.isValid()); + errorInfo = project.addFiles(product, group, QStringList() << "file.cpp"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("already"), qPrintable(errorInfo.toString())); + + // Remove one of the newly added files again. + errorInfo = project.removeFiles(product, group, QStringList("file.h")); + VERIFY_NO_ERROR(errorInfo); + + // Error handling: Try to remove the same file again. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + QVERIFY(product.groups().count() >= 10); + group = findGroup(product, "New Group 1"); + QVERIFY(group.isValid()); + errorInfo = project.removeFiles(product, group, QStringList() << "file.h"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("not known"), qPrintable(errorInfo.toString())); + + // Error handling: Try to remove a file from a complex list. + group = findGroup(product, "Existing Group 2"); + QVERIFY(group.isValid()); + errorInfo = project.removeFiles(product, group, QStringList() << "existingfile2.txt"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("complex"), qPrintable(errorInfo.toString())); + + // Remove file from product's 'files' binding. + errorInfo = project.removeFiles(product, qbs::GroupData(), QStringList("main.cpp")); + VERIFY_NO_ERROR(errorInfo); + + // Add file to non-empty array literal. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + group = findGroup(product, "Existing Group 1"); + QVERIFY(group.isValid()); + errorInfo = project.addFiles(product, group, QStringList() << "newfile1.txt"); + VERIFY_NO_ERROR(errorInfo); + + // Add files to list represented as a single string. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + errorInfo = project.addFiles(product, qbs::GroupData(), QStringList() << "newfile2.txt"); + VERIFY_NO_ERROR(errorInfo); + + // Add files to list represented as an identifier. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + group = findGroup(product, "Existing Group 2"); + QVERIFY(group.isValid()); + errorInfo = project.addFiles(product, group, QStringList() << "newfile3.txt"); + VERIFY_NO_ERROR(errorInfo); + + // Add files to list represented as a block of code (not yet implemented). + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + group = findGroup(product, "Existing Group 3"); + QVERIFY(group.isValid()); + errorInfo = project.addFiles(product, group, QStringList() << "newfile4.txt"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("complex"), qPrintable(errorInfo.toString())); + + // Add file to group with directory prefix. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + group = findGroup(product, "Existing Group 4"); + QVERIFY(group.isValid()); + errorInfo = project.addFiles(product, group, QStringList() << "file.txt"); + VERIFY_NO_ERROR(errorInfo); + + // Error handling: Add file to group with non-directory prefix. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + group = findGroup(product, "Existing Group 5"); + QVERIFY(group.isValid()); + errorInfo = project.addFiles(product, group, QStringList() << "newfile1.txt"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("prefix"), qPrintable(errorInfo.toString())); + + // Remove group. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + group = findGroup(product, "Existing Group 5"); + QVERIFY(group.isValid()); + errorInfo = project.removeGroup(product, group); + VERIFY_NO_ERROR(errorInfo); + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + QVERIFY(projectData.products().first().groups().count() >= 9); + + // Error handling: Try to remove the same group again. + errorInfo = project.removeGroup(product, group); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("does not exist"), qPrintable(errorInfo.toString())); + + // Add a file to a group where the file name is already matched by a wildcard. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + group = findGroup(product, "Group with wildcards"); + QVERIFY(group.isValid()); + QFile newFile("koerper.klaus"); + QVERIFY2(newFile.open(QIODevice::WriteOnly), qPrintable(newFile.errorString())); + newFile.close(); + errorInfo = project.addFiles(product, group, QStringList() << newFile.fileName()); + VERIFY_NO_ERROR(errorInfo); + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + group = findGroup(product, "Group with wildcards"); + QVERIFY(group.isValid()); + QCOMPARE(group.sourceArtifactsFromWildcards().count(), 1); + QCOMPARE(group.sourceArtifactsFromWildcards().first().filePath(), + QFileInfo(newFile).absoluteFilePath()); + + // Error checking: Try to remove a file that originates from a wildcard pattern. + projectData = project.projectData(); + QVERIFY(projectData.products().count() == 1); + product = projectData.products().first(); + group = findGroup(product, "Other group with wildcards"); + QVERIFY(group.isValid()); + errorInfo = project.removeFiles(product, group, QStringList() << "test.wildcard"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("pattern"), qPrintable(errorInfo.toString())); + + // Check whether building will take the added and removed cpp files into account. + // This must not be moved below the re-resolving test!!! + qbs::BuildOptions buildOptions; + buildOptions.setDryRun(true); + BuildDescriptionReceiver rcvr; + QScopedPointer buildJob(project.buildAllProducts(buildOptions, defaultProducts(), + this)); + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, + &rcvr, &BuildDescriptionReceiver::handleDescription); + waitForFinished(buildJob.data()); + QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); + QVERIFY(rcvr.descriptions.contains("compiling file.cpp")); + QVERIFY(!rcvr.descriptions.contains("compiling main.cpp")); + + // Now check whether the data updates were done correctly. + projectData = project.projectData(); + job.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + project = job->project(); + qbs::ProjectData newProjectData = project.projectData(); + + // Can't use Project::operator== here, as the target artifacts will differ due to the build + // not having run yet. + bool projectDataMatches = newProjectData.products().count() == 1 + && projectData.products().count() == 1 + && newProjectData.products().first().groups() == projectData.products().first().groups(); + if (!projectDataMatches) { + qDebug("This is the assumed project:"); + printProjectData(projectData); + qDebug("This is the actual project:"); + printProjectData(newProjectData); + } + QVERIFY(projectDataMatches); // Will fail if e.g. code locations don't match. + + // Now try building again and check if the newly resolved product behaves the same way. + buildJob.reset(project.buildAllProducts(buildOptions, defaultProducts(), this)); + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, + &rcvr, &BuildDescriptionReceiver::handleDescription); + waitForFinished(buildJob.data()); + QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); + QVERIFY(rcvr.descriptions.contains("compiling file.cpp")); + QVERIFY(!rcvr.descriptions.contains("compiling main.cpp")); + + // Now, after the build, the project data must be entirely identical. + QVERIFY(projectData == project.projectData()); + + // Error handling: Try to change the project during a build. + buildJob.reset(project.buildAllProducts(buildOptions, defaultProducts(), this)); + errorInfo = project.addGroup(newProjectData.products().first(), "blubb"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("in process"), qPrintable(errorInfo.toString())); + waitForFinished(buildJob.data()); + errorInfo = project.addGroup(newProjectData.products().first(), "blubb"); + VERIFY_NO_ERROR(errorInfo); + + project = qbs::Project(); + job.reset(0); + buildJob.reset(0); + removeBuildDir(setupParams); + // Add a file to the top level of a product that does not have a "files" binding yet. + setupParams.setProjectFilePath(QDir::cleanPath(m_workingDataDir + + "/project-editing/project-with-no-files.qbs")); + + job.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + project = job->project(); + projectData = project.projectData(); + QCOMPARE(projectData.allProducts().count(), 1); + product = projectData.allProducts().first(); + errorInfo = project.addFiles(product, qbs::GroupData(), QStringList("main.cpp")); + VERIFY_NO_ERROR(errorInfo); + projectData = project.projectData(); + rcvr.descriptions.clear(); + buildJob.reset(project.buildAllProducts(buildOptions, defaultProducts(), this)); + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, + &rcvr, &BuildDescriptionReceiver::handleDescription); + waitForFinished(buildJob.data()); + QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); + QVERIFY(rcvr.descriptions.contains("compiling main.cpp")); + job.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + // Can't use Project::operator== here, as the target artifacts will differ due to the build + // not having run yet. + newProjectData = job->project().projectData(); + projectDataMatches = newProjectData.products().count() == 1 + && projectData.products().count() == 1 + && newProjectData.products().first().groups() == projectData.products().first().groups(); + if (!projectDataMatches) { + printProjectData(projectData); + qDebug("\n====\n"); + printProjectData(newProjectData); + } + QVERIFY(projectDataMatches); +} + +#endif // QBS_ENABLE_PROJECT_FILE_UPDATES + +void TestApi::commandExtraction() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("/command-extraction/project.qbs"); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + qbs::Project project = setupJob->project(); + qbs::ProjectData projectData = project.projectData(); + QCOMPARE(projectData.allProducts().count(), 1); + qbs::ProductData productData = projectData.allProducts().first(); + qbs::ErrorInfo errorInfo; + const QString projectDirPath = QDir::cleanPath(QFileInfo(setupParams.projectFilePath()).path()); + const QString sourceFilePath = projectDirPath + "/main.cpp"; + + // Before the first build, no rules exist. + qbs::RuleCommandList commands + = project.ruleCommands(productData, sourceFilePath, "obj", &errorInfo); + QCOMPARE(commands.count(), 0); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("No rule"), qPrintable(errorInfo.toString())); + + qbs::BuildOptions options; + options.setDryRun(true); + QScopedPointer buildJob(project.buildAllProducts(options)); + waitForFinished(buildJob.data()); + QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); + projectData = project.projectData(); + QCOMPARE(projectData.allProducts().count(), 1); + productData = projectData.allProducts().first(); + errorInfo = qbs::ErrorInfo(); + + // After the build, the compile command must be found. + commands = project.ruleCommands(productData, sourceFilePath, "obj", &errorInfo); + QCOMPARE(commands.count(), 1); + QVERIFY2(!errorInfo.hasError(), qPrintable(errorInfo.toString())); + const qbs::RuleCommand command = commands.first(); + QCOMPARE(command.type(), qbs::RuleCommand::ProcessCommandType); + QVERIFY(!command.executable().isEmpty()); + QVERIFY(!command.arguments().isEmpty()); +} + +void TestApi::changeDependentLib() +{ + qbs::ErrorInfo errorInfo = doBuildProject("change-dependent-lib/change-dependent-lib.qbs"); + VERIFY_NO_ERROR(errorInfo); + + WAIT_FOR_NEW_TIMESTAMP(); + const QString qbsFileName("change-dependent-lib.qbs"); + QFile qbsFile(qbsFileName); + QVERIFY(qbsFile.open(QIODevice::ReadWrite)); + const QByteArray content1 = qbsFile.readAll(); + QByteArray content2 = content1; + content2.replace("cpp.defines: [\"XXXX\"]", "cpp.defines: [\"ABCD\"]"); + QVERIFY(content1 != content2); + qbsFile.seek(0); + qbsFile.write(content2); + qbsFile.close(); + errorInfo = doBuildProject("change-dependent-lib/change-dependent-lib.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::enableAndDisableProduct() +{ + BuildDescriptionReceiver bdr; + qbs::ErrorInfo errorInfo = doBuildProject("enable-and-disable-product/project.qbs", &bdr); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(!bdr.descriptions.contains("compiling")); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile projectFile("project.qbs"); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + QByteArray content = projectFile.readAll(); + content.replace("undefined", "'hidden'"); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + bdr.descriptions.clear(); + errorInfo = doBuildProject("enable-and-disable-product/project.qbs", &bdr); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(bdr.descriptions.contains("linking")); + + WAIT_FOR_NEW_TIMESTAMP(); + touch("main.cpp"); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + content = projectFile.readAll(); + content.replace("'hidden'", "undefined"); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + bdr.descriptions.clear(); + errorInfo = doBuildProject("enable-and-disable-product/project.qbs", &bdr); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(!bdr.descriptions.contains("compiling")); +} + +void TestApi::errorInSetupRunEnvironment() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("error-in-setup-run-environment/" + "error-in-setup-run-environment.qbs"); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + const qbs::Project project = job->project(); + QVERIFY(project.isValid()); + QCOMPARE(project.projectData().products().count(), 1); + const qbs::ProductData product = project.projectData().products().first(); + + bool exceptionCaught = false; + try { + qbs::Settings settings((QString())); + qbs::RunEnvironment runEnv = project.getRunEnvironment(product, qbs::InstallOptions(), + QProcessEnvironment(), &settings); + qbs::ErrorInfo error; + const QProcessEnvironment env = runEnv.runEnvironment(&error); + QVERIFY(error.hasError()); + QVERIFY(error.toString().contains("trallala")); + } catch (const qbs::ErrorInfo &) { + exceptionCaught = true; + } + QVERIFY(!exceptionCaught); +} + +static qbs::ErrorInfo forceRuleEvaluation(const qbs::Project project) +{ + qbs::BuildOptions buildOptions; + buildOptions.setDryRun(true); + QScopedPointer buildJob(project.buildAllProducts(buildOptions)); + waitForFinished(buildJob.data()); + return buildJob->error(); +} + +void TestApi::disabledInstallGroup() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("disabled_install_group/project.qbs"); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + const qbs::Project project = job->project(); + + const qbs::ErrorInfo errorInfo = forceRuleEvaluation(project); + VERIFY_NO_ERROR(errorInfo); + + qbs::ProjectData projectData = project.projectData(); + QCOMPARE(projectData.allProducts().count(), 1); + qbs::ProductData product = projectData.allProducts().first(); + const QList targets = product.targetArtifacts(); + QCOMPARE(targets.count(), 1); + QVERIFY(targets.first().isGenerated()); + QVERIFY(targets.first().isExecutable()); + QVERIFY(targets.first().isTargetArtifact()); + QCOMPARE(projectData.installableArtifacts().count(), 0); + QCOMPARE(product.targetExecutable(), targets.first().filePath()); +} + +void TestApi::disabledProduct() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("disabled-product/disabledProduct.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::disabledProject() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("disabled-project/disabled_project.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::duplicateProductNames() +{ + QFETCH(QString, projectFileName); + const qbs::ErrorInfo errorInfo = doBuildProject("duplicate-product-names/" + projectFileName); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("Duplicate product name"), + qPrintable(errorInfo.toString())); +} + +void TestApi::duplicateProductNames_data() +{ + QTest::addColumn("projectFileName"); + QTest::newRow("Names explicitly set") << QString("explicit.qbs"); + QTest::newRow("Unnamed products in same file") << QString("implicit.qbs"); + QTest::newRow("Unnamed products in files of the same name") << QString("implicit-indirect.qbs"); + +} + +void TestApi::dynamicLibs() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("dynamic-libs/link_dynamiclib.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::emptyFileTagList() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("empty-filetag-list/project.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::emptySubmodulesList() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("empty-submodules-list/project.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::explicitlyDependsOn() +{ + BuildDescriptionReceiver receiver; + qbs::ErrorInfo errorInfo = doBuildProject("explicitly-depends-on/project.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(receiver.descriptions.contains("Creating output artifact")); + receiver.descriptions.clear(); + + errorInfo = doBuildProject("explicitly-depends-on/project.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(!receiver.descriptions.contains("Creating output artifact")); + + WAIT_FOR_NEW_TIMESTAMP(); + touch("dependency.txt"); + errorInfo = doBuildProject("explicitly-depends-on/project.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(receiver.descriptions.contains("Creating output artifact")); +} + +void TestApi::exportSimple() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("export-simple/project.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::exportWithRecursiveDepends() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("export-with-recursive-depends/project.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::fileTagger() +{ + BuildDescriptionReceiver receiver; + const qbs::ErrorInfo errorInfo = doBuildProject("file-tagger/moc_cpp.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY2(receiver.descriptions.contains("moc bla.cpp"), qPrintable(receiver.descriptions)); +} + +void TestApi::fileTagsFilterOverride() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("filetagsfilter_override/project.qbs"); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + qbs::Project project = job->project(); + + const qbs::ErrorInfo errorInfo = forceRuleEvaluation(project); + VERIFY_NO_ERROR(errorInfo); + + qbs::ProjectData projectData = project.projectData(); + QCOMPARE(projectData.allProducts().count(), 1); + const qbs::ProductData product = projectData.allProducts().first(); + QList installableFiles = product.installableArtifacts(); + QCOMPARE(installableFiles.count(), 1); + QVERIFY(installableFiles.first().installData().installFilePath().contains("habicht")); +} + +void TestApi::generatedFilesList() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("generated-files-list/generated-files-list.qbs"); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + QVERIFY(waitForFinished(setupJob.data())); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + qbs::Project project = setupJob->project(); + qbs::BuildOptions options; + options.setExecuteRulesOnly(true); + const QScopedPointer buildJob(project.buildAllProducts(options)); + QVERIFY(waitForFinished(buildJob.data())); + VERIFY_NO_ERROR(buildJob->error()); + const qbs::ProjectData projectData = project.projectData(); + QCOMPARE(projectData.products().count(), 1); + const qbs::ProductData product = projectData.products().first(); + QString uiFilePath; + QVERIFY(product.generatedArtifacts().count() >= 6); + foreach (const qbs::ArtifactData &a, product.generatedArtifacts()) { + QVERIFY(a.isGenerated()); + QFileInfo fi(a.filePath()); + using qbs::Internal::HostOsInfo; + const QStringList possibleFileNames = QStringList() + << "main.cpp.o" << "main.cpp.obj" + << "mainwindow.cpp.o" << "mainwindow.cpp.obj" + << "moc_mainwindow.cpp" << "moc_mainwindow.cpp.o" << "moc_mainwindow.cpp.obj" + << "ui_mainwindow.h" + << HostOsInfo::appendExecutableSuffix("generated-files-list"); + QVERIFY2(possibleFileNames.contains(fi.fileName()) || fi.fileName().endsWith(".plist"), + qPrintable(fi.fileName())); + } + foreach (const qbs::GroupData &group, product.groups()) { + foreach (const qbs::ArtifactData &a, group.sourceArtifacts()) { + QVERIFY(!a.isGenerated()); + QVERIFY(!a.isTargetArtifact()); + if (a.fileTags().contains(QLatin1String("ui"))) { + uiFilePath = a.filePath(); + break; + } + } + if (!uiFilePath.isEmpty()) + break; + } + QVERIFY(!uiFilePath.isEmpty()); + const QStringList directParents = project.generatedFiles(product, uiFilePath, false); + QCOMPARE(directParents.count(), 1); + const QFileInfo uiHeaderFileInfo(directParents.first()); + QCOMPARE(uiHeaderFileInfo.fileName(), QLatin1String("ui_mainwindow.h")); + QVERIFY(!uiHeaderFileInfo.exists()); + const QStringList allParents = project.generatedFiles(product, uiFilePath, true); + QCOMPARE(allParents.count(), 3); +} + +void TestApi::infiniteLoopBuilding() +{ + QFETCH(QString, projectDirName); + qbs::SetupProjectParameters setupParams + = defaultSetupParameters(projectDirName + "/infinite-loop.qbs"); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + qbs::Project project = setupJob->project(); + const QScopedPointer buildJob(project.buildAllProducts(qbs::BuildOptions())); + QTimer::singleShot(1000, buildJob.data(), &qbs::AbstractJob::cancel); + QVERIFY(waitForFinished(buildJob.data(), 600000)); +} + +void TestApi::infiniteLoopBuilding_data() +{ + QTest::addColumn("projectDirName"); + QTest::newRow("JS Command") << QString("infinite-loop-js"); + QTest::newRow("Process Command") << QString("infinite-loop-process"); +} + +void TestApi::infiniteLoopResolving() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("infinite-loop-resolving/project.qbs"); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + QTimer::singleShot(1000, setupJob.data(), &qbs::AbstractJob::cancel); + QVERIFY(waitForFinished(setupJob.data(), 600000)); + QVERIFY2(setupJob->error().toString().toLower().contains("cancel"), + qPrintable(setupJob->error().toString())); +} + +void TestApi::inheritQbsSearchPaths() +{ + const QString projectFilePath = "inherit-qbs-search-paths/prj.qbs"; + qbs::ErrorInfo errorInfo = doBuildProject(projectFilePath); + VERIFY_NO_ERROR(errorInfo); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile projectFile(m_workingDataDir + '/' + projectFilePath); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + QByteArray content = projectFile.readAll(); + content.replace("qbsSearchPaths: \"subdir\"", "//qbsSearchPaths: \"subdir\""); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + errorInfo = doBuildProject(projectFilePath); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("Dependency 'bli' not found"), + qPrintable(errorInfo.toString())); + + QVariantMap overriddenValues; + overriddenValues.insert("project.qbsSearchPaths", QStringList() << "subdir"); + errorInfo = doBuildProject(projectFilePath, 0, 0, 0, qbs::BuildOptions(), overriddenValues); + VERIFY_NO_ERROR(errorInfo); +} + +template T findElem(const QList &list, Pred p) +{ + const auto it = std::find_if(list.constBegin(), list.constEnd(), p); + return it == list.constEnd() ? T() : *it; +} + +void TestApi::installableFiles() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("installed-artifact/installed_artifact.qbs"); + QVariantMap overriddenValues; + overriddenValues.insert(QLatin1String("qbs.installRoot"), QLatin1String("/tmp")); + setupParams.setOverriddenValues(overriddenValues); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + qbs::Project project = job->project(); + + const qbs::ErrorInfo errorInfo = forceRuleEvaluation(project); + VERIFY_NO_ERROR(errorInfo); + + qbs::ProjectData projectData = project.projectData(); + QCOMPARE(projectData.allProducts().count(), 2); + qbs::ProductData product = findElem(projectData.allProducts(), [](const qbs::ProductData &p) { + return p.name() == QLatin1String("installedApp"); + }); + QVERIFY(product.isValid()); + QList installableFiles = product.installableArtifacts(); + QCOMPARE(installableFiles.count(), 2); + foreach (const qbs::ArtifactData &f,installableFiles) { + if (!f.filePath().endsWith("main.cpp")) { + QVERIFY(f.isExecutable()); + QString expectedTargetFilePath = qbs::Internal::HostOsInfo + ::appendExecutableSuffix(QLatin1String("/tmp/usr/bin/installedApp")); + QCOMPARE(f.installData().localInstallFilePath(), expectedTargetFilePath); + QCOMPARE(product.targetExecutable(), expectedTargetFilePath); + break; + } + } + + setupParams = defaultSetupParameters("recursive-wildcards/recursive_wildcards.qbs"); + setupParams.setOverriddenValues(overriddenValues); + job.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + project = job->project(); + projectData = project.projectData(); + QCOMPARE(projectData.allProducts().count(), 1); + product = projectData.allProducts().first(); + installableFiles = product.installableArtifacts(); + QCOMPARE(installableFiles.count(), 2); + foreach (const qbs::ArtifactData &f, installableFiles) + QVERIFY(!f.isExecutable()); + QCOMPARE(installableFiles.first().installData().localInstallFilePath(), + QLatin1String("/tmp/dir/file1.txt")); + QCOMPARE(installableFiles.last().installData().localInstallFilePath(), + QLatin1String("/tmp/dir/file2.txt")); +} + +void TestApi::isRunnable() +{ + qbs::SetupProjectParameters setupParams = defaultSetupParameters("is-runnable/project.qbs"); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + qbs::Project project = job->project(); + const QList products = project.projectData().products(); + QCOMPARE(products.count(), 2); + foreach (const qbs::ProductData &p, products) { + QVERIFY2(p.name() == "app" || p.name() == "lib", qPrintable(p.name())); + if (p.name() == "app") + QVERIFY(p.isRunnable()); + else + QVERIFY(!p.isRunnable()); + } +} + +void TestApi::listBuildSystemFiles() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("subprojects/toplevelproject.qbs"); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + const QSet buildSystemFiles = job->project().buildSystemFiles(); + QVERIFY(buildSystemFiles.contains(setupParams.projectFilePath())); + QVERIFY(buildSystemFiles.contains(setupParams.buildRoot() + "/subproject2/subproject2.qbs")); + QVERIFY(buildSystemFiles.contains(setupParams.buildRoot() + + "/subproject2/subproject3/subproject3.qbs")); +} + +void TestApi::mocCppIncluded() +{ + // Initial build. + qbs::ErrorInfo errorInfo = doBuildProject("moc-hpp-included/project.qbs"); + VERIFY_NO_ERROR(errorInfo); + + // Touch header and try again. + WAIT_FOR_NEW_TIMESTAMP(); + QFile headerFile("object.h"); + QVERIFY2(headerFile.open(QIODevice::WriteOnly | QIODevice::Append), + qPrintable(headerFile.errorString())); + headerFile.write("\n"); + headerFile.close(); + errorInfo = doBuildProject("moc-hpp-included/project.qbs"); + VERIFY_NO_ERROR(errorInfo); + + // Touch cpp file and try again. + WAIT_FOR_NEW_TIMESTAMP(); + QFile cppFile("object.cpp"); + QVERIFY2(cppFile.open(QIODevice::WriteOnly | QIODevice::Append), + qPrintable(cppFile.errorString())); + cppFile.write("\n"); + cppFile.close(); + errorInfo = doBuildProject("moc-hpp-included/project.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::multiArch() +{ + qbs::SetupProjectParameters setupParams = defaultSetupParameters("multi-arch/project.qbs"); + qbs::Settings settings((QString())); + qbs::Internal::TemporaryProfile tph("host", &settings); + qbs::Profile hostProfile = tph.p; + hostProfile.setValue("qbs.architecture", "host-arch"); + qbs::Internal::TemporaryProfile tpt("target", &settings); + qbs::Profile targetProfile = tpt.p; + targetProfile.setValue("qbs.architecture", "target-arch"); + QVariantMap overriddenValues; + overriddenValues.insert("project.hostProfile", hostProfile.name()); + overriddenValues.insert("project.targetProfile", targetProfile.name()); + setupParams.setOverriddenValues(overriddenValues); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + qbs::Project project = setupJob->project(); + QCOMPARE(project.profile(), profileName()); + const QList &products = project.projectData().products(); + QCOMPARE(products.count(), 3); + QList hostProducts; + QList targetProducts; + foreach (const qbs::ProductData &p, products) { + QVERIFY2(p.profile() == hostProfile.name() || p.profile() == targetProfile.name(), + qPrintable(p.profile())); + if (p.profile() == hostProfile.name()) + hostProducts << p; + else + targetProducts << p; + } + QCOMPARE(hostProducts.count(), 2); + QCOMPARE(targetProducts.count(), 1); + QCOMPARE(targetProducts.first().name(), QLatin1String("p1")); + QStringList hostProductNames + = QStringList() << hostProducts.first().name() << hostProducts.last().name(); + QCOMPARE(hostProductNames.count("p1"), 1); + QCOMPARE(hostProductNames.count("p2"), 1); + + QScopedPointer buildJob(project.buildAllProducts(qbs::BuildOptions())); + waitForFinished(buildJob.data()); + QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); + const QString outputBaseDir = setupParams.buildRoot() + '/'; + QFile p1HostArtifact(outputBaseDir + + relativeProductBuildDir("p1", "host") + "/host+target.output"); + QVERIFY2(p1HostArtifact.exists(), qPrintable(p1HostArtifact.fileName())); + QVERIFY2(p1HostArtifact.open(QIODevice::ReadOnly), qPrintable(p1HostArtifact.errorString())); + QCOMPARE(p1HostArtifact.readAll().constData(), "host-arch"); + QFile p1TargetArtifact(outputBaseDir + relativeProductBuildDir("p1", "target") + + "/host+target.output"); + QVERIFY2(p1TargetArtifact.exists(), qPrintable(p1TargetArtifact.fileName())); + QVERIFY2(p1TargetArtifact.open(QIODevice::ReadOnly), qPrintable(p1TargetArtifact.errorString())); + QCOMPARE(p1TargetArtifact.readAll().constData(), "target-arch"); + QFile p2Artifact(outputBaseDir + relativeProductBuildDir("p2", "host") + "/host-tool.output"); + QVERIFY2(p2Artifact.exists(), qPrintable(p2Artifact.fileName())); + QVERIFY2(p2Artifact.open(QIODevice::ReadOnly), qPrintable(p2Artifact.errorString())); + QCOMPARE(p2Artifact.readAll().constData(), "host-arch"); + + const QString installRoot = outputBaseDir + relativeBuildDir() + '/' + + qbs::InstallOptions::defaultInstallRoot(); + QScopedPointer installJob(project.installAllProducts(qbs::InstallOptions())); + waitForFinished(installJob.data()); + QVERIFY2(!installJob->error().hasError(), qPrintable(installJob->error().toString())); + QFile p1HostArtifactInstalled(installRoot + "/host/host+target.output"); + QVERIFY2(p1HostArtifactInstalled.exists(), qPrintable(p1HostArtifactInstalled.fileName())); + QFile p1TargetArtifactInstalled(installRoot + "/target/host+target.output"); + QVERIFY2(p1TargetArtifactInstalled.exists(), qPrintable(p1TargetArtifactInstalled.fileName())); + QFile p2ArtifactInstalled(installRoot + "/host/host-tool.output"); + QVERIFY2(p2ArtifactInstalled.exists(), qPrintable(p2ArtifactInstalled.fileName())); + + // Error check: Try to build for the same profile twice. + overriddenValues.insert("project.targetProfile", hostProfile.name()); + setupParams.setOverriddenValues(overriddenValues); + setupJob.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY(setupJob->error().hasError()); + QVERIFY2(setupJob->error().toString().contains(hostProfile.name()) + && setupJob->error().toString().contains("not allowed"), + qPrintable(setupJob->error().toString())); + + // Error check: Try to build for the same profile twice, this time attaching + // the properties via the product name. + overriddenValues.clear(); + overriddenValues.insert("p1.profiles", targetProfile.name() + ',' + targetProfile.name()); + setupParams.setOverriddenValues(overriddenValues); + setupJob.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY(setupJob->error().hasError()); + QVERIFY2(setupJob->error().toString().contains(targetProfile.name()) + && setupJob->error().toString().contains("not allowed"), + qPrintable(setupJob->error().toString())); +} + +void TestApi::newOutputArtifactInDependency() +{ + BuildDescriptionReceiver receiver; + qbs::ErrorInfo errorInfo + = doBuildProject("new-output-artifact-in-dependency/project.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(receiver.descriptions.contains("linking app")); + const QByteArray linkingLibString = QByteArray("linking ") + + qbs::Internal::HostOsInfo::dynamicLibraryName("lib").toLatin1(); + QVERIFY(!receiver.descriptions.contains(linkingLibString)); + receiver.descriptions.clear(); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile projectFile("project.qbs"); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + QByteArray contents = projectFile.readAll(); + contents.replace("//Depends", "Depends"); + projectFile.resize(0); + projectFile.write(contents); + projectFile.close(); + errorInfo = doBuildProject("new-output-artifact-in-dependency/project.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(receiver.descriptions.contains("linking app")); + QVERIFY(receiver.descriptions.contains(linkingLibString)); +} + +void TestApi::newPatternMatch() +{ + TaskReceiver receiver; + qbs::ErrorInfo errorInfo = doBuildProject("new-pattern-match/project.qbs", 0, 0, &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY2(receiver.taskDescriptions.contains("Resolving"), qPrintable(m_logSink->output)); + receiver.taskDescriptions.clear(); + + errorInfo = doBuildProject("new-pattern-match/project.qbs", 0, 0, &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(!receiver.taskDescriptions.contains("Resolving")); + + QFile f("test.txt"); + QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString())); + f.close(); + errorInfo = doBuildProject("new-pattern-match/project.qbs", 0, 0, &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(receiver.taskDescriptions.contains("Resolving")); + receiver.taskDescriptions.clear(); + + errorInfo = doBuildProject("new-pattern-match/project.qbs", 0, 0, &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(!receiver.taskDescriptions.contains("Resolving")); + + f.remove(); + errorInfo = doBuildProject("new-pattern-match/project.qbs", 0, 0, &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY(receiver.taskDescriptions.contains("Resolving")); +} + +void TestApi::nonexistingProjectPropertyFromProduct() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("nonexistingprojectproperties/invalidaccessfromproduct.qbs"); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QEXPECT_FAIL("", "QBS-432", Abort); + QVERIFY(job->error().hasError()); + QVERIFY2(job->error().toString().contains(QLatin1String("blubb")), + qPrintable(job->error().toString())); +} + +void TestApi::nonexistingProjectPropertyFromCommandLine() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("nonexistingprojectproperties/project.qbs"); + removeBuildDir(setupParams); + QVariantMap projectProperties; + projectProperties.insert(QLatin1String("project.blubb"), QLatin1String("true")); + setupParams.setOverriddenValues(projectProperties); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY(job->error().hasError()); + QVERIFY2(job->error().toString().contains(QLatin1String("blubb")), + qPrintable(job->error().toString())); +} + +void TestApi::objC() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("objc/objc.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::projectDataAfterProductInvalidation() +{ + qbs::SetupProjectParameters setupParams = defaultSetupParameters("project-data-after-" + "product-invalidation/project-data-after-product-invalidation.qbs"); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + qbs::Project project = setupJob->project(); + QVERIFY(project.isValid()); + QCOMPARE(project.projectData().products().count(), 1); + QVERIFY(project.projectData().products().first().generatedArtifacts().isEmpty()); + QScopedPointer buildJob(project.buildAllProducts(qbs::BuildOptions())); + waitForFinished(buildJob.data()); + QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); + QCOMPARE(project.projectData().products().count(), 1); + const qbs::ProductData productAfterBulding = project.projectData().products().first(); + QVERIFY(!productAfterBulding.generatedArtifacts().isEmpty()); + QFile projectFile(setupParams.projectFilePath()); + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + QByteArray content = projectFile.readAll(); + QVERIFY(!content.isEmpty()); + content.replace("\"file.cpp", "// \"file.cpp"); + projectFile.resize(0); + projectFile.write(content); + projectFile.flush(); + setupJob.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + QVERIFY(!project.isValid()); + project = setupJob->project(); + QVERIFY(project.isValid()); + QCOMPARE(project.projectData().products().count(), 1); + QVERIFY(project.projectData().products().first().generatedArtifacts() + == productAfterBulding.generatedArtifacts()); + buildJob.reset(project.buildAllProducts(qbs::BuildOptions())); + waitForFinished(buildJob.data()); + QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString())); + QCOMPARE(project.projectData().products().count(), 1); + QVERIFY(project.projectData().products().first().generatedArtifacts() + != productAfterBulding.generatedArtifacts()); +} + +void TestApi::processResult() +{ + // On Windows, even closed files seem to sometimes block the removal of their parent directories + // for a while. + if (qbs::Internal::HostOsInfo::isWindowsHost()) + QTest::qWait(500); + removeBuildDir(defaultSetupParameters("process-result/process-result.qbs")); + + QFETCH(int, expectedExitCode); + QFETCH(bool, redirectStdout); + QFETCH(bool, redirectStderr); + QVariantMap overridden; + overridden.insert("app-caller.argument", expectedExitCode); + overridden.insert("app-caller.redirectStdout", redirectStdout); + overridden.insert("app-caller.redirectStderr", redirectStderr); + ProcessResultReceiver resultReceiver; + const qbs::ErrorInfo errorInfo = doBuildProject("process-result/process-result.qbs", + nullptr, &resultReceiver, nullptr, qbs::BuildOptions(), overridden); + QCOMPARE(expectedExitCode != 0, errorInfo.hasError()); + QVERIFY(resultReceiver.results.count() > 1); + const qbs::ProcessResult &result = resultReceiver.results.last(); + QVERIFY2(result.executableFilePath().contains("app"), qPrintable(result.executableFilePath())); + QCOMPARE(expectedExitCode, result.exitCode()); + QCOMPARE(expectedExitCode == 0, result.success()); + QCOMPARE(result.error(), QProcess::UnknownError); + struct CheckParams { + CheckParams(bool r, const QString &f, const QByteArray &c, const QStringList &co) + : redirect(r), fileName(f), expectedContent(c), consoleOutput(co) {} + bool redirect; + QString fileName; + QByteArray expectedContent; + const QStringList consoleOutput; + }; + const QVector checkParams({ + CheckParams(redirectStdout, "stdout.txt", "stdout", result.stdOut()), + CheckParams(redirectStderr, "stderr.txt", "stderr", result.stdErr()) + }); + foreach (const CheckParams &p, checkParams) { + QFile f(relativeProductBuildDir("app-caller") + '/' + p.fileName); + QCOMPARE(f.exists(), p.redirect); + if (p.redirect) { + QVERIFY2(f.open(QIODevice::ReadOnly), qPrintable(f.errorString())); + QCOMPARE(f.readAll(), p.expectedContent); + QCOMPARE(p.consoleOutput, QStringList()); + } else { + QCOMPARE(p.consoleOutput.join("").toLocal8Bit(), p.expectedContent); + } + } +} + +void TestApi::processResult_data() +{ + QTest::addColumn("expectedExitCode"); + QTest::addColumn("redirectStdout"); + QTest::addColumn("redirectStderr"); + QTest::newRow("success, no redirection") << 0 << false << false; + QTest::newRow("success, stdout redirection") << 0 << true << false; + QTest::newRow("failure, stderr redirection") << 1 << false << true; +} + +void TestApi::projectInvalidation() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("project-invalidation/project.qbs"); + QVERIFY(QFile::copy("project.no-error.qbs", "project.qbs")); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + qbs::Project project = setupJob->project(); + QVERIFY(project.isValid()); + WAIT_FOR_NEW_TIMESTAMP(); + copyFileAndUpdateTimestamp("project.early-error.qbs", "project.qbs"); + setupJob.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY(setupJob->error().hasError()); + QVERIFY(project.isValid()); // Error in Loader, old project still valid. + WAIT_FOR_NEW_TIMESTAMP(); + copyFileAndUpdateTimestamp("project.late-error.qbs", "project.qbs"); + setupJob.reset(project.setupProject(setupParams, m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY(setupJob->error().hasError()); + QVERIFY(!project.isValid()); // Error in build data re-resolving, old project not valid anymore. +} + +void TestApi::projectLocking() +{ + qbs::SetupProjectParameters setupParams = defaultSetupParameters("project-locking/project.qbs"); + QScopedPointer setupJob(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); + qbs::Project project = setupJob->project(); + setupJob.reset(project.setupProject(setupParams, m_logSink, 0)); + QScopedPointer setupJob2(project.setupProject(setupParams, + m_logSink, 0)); + waitForFinished(setupJob2.data()); + QVERIFY(setupJob2->error().hasError()); + QVERIFY2(setupJob2->error().toString() + .contains("Cannot start a job while another one is in progress."), + qPrintable(setupJob2->error().toString())); + waitForFinished(setupJob.data()); + QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString())); +} + +void TestApi::projectPropertiesByName() +{ + const QString projectFile = "project-properties-by-name/project.qbs"; + qbs::ErrorInfo errorInfo = doBuildProject(projectFile); + QVERIFY(errorInfo.hasError()); + QVariantMap overridden; + overridden.insert("project.theDefines", QStringList() << "SUB1" << "SUB2"); + errorInfo = doBuildProject(projectFile, 0, 0, 0, qbs::BuildOptions(), overridden); + QVERIFY(errorInfo.hasError()); + overridden.clear(); + overridden.insert("subproject1.theDefines", QStringList() << "SUB1"); + errorInfo = doBuildProject(projectFile, 0, 0, 0, qbs::BuildOptions(), overridden); + QVERIFY(errorInfo.hasError()); + overridden.insert("subproject2.theDefines", QStringList() << "SUB2"); + errorInfo = doBuildProject(projectFile, 0, 0, 0, qbs::BuildOptions(), overridden); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::projectWithPropertiesItem() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("project-with-properties-item/project.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::propertiesBlocks() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("properties-blocks/propertiesblocks.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::rc() +{ + BuildDescriptionReceiver receiver; + const qbs::ErrorInfo errorInfo = doBuildProject("rc/rc.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + const bool rcFileWasCompiled = receiver.descriptions.contains("compiling test.rc"); + QCOMPARE(rcFileWasCompiled, qbs::Internal::HostOsInfo::isWindowsHost()); +} + +void TestApi::referencedFileErrors() +{ + QFETCH(bool, relaxedMode); + qbs::SetupProjectParameters params + = defaultSetupParameters("referenced-file-errors/referenced-file-errors.qbs"); + params.setDryRun(true); + params.setProductErrorMode(relaxedMode ? qbs::ErrorHandlingMode::Relaxed + : qbs::ErrorHandlingMode::Strict); + QScopedPointer job(qbs::Project().setupProject(params, m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(job->error().hasError() != relaxedMode, qPrintable(job->error().toString())); + const qbs::Project project = job->project(); + QCOMPARE(project.isValid(), relaxedMode); + if (!relaxedMode) + return; + const QList products = project.projectData().allProducts(); + QCOMPARE(products.count(), 5); + foreach (const qbs::ProductData &p, products) + QCOMPARE(p.isEnabled(), p.name() != "p5"); +} + +void TestApi::referencedFileErrors_data() +{ + QTest::addColumn("relaxedMode"); + QTest::newRow("strict mode") << false; + QTest::newRow("relaxed mode") << true; +} + +qbs::SetupProjectParameters TestApi::defaultSetupParameters(const QString &projectFilePath) const +{ + qbs::SetupProjectParameters setupParams; + const QString projectDirPath = QDir::cleanPath(m_workingDataDir + QLatin1Char('/') + + QFileInfo(projectFilePath).path()); + setupParams.setProjectFilePath(projectDirPath + QLatin1Char('/') + + QFileInfo(projectFilePath).fileName()); + setupParams.setPropertyCheckingMode(qbs::ErrorHandlingMode::Strict); + QDir::setCurrent(projectDirPath); + setupParams.setBuildRoot(projectDirPath); + qbs::Settings settings((QString())); + const qbs::Preferences prefs(&settings, profileName()); + setupParams.setSearchPaths(prefs.searchPaths(QDir::cleanPath(QCoreApplication::applicationDirPath() + + QLatin1String("/" QBS_RELATIVE_SEARCH_PATH)))); + setupParams.setPluginPaths(prefs.pluginPaths(QDir::cleanPath(QCoreApplication::applicationDirPath() + + QLatin1String("/" QBS_RELATIVE_PLUGINS_PATH)))); + setupParams.setLibexecPath(QDir::cleanPath(QCoreApplication::applicationDirPath() + + QLatin1String("/" QBS_RELATIVE_LIBEXEC_PATH))); + setupParams.setTopLevelProfile(profileName()); + setupParams.setConfigurationName(QStringLiteral("default")); + return setupParams; +} + +void TestApi::references() +{ + qbs::SetupProjectParameters setupParams = defaultSetupParameters("references/invalid1.qbs"); + const QString projectDir = QDir::cleanPath(m_workingDataDir + "/references"); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY(job->error().hasError()); + QString errorString = job->error().toString(); + QVERIFY2(errorString.contains("does not contain"), qPrintable(errorString)); + + setupParams.setProjectFilePath(projectDir + QLatin1String("/invalid2.qbs")); + job.reset(qbs::Project().setupProject(setupParams, m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY(job->error().hasError()); + errorString = job->error().toString(); + QVERIFY2(errorString.contains("contains more than one"), qPrintable(errorString)); + + setupParams.setProjectFilePath(projectDir + QLatin1String("/valid.qbs")); + job.reset(qbs::Project().setupProject(setupParams, m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + const qbs::ProjectData topLevelProject = job->project().projectData(); + QCOMPARE(topLevelProject.subProjects().count(), 1); + const QString subProjectFileName + = QFileInfo(topLevelProject.subProjects().first().location().filePath()).fileName(); + QCOMPARE(subProjectFileName, QString("p.qbs")); +} + +void TestApi::relaxedModeRecovery() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("relaxed-mode-recovery/relaxed-mode-recovery.qbs"); + setupParams.setProductErrorMode(qbs::ErrorHandlingMode::Relaxed); + setupParams.setPropertyCheckingMode(qbs::ErrorHandlingMode::Relaxed); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + if (m_logSink->warnings.count() != 4) { + foreach (const qbs::ErrorInfo &error, m_logSink->warnings) + qDebug() << error.toString(); + } + QCOMPARE(m_logSink->warnings.count(), 4); + foreach (const qbs::ErrorInfo &error, m_logSink->warnings) { + QVERIFY2(!error.toString().contains("ASSERT") + && (error.toString().contains("Dependency 'blubb' not found") + || error.toString().contains("Product 'p1' had errors and was disabled") + || error.toString().contains("Product 'p2' had errors and was disabled")), + qPrintable(error.toString())); + } +} + +void TestApi::renameProduct() +{ + // Initial run. + qbs::ErrorInfo errorInfo = doBuildProject("rename-product/rename.qbs"); + VERIFY_NO_ERROR(errorInfo); + + // Rename lib and adapt Depends item. + WAIT_FOR_NEW_TIMESTAMP(); + QFile f("rename.qbs"); + QVERIFY(f.open(QIODevice::ReadWrite)); + QByteArray contents = f.readAll(); + contents.replace("TheLib", "thelib"); + f.resize(0); + f.write(contents); + f.close(); + errorInfo = doBuildProject("rename-product/rename.qbs"); + VERIFY_NO_ERROR(errorInfo); + + // Rename lib and don't adapt Depends item. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(f.open(QIODevice::ReadWrite)); + contents = f.readAll(); + const int libNameIndex = contents.lastIndexOf("thelib"); + QVERIFY(libNameIndex != -1); + contents.replace(libNameIndex, 6, "TheLib"); + f.resize(0); + f.write(contents); + f.close(); + errorInfo = doBuildProject("rename-product/rename.qbs"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("Dependency 'thelib' not found"), + qPrintable(errorInfo.toString())); +} + +void TestApi::renameTargetArtifact() +{ + // Initial run. + BuildDescriptionReceiver receiver; + qbs::ErrorInfo errorInfo = doBuildProject("rename-target-artifact/rename.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY2(receiver.descriptions.contains("compiling"), qPrintable(receiver.descriptions)); + QCOMPARE(receiver.descriptions.count("linking"), 2); + receiver.descriptions.clear(); + + // Rename library file name. + WAIT_FOR_NEW_TIMESTAMP(); + QFile f("rename.qbs"); + QVERIFY(f.open(QIODevice::ReadWrite)); + QByteArray contents = f.readAll(); + contents.replace("the_lib", "TheLib"); + f.resize(0); + f.write(contents); + f.close(); + errorInfo = doBuildProject("rename-target-artifact/rename.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY2(!receiver.descriptions.contains("compiling"), qPrintable(receiver.descriptions)); + QCOMPARE(receiver.descriptions.count("linking"), 2); +} + +void TestApi::removeFileDependency() +{ + qbs::ErrorInfo errorInfo = doBuildProject("remove-file-dependency/removeFileDependency.qbs"); + VERIFY_NO_ERROR(errorInfo); + + QFile::remove("someheader.h"); + ProcessResultReceiver receiver; + errorInfo = doBuildProject("remove-file-dependency/removeFileDependency.qbs", 0, &receiver); + QVERIFY(errorInfo.hasError()); + QVERIFY2(receiver.output.contains("someheader.h"), qPrintable(receiver.output)); +} + +void TestApi::resolveProject() +{ + QFETCH(QString, projectSubDir); + QFETCH(QString, productFileName); + + const qbs::SetupProjectParameters params + = defaultSetupParameters(projectSubDir + "/project.qbs"); + removeBuildDir(params); + const QScopedPointer setupJob(qbs::Project().setupProject(params, + m_logSink, 0)); + waitForFinished(setupJob.data()); + VERIFY_NO_ERROR(setupJob->error()); + QVERIFY2(!QFile::exists(productFileName), qPrintable(productFileName)); + QVERIFY(regularFileExists(relativeBuildGraphFilePath())); +} + +void TestApi::resolveProject_data() +{ + return buildProject_data(); +} + +void TestApi::resolveProjectDryRun() +{ + QFETCH(QString, projectSubDir); + QFETCH(QString, productFileName); + + qbs::SetupProjectParameters params = defaultSetupParameters(projectSubDir + "/project.qbs"); + params.setDryRun(true); + removeBuildDir(params); + const QScopedPointer setupJob(qbs::Project().setupProject(params, + m_logSink, 0)); + waitForFinished(setupJob.data()); + VERIFY_NO_ERROR(setupJob->error()); + QVERIFY2(!QFile::exists(productFileName), qPrintable(productFileName)); + QVERIFY(!regularFileExists(relativeBuildGraphFilePath())); +} + +void TestApi::resolveProjectDryRun_data() +{ + return resolveProject_data(); +} + +void TestApi::restoredWarnings() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("restored-warnings/restored-warnings.qbs"); + setupParams.setPropertyCheckingMode(qbs::ErrorHandlingMode::Relaxed); + setupParams.setProductErrorMode(qbs::ErrorHandlingMode::Relaxed); + + // Initial resolving: Errors are new. + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + job.reset(nullptr); + QCOMPARE(m_logSink->warnings.toSet().count(), 2); + foreach (const qbs::ErrorInfo &e, m_logSink->warnings) { + const QString msg = e.toString(); + QVERIFY2(msg.contains("Superfluous version") + || msg.contains("Property 'blubb' is not declared"), + qPrintable(msg)); + } + m_logSink->warnings.clear(); + + // Re-resolving with no changes: Errors come from the stored build graph. + job.reset(qbs::Project().setupProject(setupParams, m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + job.reset(nullptr); + QCOMPARE(m_logSink->warnings.toSet().count(), 2); + m_logSink->warnings.clear(); + + // Re-resolving with changes: Errors come from the re-resolving, stored ones must be suppressed. + QVariantMap overridenValues; + overridenValues.insert("theProduct.moreFiles", true); + setupParams.setOverriddenValues(overridenValues); + job.reset(qbs::Project().setupProject(setupParams, m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + job.reset(nullptr); + QCOMPARE(m_logSink->warnings.toSet().count(), 3); // One more for the additional group + foreach (const qbs::ErrorInfo &e, m_logSink->warnings) { + const QString msg = e.toString(); + QVERIFY2(msg.contains("Superfluous version") + || msg.contains("Property 'blubb' is not declared") + || msg.contains("blubb.cpp' does not exist"), + qPrintable(msg)); + } + m_logSink->warnings.clear(); +} + +void TestApi::ruleConflict() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("rule-conflict/rule-conflict.qbs"); + QVERIFY(errorInfo.hasError()); + const QString errorString = errorInfo.toString(); + QVERIFY2(errorString.contains("conflict") && errorString.contains("pch1.h") + && errorString.contains("pch2.h"), qPrintable(errorString)); +} + +void TestApi::softDependency() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("soft-dependency/project.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::sourceFileInBuildDir() +{ + qbs::SetupProjectParameters setupParams + = defaultSetupParameters("source-file-in-build-dir/project.qbs"); + QScopedPointer job(qbs::Project().setupProject(setupParams, + m_logSink, 0)); + waitForFinished(job.data()); + QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString())); + const qbs::ProjectData projectData = job->project().projectData(); + QCOMPARE(projectData.allProducts().count(), 1); + const qbs::ProductData product = projectData.allProducts().first(); + QCOMPARE(product.profile(), profileName()); + const qbs::GroupData group = findGroup(product, "the group"); + QVERIFY(group.isValid()); + QCOMPARE(group.allFilePaths().count(), 1); +} + +void TestApi::subProjects() +{ + const qbs::SetupProjectParameters params + = defaultSetupParameters("subprojects/toplevelproject.qbs"); + removeBuildDir(params); + + // Check all three types of subproject creation, plus property overrides. + qbs::ErrorInfo errorInfo = doBuildProject("subprojects/toplevelproject.qbs"); + VERIFY_NO_ERROR(errorInfo); + + // Disabling both the project with the dependency and the one with the dependent + // should not cause an error. + WAIT_FOR_NEW_TIMESTAMP(); + QFile f(params.projectFilePath()); + QVERIFY(f.open(QIODevice::ReadWrite)); + QByteArray contents = f.readAll(); + contents.replace("condition: true", "condition: false"); + f.resize(0); + f.write(contents); + f.close(); + f.setFileName(params.buildRoot() + "/subproject2/subproject2.qbs"); + QVERIFY(f.open(QIODevice::ReadWrite)); + contents = f.readAll(); + contents.replace("condition: qbs.targetOS.length > 0", "condition: false"); + f.resize(0); + f.write(contents); + f.close(); + errorInfo = doBuildProject("subprojects/toplevelproject.qbs"); + VERIFY_NO_ERROR(errorInfo); + + // Disabling the project with the dependency only is an error. + // This tests also whether changes in sub-projects are detected. + WAIT_FOR_NEW_TIMESTAMP(); + f.setFileName(params.projectFilePath()); + QVERIFY(f.open(QIODevice::ReadWrite)); + contents = f.readAll(); + contents.replace("condition: false", "condition: true"); + f.resize(0); + f.write(contents); + f.close(); + errorInfo = doBuildProject("subprojects/toplevelproject.qbs"); + QVERIFY(errorInfo.hasError()); + QVERIFY2(errorInfo.toString().contains("Dependency 'testLib' not found"), + qPrintable(errorInfo.toString())); +} + +void TestApi::trackAddQObjectHeader() +{ + const qbs::SetupProjectParameters params + = defaultSetupParameters("missing-qobject-header/missingheader.qbs"); + QFile qbsFile(params.projectFilePath()); + QVERIFY(qbsFile.open(QIODevice::WriteOnly | QIODevice::Truncate)); + qbsFile.write("import qbs.base 1.0\nCppApplication {\n Depends { name: 'Qt.core' }\n" + " files: ['main.cpp', 'myobject.cpp']\n}"); + qbsFile.close(); + ProcessResultReceiver receiver; + qbs::ErrorInfo errorInfo + = doBuildProject("missing-qobject-header/missingheader.qbs", 0, &receiver); + QVERIFY(errorInfo.hasError()); + QVERIFY2(isAboutUndefinedSymbols(receiver.output), qPrintable(receiver.output)); + + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(qbsFile.open(QIODevice::WriteOnly | QIODevice::Truncate)); + qbsFile.write("import qbs.base 1.0\nCppApplication {\n Depends { name: 'Qt.core' }\n" + " files: ['main.cpp', 'myobject.cpp','myobject.h']\n}"); + qbsFile.close(); + errorInfo = doBuildProject("missing-qobject-header/missingheader.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::trackRemoveQObjectHeader() +{ + const qbs::SetupProjectParameters params + = defaultSetupParameters("missing-qobject-header/missingheader.qbs"); + removeBuildDir(params); + QFile qbsFile(params.projectFilePath()); + QVERIFY(qbsFile.open(QIODevice::WriteOnly | QIODevice::Truncate)); + qbsFile.write("import qbs.base 1.0\nCppApplication {\n Depends { name: 'Qt.core' }\n" + " files: ['main.cpp', 'myobject.cpp','myobject.h']\n}"); + qbsFile.close(); + qbs::ErrorInfo errorInfo = doBuildProject("missing-qobject-header/missingheader.qbs"); + VERIFY_NO_ERROR(errorInfo); + + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(qbsFile.open(QIODevice::WriteOnly | QIODevice::Truncate)); + qbsFile.write("import qbs.base 1.0\nCppApplication {\n Depends { name: 'Qt.core' }\n" + " files: ['main.cpp', 'myobject.cpp']\n}"); + qbsFile.close(); + ProcessResultReceiver receiver; + errorInfo = doBuildProject("missing-qobject-header/missingheader.qbs", 0, &receiver); + QVERIFY(errorInfo.hasError()); + QVERIFY2(isAboutUndefinedSymbols(receiver.output), qPrintable(receiver.output)); +} + +void TestApi::transformers() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("transformers/transformers.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + +void TestApi::typeChange() +{ + BuildDescriptionReceiver receiver; + qbs::ErrorInfo errorInfo = doBuildProject("type-change/project.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY2(!receiver.descriptions.contains("compiling"), qPrintable(receiver.descriptions)); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile projectFile("project.qbs"); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + QByteArray content = projectFile.readAll(); + content.replace("//", ""); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + errorInfo = doBuildProject("type-change/project.qbs", &receiver); + VERIFY_NO_ERROR(errorInfo); + QVERIFY2(receiver.descriptions.contains("compiling"), qPrintable(receiver.descriptions)); +} + +void TestApi::uic() +{ + const qbs::ErrorInfo errorInfo = doBuildProject("uic/uic.qbs"); + VERIFY_NO_ERROR(errorInfo); +} + + +qbs::ErrorInfo TestApi::doBuildProject( + const QString &projectFilePath, BuildDescriptionReceiver *buildDescriptionReceiver, + ProcessResultReceiver *procResultReceiver, TaskReceiver *taskReceiver, + const qbs::BuildOptions &options, const QVariantMap overriddenValues) +{ + qbs::SetupProjectParameters params = defaultSetupParameters(projectFilePath); + params.setOverriddenValues(overriddenValues); + params.setDryRun(options.dryRun()); + const QScopedPointer setupJob(qbs::Project().setupProject(params, + m_logSink, 0)); + if (taskReceiver) { + connect(setupJob.data(), &qbs::AbstractJob::taskStarted, + taskReceiver, &TaskReceiver::handleTaskStart); + } + waitForFinished(setupJob.data()); + if (setupJob->error().hasError()) + return setupJob->error(); + const QScopedPointer buildJob(setupJob->project().buildAllProducts(options)); + if (buildDescriptionReceiver) { + connect(buildJob.data(), &qbs::BuildJob::reportCommandDescription, + buildDescriptionReceiver, &BuildDescriptionReceiver::handleDescription); + } + if (procResultReceiver) { + connect(buildJob.data(), &qbs::BuildJob::reportProcessResult, + procResultReceiver, &ProcessResultReceiver::handleProcessResult); + } + waitForFinished(buildJob.data()); + return buildJob->error(); +} + +QTEST_MAIN(TestApi) + +#include "tst_api.moc" diff --git a/tests/auto/api/tst_api.h b/tests/auto/api/tst_api.h new file mode 100644 index 00000000..54aed93e --- /dev/null +++ b/tests/auto/api/tst_api.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_TST_API_H +#define QBS_TST_API_H + +#include + +#include +#include + +namespace qbs { +class ErrorInfo; +class SetupProjectParameters; +} + +class BuildDescriptionReceiver; +class LogSink; +class ProcessResultReceiver; +class TaskReceiver; + +class TestApi : public QObject +{ + Q_OBJECT + +public: + TestApi(); + ~TestApi(); + +private slots: + void initTestCase(); + void init(); + + void addQObjectMacroToCppFile(); + void addedFilePersistent(); + void baseProperties(); + void buildGraphLocking(); + void buildProject(); + void buildProject_data(); + void buildProjectDryRun(); + void buildProjectDryRun_data(); + void buildSingleFile(); + void canonicalToolchainList(); +#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES + void changeContent(); +#endif + void changeDependentLib(); + void checkOutputs(); + void checkOutputs_data(); + void commandExtraction(); + void disabledInstallGroup(); + void disabledProduct(); + void disabledProject(); + void duplicateProductNames(); + void duplicateProductNames_data(); + void dynamicLibs(); + void emptyFileTagList(); + void emptySubmodulesList(); + void enableAndDisableProduct(); + void errorInSetupRunEnvironment(); + void explicitlyDependsOn(); + void exportSimple(); + void exportWithRecursiveDepends(); + void fileTagger(); + void fileTagsFilterOverride(); + void generatedFilesList(); + void infiniteLoopBuilding(); + void infiniteLoopBuilding_data(); + void infiniteLoopResolving(); + void inheritQbsSearchPaths(); + void installableFiles(); + void isRunnable(); + void listBuildSystemFiles(); + void mocCppIncluded(); + void multiArch(); + void newOutputArtifactInDependency(); + void newPatternMatch(); + void nonexistingProjectPropertyFromCommandLine(); + void nonexistingProjectPropertyFromProduct(); + void objC(); + void projectDataAfterProductInvalidation(); + void processResult(); + void processResult_data(); + void projectInvalidation(); + void projectLocking(); + void projectPropertiesByName(); + void projectWithPropertiesItem(); + void propertiesBlocks(); + void rc(); + void referencedFileErrors(); + void referencedFileErrors_data(); + void references(); + void relaxedModeRecovery(); + void removeFileDependency(); + void renameProduct(); + void renameTargetArtifact(); + void resolveProject(); + void resolveProject_data(); + void resolveProjectDryRun(); + void resolveProjectDryRun_data(); + void restoredWarnings(); + void ruleConflict(); + void softDependency(); + void sourceFileInBuildDir(); + void subProjects(); + void trackAddQObjectHeader(); + void trackRemoveQObjectHeader(); + void transformers(); + void typeChange(); + void uic(); + +private: + qbs::SetupProjectParameters defaultSetupParameters(const QString &projectFilePath) const; + qbs::ErrorInfo doBuildProject(const QString &projectFilePath, + BuildDescriptionReceiver *buildDescriptionReceiver = 0, + ProcessResultReceiver *procResultReceiver = 0, + TaskReceiver *taskReceiver = 0, + const qbs::BuildOptions &options = qbs::BuildOptions(), + const QVariantMap overriddenValues = QVariantMap()); + + LogSink * const m_logSink; + const QString m_sourceDataDir; + const QString m_workingDataDir; +}; + +#endif // Include guard. diff --git a/tests/auto/auto.pri b/tests/auto/auto.pri new file mode 100644 index 00000000..545d1365 --- /dev/null +++ b/tests/auto/auto.pri @@ -0,0 +1,12 @@ +TEMPLATE = app +DESTDIR = ../../../bin +DEFINES += SRCDIR=\\\"$$_PRO_FILE_PWD_\\\" +INCLUDEPATH += $$PWD/../../src + +QT = core testlib +CONFIG += depend_includepath testcase console +CONFIG -= app_bundle +CONFIG += c++11 +target.CONFIG += no_default_install + +include(../../src/lib/corelib/use_corelib.pri) diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro new file mode 100644 index 00000000..c835768e --- /dev/null +++ b/tests/auto/auto.pro @@ -0,0 +1,15 @@ +TEMPLATE=subdirs + +qbs_enable_unit_tests { + SUBDIRS += \ + buildgraph \ + language \ + tools \ +} + +SUBDIRS += \ + cmdlineparser \ + blackbox/blackbox.pro \ + blackbox/blackbox-clangdb.pro \ + blackbox/blackbox-java.pro \ + api diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs new file mode 100644 index 00000000..aa5b3ff4 --- /dev/null +++ b/tests/auto/auto.qbs @@ -0,0 +1,13 @@ +import qbs + +Project { + name: "Autotests" + references: [ + "api/api.qbs", + "blackbox/blackbox.qbs", + "buildgraph/buildgraph.qbs", + "cmdlineparser/cmdlineparser.qbs", + "language/language.qbs", + "tools/tools.qbs", + ] +} diff --git a/tests/auto/blackbox/blackbox-clangdb.pro b/tests/auto/blackbox/blackbox-clangdb.pro new file mode 100644 index 00000000..c4fcedbe --- /dev/null +++ b/tests/auto/blackbox/blackbox-clangdb.pro @@ -0,0 +1,16 @@ +TARGET = tst_blackbox-clangdb + +HEADERS = tst_blackboxbase.h tst_clangdb.h +SOURCES = tst_blackboxbase.cpp tst_clangdb.cpp + +include(../auto.pri) + +DATA_DIRS = testdata-clangdb + +for(data_dir, DATA_DIRS) { + files = $$files($$PWD/$$data_dir/*, true) + win32:files ~= s|\\\\|/|g + for(file, files):!exists($$file/*):FILES += $$file +} + +OTHER_FILES += $$FILES diff --git a/tests/auto/blackbox/blackbox-java.pro b/tests/auto/blackbox/blackbox-java.pro new file mode 100644 index 00000000..e3e60564 --- /dev/null +++ b/tests/auto/blackbox/blackbox-java.pro @@ -0,0 +1,16 @@ +TARGET = tst_blackbox-java + +HEADERS = tst_blackboxjava.h tst_blackboxbase.h +SOURCES = tst_blackboxjava.cpp tst_blackboxbase.cpp + +include(../auto.pri) + +DATA_DIRS = testdata-java ../find + +for(data_dir, DATA_DIRS) { + files = $$files($$PWD/$$data_dir/*, true) + win32:files ~= s|\\\\|/|g + for(file, files):!exists($$file/*):FILES += $$file +} + +OTHER_FILES += $$FILES diff --git a/tests/auto/blackbox/blackbox.pro b/tests/auto/blackbox/blackbox.pro new file mode 100644 index 00000000..8398270d --- /dev/null +++ b/tests/auto/blackbox/blackbox.pro @@ -0,0 +1,16 @@ +TARGET = tst_blackbox + +HEADERS = tst_blackbox.h tst_blackboxbase.h +SOURCES = tst_blackbox.cpp tst_blackboxbase.cpp + +include(../auto.pri) + +DATA_DIRS = testdata ../find + +for(data_dir, DATA_DIRS) { + files = $$files($$PWD/$$data_dir/*, true) + win32:files ~= s|\\\\|/|g + for(file, files):!exists($$file/*):FILES += $$file +} + +OTHER_FILES += $$FILES diff --git a/tests/auto/blackbox/blackbox.qbs b/tests/auto/blackbox/blackbox.qbs new file mode 100644 index 00000000..efbc1241 --- /dev/null +++ b/tests/auto/blackbox/blackbox.qbs @@ -0,0 +1,68 @@ +import qbs 1.0 + +Project { + name: "blackbox tests" + + QbsAutotest { + testName: "blackbox" + Depends { name: "qbs_app" } + Depends { name: "qbs-setup-toolchains" } + Group { + name: "testdata" + prefix: "testdata/" + files: ["**/*"] + fileTags: [] + } + files: [ + "../shared.h", + "tst_blackboxbase.cpp", + "tst_blackboxbase.h", + "tst_blackbox.cpp", + "tst_blackbox.h", + ] + cpp.defines: base.concat(['SRCDIR="' + path + '"']) + } + + QbsAutotest { + testName: "blackbox-java" + Depends { name: "qbs_app" } + Depends { name: "qbs-setup-toolchains" } + Group { + name: "testdata" + prefix: "testdata-java/" + files: ["**/*"] + fileTags: [] + } + files: [ + "../shared.h", + "tst_blackboxbase.cpp", + "tst_blackboxbase.h", + "tst_blackboxjava.cpp", + "tst_blackboxjava.h", + ] + cpp.defines: base.concat(['SRCDIR="' + path + '"']) + } + + QbsAutotest { + testName: "blackbox-clangdb" + + Depends { name: "qbs_app" } + Depends { name: "qbs-setup-toolchains" } + + Group { + name: "testdata" + prefix: "testdata-clangdb/" + files: ["**/*"] + fileTags: [] + } + + files: [ + "../shared.h", + "tst_blackboxbase.cpp", + "tst_blackboxbase.h", + "tst_clangdb.cpp", + "tst_clangdb.h", + ] + cpp.defines: base.concat(['SRCDIR="' + path + '"']) + } +} diff --git a/tests/auto/blackbox/find/find-android.qbs b/tests/auto/blackbox/find/find-android.qbs new file mode 100644 index 00000000..4b39afee --- /dev/null +++ b/tests/auto/blackbox/find/find-android.qbs @@ -0,0 +1,41 @@ +import qbs +import qbs.TextFile + +Product { + property string packageName: "" + + Depends { name: "Android.sdk"; required: false } + Depends { name: "Android.ndk"; required: false } + type: ["json"] + Rule { + inputs: ["qbs"] + Artifact { + filePath: ["android.json"] + fileTags: ["json"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = output.filePath; + cmd.sourceCode = function() { + var tools = {}; + if (product.moduleProperty("Android.sdk", "present")) { + tools["sdk"] = product.moduleProperty("Android.sdk", "sdkDir"); + } + + if (product.moduleProperty("Android.ndk", "present")) { + tools["ndk"] = product.moduleProperty("Android.ndk", "ndkDir"); + } + + var tf; + try { + tf = new TextFile(output.filePath, TextFile.WriteOnly); + tf.writeLine(JSON.stringify(tools, undefined, 4)); + } finally { + if (tf) + tf.close(); + } + }; + return cmd; + } + } +} diff --git a/tests/auto/blackbox/find/find-jdk.qbs b/tests/auto/blackbox/find/find-jdk.qbs new file mode 100644 index 00000000..6bfc70ac --- /dev/null +++ b/tests/auto/blackbox/find/find-jdk.qbs @@ -0,0 +1,36 @@ +import qbs +import qbs.TextFile + +Product { + Depends { name: "java"; required: false } + type: ["json"] + Rule { + inputs: ["qbs"] + Artifact { + filePath: ["jdk.json"] + fileTags: ["json"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = output.filePath; + cmd.sourceCode = function() { + var tools = {}; + if (product.moduleProperty("java", "present")) { + tools["javac"] = product.moduleProperty("java", "compilerFilePath"); + tools["java"] = product.moduleProperty("java", "interpreterFilePath"); + tools["jar"] = product.moduleProperty("java", "jarFilePath"); + } + + var tf; + try { + tf = new TextFile(output.filePath, TextFile.WriteOnly); + tf.writeLine(JSON.stringify(tools, undefined, 4)); + } finally { + if (tf) + tf.close(); + } + }; + return cmd; + } + } +} diff --git a/tests/auto/blackbox/find/find-nodejs.qbs b/tests/auto/blackbox/find/find-nodejs.qbs new file mode 100644 index 00000000..107937da --- /dev/null +++ b/tests/auto/blackbox/find/find-nodejs.qbs @@ -0,0 +1,34 @@ +import qbs +import qbs.TextFile + +Product { + Depends { name: "nodejs"; required: false } + type: ["json"] + Rule { + inputs: ["qbs"] + Artifact { + filePath: ["nodejs.json"] + fileTags: ["json"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = output.filePath; + cmd.sourceCode = function() { + var tools = {}; + if (product.moduleProperty("nodejs", "present")) { + tools["node"] = product.moduleProperty("nodejs", "interpreterFilePath"); + } + + var tf; + try { + tf = new TextFile(output.filePath, TextFile.WriteOnly); + tf.writeLine(JSON.stringify(tools, undefined, 4)); + } finally { + if (tf) + tf.close(); + } + }; + return cmd; + } + } +} diff --git a/tests/auto/blackbox/find/find-typescript.qbs b/tests/auto/blackbox/find/find-typescript.qbs new file mode 100644 index 00000000..e5616633 --- /dev/null +++ b/tests/auto/blackbox/find/find-typescript.qbs @@ -0,0 +1,35 @@ +import qbs +import qbs.TextFile + +Product { + Depends { name: "nodejs"; required: false } + Depends { name: "typescript"; required: false } + type: ["json"] + Rule { + inputs: ["qbs"] + Artifact { + filePath: ["typescript.json"] + fileTags: ["json"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = output.filePath; + cmd.sourceCode = function() { + var tools = {}; + if (product.moduleProperty("typescript", "present")) { + tools["tsc"] = product.moduleProperty("typescript", "compilerPath"); + } + + var tf; + try { + tf = new TextFile(output.filePath, TextFile.WriteOnly); + tf.writeLine(JSON.stringify(tools, undefined, 4)); + } finally { + if (tf) + tf.close(); + } + }; + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata-clangdb/project1/i like spaces.cpp b/tests/auto/blackbox/testdata-clangdb/project1/i like spaces.cpp new file mode 100644 index 00000000..64d87d52 --- /dev/null +++ b/tests/auto/blackbox/testdata-clangdb/project1/i like spaces.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +using namespace std; + +#define _STR(x) #x +#define STR(x) _STR(x) + +int main(int argc, char **argv) +{ + int garbage; + int unused = garbage; + cout << "SPACES=" << SPACES << "SPICES=" STR(SPICES) << "SLICES=" << SLICES << endl; + return 0; +} diff --git a/tests/auto/blackbox/testdata-clangdb/project1/project.qbs b/tests/auto/blackbox/testdata-clangdb/project1/project.qbs new file mode 100644 index 00000000..354912ea --- /dev/null +++ b/tests/auto/blackbox/testdata-clangdb/project1/project.qbs @@ -0,0 +1,24 @@ +import qbs + +// $ g++ 'i like spaces.cpp' '-DSPACES="!have \\fun\x5c!\n"' '-DSPICES=%T% # && $$ 1>&2 '\''\n'\''\n' '-DSLICES=(42>24)' && ./a.out +// SPACES=!have \fun\! +// SPICES=%T% # && $$ 1>&2 '\n' +// SLICES=1 + +Project { +Application { + targetName: "i like spaces" + + Depends { + name: "cpp" + } + + cpp.defines: base.concat([ + "SPACES=\"!have \\\\fun\\x5c!\\n\"", + "SPICES=%T% # && $$ 1>&2 '\\n'\\n", + "SLICES=(42>24)" + ]); + + files: ["i like spaces.cpp"] +} +} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/multiple-apks-per-project.qbs b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/multiple-apks-per-project.qbs new file mode 100644 index 00000000..3d03c309 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/multiple-apks-per-project.qbs @@ -0,0 +1,8 @@ +import qbs + +Project { + references: [ + "product1", + "product2", + ] +} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/product1.qbs b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/product1.qbs new file mode 100644 index 00000000..1058b9db --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/product1.qbs @@ -0,0 +1,31 @@ +import qbs + +Project { + DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } + name: "p1lib1" + files: ["src/main/jni/lib1.cpp"] + Android.ndk.appStl: "stlport_shared" + architectures: ["mips", "x86"] + cpp.useRPaths: false + } + + DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } + name: "p1lib2" + files: ["src/main/jni/lib2.cpp"] + Android.ndk.appStl: "stlport_shared" + cpp.useRPaths: false + } + + AndroidApk { + name: "twolibs1" + packageName: "io.qt.dummy1" + Depends { + productTypes: ["android.nativelibrary"] + limitToSubProject: true + } + } +} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1468b52f --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/java/io/qt/dummy/Dummy.java b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/java/io/qt/dummy/Dummy.java new file mode 100644 index 00000000..149af081 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/java/io/qt/dummy/Dummy.java @@ -0,0 +1,11 @@ +package io.qt.dummy; + +import android.app.Activity; + +public class Dummy extends Activity +{ + static { + System.loadLibrary("lib1"); + System.loadLibrary("lib"); + } +} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/jni/lib1.cpp b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/jni/lib1.cpp new file mode 100644 index 00000000..474897da --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/jni/lib1.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f() {} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/jni/lib2.cpp b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/jni/lib2.cpp new file mode 100644 index 00000000..de580ab0 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/jni/lib2.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void g() {} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/res/values/strings.xml b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/res/values/strings.xml new file mode 100644 index 00000000..94974895 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product1/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + dummy1 + diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/product2.qbs b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/product2.qbs new file mode 100644 index 00000000..f0418a42 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/product2.qbs @@ -0,0 +1,27 @@ +import qbs + +Project { + DynamicLibrary { + Depends { name: "cpp" } + name: "p2lib1" + files: ["src/main/jni/lib1.cpp"] + cpp.useRPaths: false + } + + DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } + name: "p2lib2" + files: ["src/main/jni/lib2.cpp"] + Android.ndk.appStl: "stlport_shared" + } + + AndroidApk { + name: "twolibs2" + packageName: "io.qt.dummy2" + Depends { + productTypes: ["android.nativelibrary"] + limitToSubProject: true + } + } +} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml new file mode 100644 index 00000000..4e5cdf97 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/java/io/qt/dummy/Dummy.java b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/java/io/qt/dummy/Dummy.java new file mode 100644 index 00000000..149af081 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/java/io/qt/dummy/Dummy.java @@ -0,0 +1,11 @@ +package io.qt.dummy; + +import android.app.Activity; + +public class Dummy extends Activity +{ + static { + System.loadLibrary("lib1"); + System.loadLibrary("lib"); + } +} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/jni/lib1.cpp b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/jni/lib1.cpp new file mode 100644 index 00000000..474897da --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/jni/lib1.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f() {} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/jni/lib2.cpp b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/jni/lib2.cpp new file mode 100644 index 00000000..de580ab0 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-apks-per-project/product2/src/main/jni/lib2.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void g() {} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/multiple-libs-per-apk.qbs b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/multiple-libs-per-apk.qbs new file mode 100644 index 00000000..94eb33ce --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/multiple-libs-per-apk.qbs @@ -0,0 +1,27 @@ +import qbs + +Project { + DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } + name: "lib1" + files: ["src/main/jni/lib1.cpp"] + Android.ndk.appStl: "stlport_shared" + cpp.useRPaths: false + } + + DynamicLibrary { + Depends { name: "Android.ndk" } + Depends { name: "cpp" } + name: "lib2" + files: ["src/main/jni/lib2.cpp"] + Android.ndk.appStl: "stlport_shared" + cpp.useRPaths: false + } + + AndroidApk { + name: "twolibs" + packageName: "io.qt.dummy" + Depends { productTypes: ["android.nativelibrary"] } + } +} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/AndroidManifest.xml new file mode 100644 index 00000000..639d3a64 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/java/io/qt/dummy/Dummy.java b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/java/io/qt/dummy/Dummy.java new file mode 100644 index 00000000..149af081 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/java/io/qt/dummy/Dummy.java @@ -0,0 +1,11 @@ +package io.qt.dummy; + +import android.app.Activity; + +public class Dummy extends Activity +{ + static { + System.loadLibrary("lib1"); + System.loadLibrary("lib"); + } +} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/jni/lib1.cpp b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/jni/lib1.cpp new file mode 100644 index 00000000..474897da --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/jni/lib1.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f() {} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/jni/lib2.cpp b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/jni/lib2.cpp new file mode 100644 index 00000000..de580ab0 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/jni/lib2.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void g() {} diff --git a/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/res/values/strings.xml b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/res/values/strings.xml new file mode 100644 index 00000000..297dfa7f --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/multiple-libs-per-apk/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + dummy + diff --git a/tests/auto/blackbox/testdata-java/android/no-native/no-native.qbs b/tests/auto/blackbox/testdata-java/android/no-native/no-native.qbs new file mode 100644 index 00000000..2909adc0 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/no-native/no-native.qbs @@ -0,0 +1,8 @@ +import qbs + +AndroidApk { + name: "com.example.android.basicmediadecoder" + + sourceSetDir: Android.sdk.sdkDir + + "/samples/android-BasicMediaDecoder/Application/src/main" +} diff --git a/tests/auto/blackbox/testdata-java/android/teapot/teapot.qbs b/tests/auto/blackbox/testdata-java/android/teapot/teapot.qbs new file mode 100644 index 00000000..1ce4d897 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/android/teapot/teapot.qbs @@ -0,0 +1,93 @@ +import qbs + +Project { + minimumQbsVersion: qbs.version + property stringList architectures: ["arm64", "armv7a", "x86", "x86_64", "mips", "mips64"] + StaticLibrary { + architectures: project.architectures + name: "native-glue" + Depends { name: "cpp" } + Group { + id: glue_sources + prefix: Android.ndk.ndkDir + "/sources/android/native_app_glue/" + files: ["*.c", "*.h"] + } + + Export { + Depends { name: "cpp" } + cpp.includePaths: [glue_sources.prefix] + cpp.dynamicLibraries: ["log"] + } + } + + StaticLibrary { + architectures: project.architectures + name: "ndk-helper" + Depends { name: "Android.ndk" } + Depends { name: "cpp" } + Depends { name: "native-glue" } + + Group { + id: ndkhelper_sources + prefix: Android.ndk.ndkDir + "/sources/android/ndk_helper/" + files: ["*.c", "*.cpp", "*.h"] + } + Android.ndk.appStl: "stlport_shared" + + Export { + Depends { name: "cpp" } + cpp.includePaths: [ndkhelper_sources.prefix] + cpp.dynamicLibraries: ["log", "android", "EGL", "GLESv2"] + } + } + + StaticLibrary { + architectures: project.architectures + name: "cpufeatures" + Depends { name: "cpp" } + Group { + id: cpufeatures_sources + prefix: Android.ndk.ndkDir + "/sources/android/cpufeatures/" + files: ["*.c", "*.h"] + } + + Export { + Depends { name: "cpp" } + cpp.includePaths: [cpufeatures_sources.prefix] + cpp.dynamicLibraries: ["dl"] + } + } + + DynamicLibrary { + name: "TeapotNativeActivity" + architectures: project.architectures + Depends { name: "Android.ndk" } + Depends { name: "cpp" } + Depends { name: "cpufeatures" } + Depends { name: "native-glue" } + Depends { name: "ndk-helper" } + + Group { + name: "C++ sources" + prefix: Android.ndk.ndkDir + "/samples/Teapot/app/src/main/jni/" + files: [ + "TeapotNativeActivity.cpp", + "TeapotRenderer.cpp", + "TeapotRenderer.h", + "teapot.inl", + ] + } + + FileTagger { patterns: ["*.inl"]; fileTags: ["hpp"] } + + Android.ndk.appStl: "stlport_shared" + cpp.dynamicLibraries: ["log", "android", "EGL", "GLESv2"] + cpp.useRPaths: false + } + + AndroidApk { + name: "com.sample.teapot" + sourceSetDir: Android.sdk.ndkDir + "/samples/Teapot/app/src/main" + Depends { productTypes: ["android.nativelibrary"] } + } +} diff --git a/tests/auto/blackbox/testdata-java/java/Car.java b/tests/auto/blackbox/testdata-java/java/Car.java new file mode 100644 index 00000000..06afd5ae --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/Car.java @@ -0,0 +1,23 @@ +class Car implements Vehicle +{ + private InternalCombustionEngine engine; + + public Car() { + engine = new InternalCombustionEngine(); + } + + public void go() + { + System.out.println("Driving!"); + engine.run(); + } + + public class InternalCombustionEngine + { + public native void run(); + + public class ChemicalReaction { + public native void occur(); + } + } +} diff --git a/tests/auto/blackbox/testdata-java/java/Car8.java b/tests/auto/blackbox/testdata-java/java/Car8.java new file mode 100644 index 00000000..941c7126 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/Car8.java @@ -0,0 +1,28 @@ +class Car8 implements Vehicle +{ + private InternalCombustionEngine engine; + + public Car8() { + engine = new InternalCombustionEngine(); + } + + public void go() + { + System.out.println("Driving!"); + engine.run(); + } + + public class InternalCombustionEngine + { + public native void run(); + + public class ChemicalReaction { + public native void occur(); + + public class Atoms { + @java.lang.annotation.Native + public int hydrogenAtomCount; + } + } + } +} diff --git a/tests/auto/blackbox/testdata-java/java/HelloWorld.java b/tests/auto/blackbox/testdata-java/java/HelloWorld.java new file mode 100644 index 00000000..d42146d6 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/HelloWorld.java @@ -0,0 +1,15 @@ +package io.qt.qbs; + +public class HelloWorld { + public static void main(String[] args) { + System.out.println("Tach."); + } + + public class Internal { + public native void something(); + } + + public class Other { + public final int countOfThings = 0; + } +} diff --git a/tests/auto/blackbox/testdata-java/java/HelloWorld8.java b/tests/auto/blackbox/testdata-java/java/HelloWorld8.java new file mode 100644 index 00000000..d1ced80d --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/HelloWorld8.java @@ -0,0 +1,8 @@ +package io.qt.qbs; + +public class HelloWorld8 { + public class Other { + @java.lang.annotation.Native + public final int countOfThings = 0; + } +} diff --git a/tests/auto/blackbox/testdata-java/java/Jet.java b/tests/auto/blackbox/testdata-java/java/Jet.java new file mode 100644 index 00000000..38c00c04 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/Jet.java @@ -0,0 +1,7 @@ +class Jet implements Vehicle +{ + public void go() + { + System.out.println("Flying!"); + } +} diff --git a/tests/auto/blackbox/testdata-java/java/NoPackage.java b/tests/auto/blackbox/testdata-java/java/NoPackage.java new file mode 100644 index 00000000..84045c3a --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/NoPackage.java @@ -0,0 +1,5 @@ +// package this.should.not.be.parsed; + +public class NoPackage { +public static void doSomething() {} +} diff --git a/tests/auto/blackbox/testdata-java/java/RandomStuff.java b/tests/auto/blackbox/testdata-java/java/RandomStuff.java new file mode 100644 index 00000000..fd98b3f7 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/RandomStuff.java @@ -0,0 +1,6 @@ +package glob; + +public class RandomStuff { + public static void bar() { + } +} diff --git a/tests/auto/blackbox/testdata-java/java/Ship.java b/tests/auto/blackbox/testdata-java/java/Ship.java new file mode 100644 index 00000000..3ecce635 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/Ship.java @@ -0,0 +1,12 @@ +class Ship implements Vehicle +{ + public boolean isInSpace; + + public void go() + { + if (isInSpace) + System.out.println("Flying (this is a space ship)!"); + else + System.out.println("Sailing!"); + } +} diff --git a/tests/auto/blackbox/testdata-java/java/Vehicle.java b/tests/auto/blackbox/testdata-java/java/Vehicle.java new file mode 100644 index 00000000..b3a0f805 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/Vehicle.java @@ -0,0 +1,4 @@ +interface Vehicle +{ + public void go(); +} diff --git a/tests/auto/blackbox/testdata-java/java/Vehicles.java b/tests/auto/blackbox/testdata-java/java/Vehicles.java new file mode 100644 index 00000000..e25ab980 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/Vehicles.java @@ -0,0 +1,37 @@ +import java.util.ArrayList; +import glob.RandomStuff; + +class Vehicles +{ + public static void main(String[] args) + { + System.loadLibrary("native"); + RandomStuff.bar(); + ArrayList vehicles = new ArrayList(); + + for (int i = 0; i < 3; i++) + { + vehicles.add(new Car()); + } + + for (int i = 0; i < 3; i++) + { + vehicles.add(new Jet()); + } + + for (int i = 0; i < 4; i++) + { + Ship ship = new Ship(); + ship.isInSpace = i % 2 == 0; + vehicles.add(ship); + } + + for (int i = 0; i < vehicles.size(); i++) + { + vehicles.get(i).go(); + } + + // doesn't compile, must be a bug + // delete vehicles; + } +} diff --git a/tests/auto/blackbox/testdata-java/java/engine.c b/tests/auto/blackbox/testdata-java/java/engine.c new file mode 100644 index 00000000..3c292ed1 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/engine.c @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +// javac 1.8 is required to generate native headers +#ifdef JNI_VERSION_1_8 +#include "Car_InternalCombustionEngine.h" +#endif + +JNIEXPORT void JNICALL Java_Car_00024InternalCombustionEngine_run(JNIEnv *env, jobject obj) { + printf("Native code performing complex internal combustion process (%p, %p)!\n", env, obj); +} diff --git a/tests/auto/blackbox/testdata-java/java/inner-class/InnerClass.java b/tests/auto/blackbox/testdata-java/java/inner-class/InnerClass.java new file mode 100644 index 00000000..3e0333ec --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/inner-class/InnerClass.java @@ -0,0 +1,6 @@ +public class InnerClass { + private final InnerInnerClass clazz = new InnerInnerClass(); + + private class InnerInnerClass { + } +} diff --git a/tests/auto/blackbox/testdata-java/java/inner-class/inner-class.qbs b/tests/auto/blackbox/testdata-java/java/inner-class/inner-class.qbs new file mode 100644 index 00000000..5bc861f5 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/inner-class/inner-class.qbs @@ -0,0 +1,5 @@ +import qbs + +JavaJarFile { + files: ["**/*.java"] +} diff --git a/tests/auto/blackbox/testdata-java/java/vehicles.qbs b/tests/auto/blackbox/testdata-java/java/vehicles.qbs new file mode 100644 index 00000000..78c09217 --- /dev/null +++ b/tests/auto/blackbox/testdata-java/java/vehicles.qbs @@ -0,0 +1,98 @@ +import qbs +import qbs.FileInfo + +Project { + DynamicLibrary { + Depends { name: "cpp" } + Depends { name: "car_jar" } + bundle.isBundle: false + + name: "native" + files: ["engine.c"] + + Group { + fileTagsFilter: ["dynamiclibrary"] + qbs.install: true + } + } + + JavaClassCollection { + Depends { name: "random_stuff" } + name: "class_collection" + java.additionalCompilerFlags: ["-Xlint:all"] + files: [ + "Car.java", "HelloWorld.java", "Jet.java", "NoPackage.java", "Ship.java", + "Vehicle.java", "Vehicles.java" + ] + + Group { + condition: java.compilerVersionMinor >= 8 + files: ["Car8.java", "HelloWorld8.java"] + } + + Export { + Depends { name: "java" } + java.manifestClassPath: [product.targetName + ".jar"] + } + } + + JavaJarFile { + name: "random_stuff" + files: ["RandomStuff.java"] + + Group { + fileTagsFilter: ["java.jar"] + qbs.install: true + } + + Export { + Depends { name: "java" } + java.manifestClassPath: [product.targetName + ".jar"] + } + } + + JavaJarFile { + name: "car_jar" + files: ["Car.java", "Vehicle.java"] + + Group { + condition: java.compilerVersionMinor >= 8 + files: ["Car8.java"] + } + + property stringList cppIncludePaths: { + var paths = java.jdkIncludePaths; + if (java.compilerVersionMinor >= 8) { + paths.push(buildDirectory); // generated JNI headers + } + return paths; + } + + Export { + Depends { name: "cpp" } + cpp.systemIncludePaths: product.cppIncludePaths + + Depends { name: "java" } + java.manifestClassPath: [product.targetName + ".jar"] + } + + Group { + fileTagsFilter: ["java.jar"] + qbs.install: true + } + } + + JavaJarFile { + Depends { name: "random_stuff" } + Depends { name: "car_jar" } + Depends { name: "native" } + name: "jar_file" + entryPoint: "Vehicles" + files: ["Jet.java", "Ship.java", "Vehicles.java"] + + Group { + fileTagsFilter: ["java.jar"] + qbs.install: true + } + } +} diff --git a/tests/auto/blackbox/testdata/QTBUG-51237/modules/mymodule/mymodule.qbs b/tests/auto/blackbox/testdata/QTBUG-51237/modules/mymodule/mymodule.qbs new file mode 100644 index 00000000..223da0b3 --- /dev/null +++ b/tests/auto/blackbox/testdata/QTBUG-51237/modules/mymodule/mymodule.qbs @@ -0,0 +1,6 @@ +import qbs + +Module { + property stringList theProperty: [] + //property stringList otherProperty: theProperty.concat([]) +} diff --git a/tests/auto/blackbox/testdata/QTBUG-51237/qtbug-51237.qbs b/tests/auto/blackbox/testdata/QTBUG-51237/qtbug-51237.qbs new file mode 100644 index 00000000..e1f8d8ef --- /dev/null +++ b/tests/auto/blackbox/testdata/QTBUG-51237/qtbug-51237.qbs @@ -0,0 +1,22 @@ +import qbs + +Product { + type: "custom" + Depends { name: "mymodule" } + Rule { + multiplex: true + Artifact { + filePath: "dummy.custom" + fileTags: ["custom"] + } + prepare: { + var theProperty = product.moduleProperty("mymodule", "theProperty"); + if (!theProperty) + throw "Oh no!"; + var dummy = new JavaScriptCommand(); + dummy.silent = true; + dummy.sourceCode = function() {}; + return [dummy]; + } + } +} diff --git a/tests/auto/blackbox/testdata/always-run/dummy.txt b/tests/auto/blackbox/testdata/always-run/dummy.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/always-run/rule.qbs b/tests/auto/blackbox/testdata/always-run/rule.qbs new file mode 100644 index 00000000..6dbb5c84 --- /dev/null +++ b/tests/auto/blackbox/testdata/always-run/rule.qbs @@ -0,0 +1,29 @@ +import qbs +import qbs.TextFile + +Product { + type: ["blubb"] + Group { + files: ["dummy.txt"] + fileTags: ["dummy"] + } + + Rule { + alwaysRun: false + inputs: ["dummy"] + Artifact { + filePath: "blubb.txt" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "yo"; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.write("blubb"); + f.close(); + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/always-run/transformer.qbs b/tests/auto/blackbox/testdata/always-run/transformer.qbs new file mode 100644 index 00000000..b5c83eaa --- /dev/null +++ b/tests/auto/blackbox/testdata/always-run/transformer.qbs @@ -0,0 +1,23 @@ +import qbs +import qbs.TextFile + +Product { + type: ["blubb"] + Transformer { + alwaysRun: false + Artifact { + filePath: "blubb.txt" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "yo"; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.write("blubb"); + f.close(); + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/archiver/archivable.qbs b/tests/auto/blackbox/testdata/archiver/archivable.qbs new file mode 100644 index 00000000..d6a8e048 --- /dev/null +++ b/tests/auto/blackbox/testdata/archiver/archivable.qbs @@ -0,0 +1,13 @@ +import qbs + +Product { + name: "archivable" + type: "archiver.archive" + Depends { name: "archiver" } + archiver.workingDirectory: path + Group { + files: ["list.txt"] + fileTags: ["archiver.input-list"] + } + files: ["test.txt"] +} diff --git a/tests/auto/blackbox/testdata/archiver/list.txt b/tests/auto/blackbox/testdata/archiver/list.txt new file mode 100644 index 00000000..e300f8e2 --- /dev/null +++ b/tests/auto/blackbox/testdata/archiver/list.txt @@ -0,0 +1,2 @@ +test.txt +archivable.qbs diff --git a/tests/auto/blackbox/testdata/archiver/test.txt b/tests/auto/blackbox/testdata/archiver/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/assembly/assembly.qbs b/tests/auto/blackbox/testdata/assembly/assembly.qbs new file mode 100644 index 00000000..77c2e89c --- /dev/null +++ b/tests/auto/blackbox/testdata/assembly/assembly.qbs @@ -0,0 +1,33 @@ +import qbs 1.0 + +Project { + StaticLibrary { + name : "testa" + files : [ "testa.s" ] + Depends { name: "cpp" } + condition: qbs.toolchain.contains("gcc") + } + StaticLibrary { + name : "testb" + files : [ "testb.S" ] + Depends { name: "cpp" } + condition: qbs.toolchain.contains("gcc") + } + StaticLibrary { + name : "testc" + files : [ "testc.sx" ] + Depends { name: "cpp" } + condition: qbs.toolchain.contains("gcc") + } + StaticLibrary { + name: "testd" + Group { + condition: product.condition + files: ["testd_" + qbs.architecture + ".asm"] + } + Depends { name: "cpp" } + condition: qbs.toolchain.contains("msvc") + && (qbs.architecture === "x86" || qbs.architecture === "x86_64") + } +} + diff --git a/tests/auto/blackbox/testdata/assembly/testa.s b/tests/auto/blackbox/testdata/assembly/testa.s new file mode 100644 index 00000000..39e402ae --- /dev/null +++ b/tests/auto/blackbox/testdata/assembly/testa.s @@ -0,0 +1,3 @@ +.globl symbola +symbola: + nop diff --git a/tests/auto/blackbox/testdata/assembly/testb.S b/tests/auto/blackbox/testdata/assembly/testb.S new file mode 100644 index 00000000..845be550 --- /dev/null +++ b/tests/auto/blackbox/testdata/assembly/testb.S @@ -0,0 +1,3 @@ +#define bla nop +symbolb: + bla diff --git a/tests/auto/blackbox/testdata/assembly/testc.sx b/tests/auto/blackbox/testdata/assembly/testc.sx new file mode 100644 index 00000000..845be550 --- /dev/null +++ b/tests/auto/blackbox/testdata/assembly/testc.sx @@ -0,0 +1,3 @@ +#define bla nop +symbolb: + bla diff --git a/tests/auto/blackbox/testdata/assembly/testd_x86.asm b/tests/auto/blackbox/testdata/assembly/testd_x86.asm new file mode 100644 index 00000000..7b99c017 --- /dev/null +++ b/tests/auto/blackbox/testdata/assembly/testd_x86.asm @@ -0,0 +1,11 @@ +.386 +.model flat, stdcall + +.code +foo PROC + nop + RET +foo ENDP + +END + diff --git a/tests/auto/blackbox/testdata/assembly/testd_x86_64.asm b/tests/auto/blackbox/testdata/assembly/testd_x86_64.asm new file mode 100644 index 00000000..e957b0a7 --- /dev/null +++ b/tests/auto/blackbox/testdata/assembly/testd_x86_64.asm @@ -0,0 +1,8 @@ +.code +foo PROC + nop + RET +foo ENDP + +END + diff --git a/tests/auto/blackbox/testdata/auto-qrc/auto-qrc.qbs b/tests/auto/blackbox/testdata/auto-qrc/auto-qrc.qbs new file mode 100644 index 00000000..3055e51b --- /dev/null +++ b/tests/auto/blackbox/testdata/auto-qrc/auto-qrc.qbs @@ -0,0 +1,52 @@ +import qbs + +Project { + QtApplication { + name: "app" + files: ["main.cpp"] + + Group { + prefix: "qrc-base/" + + Qt.core.resourcePrefix: "/thePrefix" + Qt.core.resourceSourceBase: "qrc-base" + + files: ["resource1.txt"] + fileTags: ["qt.core.resource_data"] + + Group { + prefix: "qrc-base/subdir/" + + Qt.core.resourceSourceBase: "qrc-base/subdir" + + files: ["resource2.txt"] + + Group { + prefix: "qrc-base/subdir/" + + Qt.core.resourcePrefix: "/theOtherPrefix" + + files: ["resource3.txt"] + } + } + } + } + + Product { + name: "runner" + type: ["runner"] + Depends { name: "app" } + Rule { + inputsFromDependencies: ["application"] + Artifact { + filePath: "dummy" + fileTags: ["runner"] + } + prepare: { + var cmd = new Command(input.filePath); + cmd.description = "running " + input.filePath; + return [cmd]; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/auto-qrc/main.cpp b/tests/auto/blackbox/testdata/auto-qrc/main.cpp new file mode 100644 index 00000000..53a33854 --- /dev/null +++ b/tests/auto/blackbox/testdata/auto-qrc/main.cpp @@ -0,0 +1,18 @@ +#include + +#include + +int main() +{ + QFile resource1(":/thePrefix/resource1.txt"); + if (!resource1.open(QIODevice::ReadOnly)) + return 1; + QFile resource2(":/thePrefix/resource2.txt"); + if (!resource2.open(QIODevice::ReadOnly)) + return 2; + QFile resource3(":/theOtherPrefix/resource3.txt"); + if (!resource3.open(QIODevice::ReadOnly)) + return 3; + std::cout << "resource data: " << resource1.readAll().constData() + << resource2.readAll().constData() << resource3.readAll().constData() << std::endl; +} diff --git a/tests/auto/blackbox/testdata/auto-qrc/qrc-base/resource1.txt b/tests/auto/blackbox/testdata/auto-qrc/qrc-base/resource1.txt new file mode 100644 index 00000000..edf22a3d --- /dev/null +++ b/tests/auto/blackbox/testdata/auto-qrc/qrc-base/resource1.txt @@ -0,0 +1 @@ +resource1 diff --git a/tests/auto/blackbox/testdata/auto-qrc/qrc-base/subdir/resource2.txt b/tests/auto/blackbox/testdata/auto-qrc/qrc-base/subdir/resource2.txt new file mode 100644 index 00000000..b7c27009 --- /dev/null +++ b/tests/auto/blackbox/testdata/auto-qrc/qrc-base/subdir/resource2.txt @@ -0,0 +1 @@ +resource2 diff --git a/tests/auto/blackbox/testdata/auto-qrc/qrc-base/subdir/resource3.txt b/tests/auto/blackbox/testdata/auto-qrc/qrc-base/subdir/resource3.txt new file mode 100644 index 00000000..6df9761d --- /dev/null +++ b/tests/auto/blackbox/testdata/auto-qrc/qrc-base/subdir/resource3.txt @@ -0,0 +1 @@ +resource3 diff --git a/tests/auto/blackbox/testdata/badInterpreter/badInterpreter.qbs b/tests/auto/blackbox/testdata/badInterpreter/badInterpreter.qbs new file mode 100644 index 00000000..a4539147 --- /dev/null +++ b/tests/auto/blackbox/testdata/badInterpreter/badInterpreter.qbs @@ -0,0 +1,49 @@ +import qbs + +Project { + qbsSearchPaths: base.concat(["qbs"]) + + Product { + Depends { name: "script-test" } + name: "script-ok" + type: ["application"] + + Group { + files: [product.name] + fileTags: ["script"] + } + } + + Product { + Depends { name: "script-test" } + name: "script-noexec" + type: ["application"] + + Group { + files: [product.name] + fileTags: ["script"] + } + } + + Product { + Depends { name: "script-test" } + name: "script-interp-missing" + type: ["application"] + + Group { + files: [product.name] + fileTags: ["script"] + } + } + + Product { + Depends { name: "script-test" } + name: "script-interp-noexec" + type: ["application"] + + Group { + files: [product.name] + fileTags: ["script"] + } + } +} diff --git a/tests/auto/blackbox/testdata/badInterpreter/qbs/modules/script-test/script-test.qbs b/tests/auto/blackbox/testdata/badInterpreter/qbs/modules/script-test/script-test.qbs new file mode 100644 index 00000000..79c008cf --- /dev/null +++ b/tests/auto/blackbox/testdata/badInterpreter/qbs/modules/script-test/script-test.qbs @@ -0,0 +1,39 @@ +import qbs +import qbs.FileInfo +import qbs.TextFile + +Module { + name: "script-test" + + Rule { + inputs: ["script"] + + Artifact { + filePath: FileInfo.joinPaths(project.buildDirectory, input.fileName) + fileTags: ["application"] + } + + prepare: { + var cmds = []; + var cmd = new JavaScriptCommand(); + cmd.description = "copying " + input.fileName; + cmd.sourceCode = function() { + var tf = new TextFile(input.filePath, TextFile.ReadOnly); + var s = tf.readAll().replace("$PWD", project.buildDirectory); + tf.close(); + var tf2 = new TextFile(output.filePath, TextFile.ReadWrite); + tf2.write(s); + tf2.close(); + }; + cmds.push(cmd); + + if (output.fileName !== "script-noexec") { + var cmd2 = new Command("chmod", ["+x", output.filePath]); + cmd2.silent = true; + cmds.push(cmd2); + } + + return cmds; + } + } +} diff --git a/tests/auto/blackbox/testdata/badInterpreter/script-interp-missing b/tests/auto/blackbox/testdata/badInterpreter/script-interp-missing new file mode 100755 index 00000000..b92a0e9d --- /dev/null +++ b/tests/auto/blackbox/testdata/badInterpreter/script-interp-missing @@ -0,0 +1 @@ +#!/does/not/exist diff --git a/tests/auto/blackbox/testdata/badInterpreter/script-interp-noexec b/tests/auto/blackbox/testdata/badInterpreter/script-interp-noexec new file mode 100755 index 00000000..8a336b98 --- /dev/null +++ b/tests/auto/blackbox/testdata/badInterpreter/script-interp-noexec @@ -0,0 +1 @@ +#!$PWD/script-noexec diff --git a/tests/auto/blackbox/testdata/badInterpreter/script-noexec b/tests/auto/blackbox/testdata/badInterpreter/script-noexec new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/badInterpreter/script-ok b/tests/auto/blackbox/testdata/badInterpreter/script-ok new file mode 100755 index 00000000..a9bf588e --- /dev/null +++ b/tests/auto/blackbox/testdata/badInterpreter/script-ok @@ -0,0 +1 @@ +#!/bin/bash diff --git a/tests/auto/blackbox/testdata/build-directories/project.qbs b/tests/auto/blackbox/testdata/build-directories/project.qbs new file mode 100644 index 00000000..dd8fc6c0 --- /dev/null +++ b/tests/auto/blackbox/testdata/build-directories/project.qbs @@ -0,0 +1,45 @@ +import qbs + +Project { + Product { + name: "p1" + type: "blubb1" + Rule { + multiplex: true + Artifact { + filePath: "dummy1.txt" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + console.info(product.buildDirectory); + } + return cmd; + } + } + } + Product { + name: "p2" + type: "blubb2" + Depends { name: "p1" } + Rule { + inputsFromDependencies: "blubb1" + Artifact { + filePath: "dummy2.txt" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + console.info(product.buildDirectory); + console.info(project.buildDirectory); + console.info(project.sourceDirectory); + } + return cmd; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/bundle-structure/bundle-structure.qbs b/tests/auto/blackbox/testdata/bundle-structure/bundle-structure.qbs new file mode 100644 index 00000000..5b372589 --- /dev/null +++ b/tests/auto/blackbox/testdata/bundle-structure/bundle-structure.qbs @@ -0,0 +1,167 @@ +import qbs + +Project { + property stringList bundleFileTags: [ + "aggregate_infoplist", "pkginfo", "hpp", + "icns", "xcent", + "compiled_ibdoc", "compiled_assetcatalog", + "bundle.symlink.headers", "bundle.symlink.private-headers", + "bundle.symlink.resources", "bundle.symlink.executable", + "bundle.symlink.version", "bundle.hpp", "bundle.resource", + ] + + property stringList buildableProducts: ["A", "B", "C", "D", "E", "F", "G"] + + Application { + Depends { name: "cpp" } + Depends { name: "B" } + Depends { name: "C" } + Depends { name: "D" } + condition: buildableProducts.contains("A") + name: "A" + bundle.isBundle: true + bundle.publicHeaders: ["dummy.h"] + bundle.privateHeaders: ["dummy_p.h"] + bundle.resources: ["resource.txt"] + files: ["dummy.c"] + Group { + fileTagsFilter: product.type.concat(project.bundleFileTags) + qbs.install: true + qbs.installSourceBase: product.buildDirectory + } + } + + Application { + Depends { name: "cpp" } + Depends { name: "B" } + Depends { name: "C" } + Depends { name: "D" } + condition: buildableProducts.contains("ABadApple") + name: "ABadApple" + bundle._productTypeIdentifier: "com.apple.product-type.will.never.exist.ever.guaranteed" + bundle.isBundle: true + bundle.publicHeaders: ["dummy.h"] + bundle.privateHeaders: ["dummy_p.h"] + bundle.resources: ["resource.txt"] + files: ["dummy.c"] + Group { + fileTagsFilter: product.type.concat(project.bundleFileTags) + qbs.install: true + qbs.installSourceBase: product.buildDirectory + } + } + + Application { + Depends { name: "cpp" } + Depends { name: "B" } + Depends { name: "C" } + Depends { name: "D" } + condition: buildableProducts.contains("ABadThirdParty") + name: "ABadThirdParty" + bundle._productTypeIdentifier: "org.special.third.party.non.existent.product.type" + bundle.isBundle: true + bundle.publicHeaders: ["dummy.h"] + bundle.privateHeaders: ["dummy_p.h"] + bundle.resources: ["resource.txt"] + files: ["dummy.c"] + Group { + fileTagsFilter: product.type.concat(project.bundleFileTags) + qbs.install: true + qbs.installSourceBase: product.buildDirectory + } + } + + DynamicLibrary { + Depends { name: "cpp" } + condition: buildableProducts.containsAny(["A", "B", "ABadApple", "ABadThirdParty"]) + name: "B" + bundle.isBundle: true + bundle.publicHeaders: ["dummy.h"] + bundle.privateHeaders: ["dummy_p.h"] + bundle.resources: ["resource.txt"] + files: ["dummy.c"] + Group { + fileTagsFilter: product.type.concat(project.bundleFileTags) + qbs.install: true + qbs.installSourceBase: product.buildDirectory + } + } + + StaticLibrary { + Depends { name: "cpp" } + condition: buildableProducts.containsAny(["A", "C", "ABadApple", "ABadThirdParty"]) + name: "C" + bundle.isBundle: true + bundle.publicHeaders: ["dummy.h"] + bundle.privateHeaders: ["dummy_p.h"] + bundle.resources: ["resource.txt"] + files: ["dummy.c"] + Group { + fileTagsFilter: product.type.concat(project.bundleFileTags) + qbs.install: true + qbs.installSourceBase: product.buildDirectory + } + } + + LoadableModule { + Depends { name: "cpp" } + condition: buildableProducts.containsAny(["A", "D", "ABadApple", "ABadThirdParty"]) + name: "D" + bundle.isBundle: true + bundle.publicHeaders: ["dummy.h"] + bundle.privateHeaders: ["dummy_p.h"] + bundle.resources: ["resource.txt"] + files: ["dummy.c"] + Group { + fileTagsFilter: product.type.concat(project.bundleFileTags) + qbs.install: true + qbs.installSourceBase: product.buildDirectory + } + } + + ApplicationExtension { + Depends { name: "cpp" } + condition: buildableProducts.contains("E") + name: "E" + bundle.isBundle: true + bundle.publicHeaders: ["dummy.h"] + bundle.privateHeaders: ["dummy_p.h"] + bundle.resources: ["resource.txt"] + files: ["dummy.c"] + Group { + fileTagsFilter: product.type.concat(project.bundleFileTags) + qbs.install: true + qbs.installSourceBase: product.buildDirectory + } + } + + XPCService { + Depends { name: "cpp" } + condition: buildableProducts.contains("F") + name: "F" + bundle.isBundle: true + bundle.publicHeaders: ["dummy.h"] + bundle.privateHeaders: ["dummy_p.h"] + bundle.resources: ["resource.txt"] + files: ["dummy.c"] + Group { + fileTagsFilter: product.type.concat(project.bundleFileTags) + qbs.install: true + qbs.installSourceBase: product.buildDirectory + } + } + + Product { + Depends { name: "bundle" } + condition: buildableProducts.contains("G") + type: ["inapppurchase"] + name: "G" + bundle.isBundle: true + bundle.resources: ["resource.txt"] + Group { + fileTagsFilter: product.type.concat(project.bundleFileTags) + qbs.install: true + qbs.installSourceBase: product.buildDirectory + } + } +} diff --git a/tests/auto/blackbox/testdata/bundle-structure/dummy.c b/tests/auto/blackbox/testdata/bundle-structure/dummy.c new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/bundle-structure/dummy.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/bundle-structure/dummy.h b/tests/auto/blackbox/testdata/bundle-structure/dummy.h new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/blackbox/testdata/bundle-structure/dummy.h @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/blackbox/testdata/bundle-structure/dummy_p.h b/tests/auto/blackbox/testdata/bundle-structure/dummy_p.h new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/blackbox/testdata/bundle-structure/dummy_p.h @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/blackbox/testdata/bundle-structure/resource.txt b/tests/auto/blackbox/testdata/bundle-structure/resource.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/change-in-disabled-product/project.qbs b/tests/auto/blackbox/testdata/change-in-disabled-product/project.qbs new file mode 100644 index 00000000..8a99d2c8 --- /dev/null +++ b/tests/auto/blackbox/testdata/change-in-disabled-product/project.qbs @@ -0,0 +1,9 @@ +import qbs + +Product { + condition: false + files: [ + 'test1.txt', + // 'test2.txt' + ] +} diff --git a/tests/auto/blackbox/testdata/change-in-disabled-product/test1.txt b/tests/auto/blackbox/testdata/change-in-disabled-product/test1.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/change-in-disabled-product/test2.txt b/tests/auto/blackbox/testdata/change-in-disabled-product/test2.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/change-in-imported-file/change-in-imported-file.qbs b/tests/auto/blackbox/testdata/change-in-imported-file/change-in-imported-file.qbs new file mode 100644 index 00000000..cb8bf066 --- /dev/null +++ b/tests/auto/blackbox/testdata/change-in-imported-file/change-in-imported-file.qbs @@ -0,0 +1,23 @@ +import qbs +import "prepare.js" as PrepareHelper + +Product { + type: ["output"] + Group { + files: ["test.txt"] + fileTags: ["input"] + } + Rule { + inputs: ["input"] + Artifact { + filePath: "dummy" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + PrepareHelper.prepare(cmd); + cmd.description = "Creating output"; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/change-in-imported-file/prepare.js b/tests/auto/blackbox/testdata/change-in-imported-file/prepare.js new file mode 100644 index 00000000..fa73f0ff --- /dev/null +++ b/tests/auto/blackbox/testdata/change-in-imported-file/prepare.js @@ -0,0 +1,3 @@ +function prepare(cmd) { + cmd.sourceCode = function() { console.info("old output"); }; +} diff --git a/tests/auto/blackbox/testdata/change-in-imported-file/test.txt b/tests/auto/blackbox/testdata/change-in-imported-file/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/changed-files/file1.cpp b/tests/auto/blackbox/testdata/changed-files/file1.cpp new file mode 100644 index 00000000..51bcc546 --- /dev/null +++ b/tests/auto/blackbox/testdata/changed-files/file1.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f1() {} diff --git a/tests/auto/blackbox/testdata/changed-files/file2.cpp b/tests/auto/blackbox/testdata/changed-files/file2.cpp new file mode 100644 index 00000000..e6d0ae53 --- /dev/null +++ b/tests/auto/blackbox/testdata/changed-files/file2.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f2() {} diff --git a/tests/auto/blackbox/testdata/changed-files/main.cpp b/tests/auto/blackbox/testdata/changed-files/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/changed-files/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/changed-files/project.qbs b/tests/auto/blackbox/testdata/changed-files/project.qbs new file mode 100644 index 00000000..dd222479 --- /dev/null +++ b/tests/auto/blackbox/testdata/changed-files/project.qbs @@ -0,0 +1,30 @@ +import qbs +import qbs.TextFile + +CppApplication { + type: ["application", "stuff"] + consoleApplication: true + files: ["file1.cpp", "file2.cpp", "main.cpp"] + + Rule { + inputs: ["cpp"] + outputFileTags: ["stuff"] + outputArtifacts: { + return [{ + filePath: input.completeBaseName + ".stuff", + fileTags: ["stuff"] + }]; + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "creating " + output.fileName + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.write("crazy stuff"); + f.close(); + } + return cmd; + } + } +} + diff --git a/tests/auto/blackbox/testdata/clean/clean.qbs b/tests/auto/blackbox/testdata/clean/clean.qbs new file mode 100644 index 00000000..cf16e7c8 --- /dev/null +++ b/tests/auto/blackbox/testdata/clean/clean.qbs @@ -0,0 +1,19 @@ +import qbs 1.0 + +Project { + DynamicLibrary { + Depends { name: "cpp" } + Depends { name: "Qt.core" } + version: "1.1.0" + name: "dep" + files: "dep.cpp" + bundle.isBundle: false + } + + CppApplication { + consoleApplication: true + name: "app" + Depends { name: "dep" } + files: "main.cpp" + } +} diff --git a/tests/auto/blackbox/testdata/clean/dep.cpp b/tests/auto/blackbox/testdata/clean/dep.cpp new file mode 100644 index 00000000..7e43bdcc --- /dev/null +++ b/tests/auto/blackbox/testdata/clean/dep.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +Q_DECL_EXPORT void f() {} diff --git a/tests/auto/blackbox/testdata/clean/main.cpp b/tests/auto/blackbox/testdata/clean/main.cpp new file mode 100644 index 00000000..e14f806b --- /dev/null +++ b/tests/auto/blackbox/testdata/clean/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { } diff --git a/tests/auto/blackbox/testdata/cli/HelloWorld.cs b/tests/auto/blackbox/testdata/cli/HelloWorld.cs new file mode 100755 index 00000000..1b794c18 --- /dev/null +++ b/tests/auto/blackbox/testdata/cli/HelloWorld.cs @@ -0,0 +1,12 @@ +using Fake; + +public class HelloWorld +{ + public static void Main() + { + System.Console.WriteLine("Hello world!"); + System.Console.WriteLine(Mod.Cool()); + System.Console.WriteLine(Libby.Cool()); + System.Console.WriteLine(Libby2.Cool()); + } +} diff --git a/tests/auto/blackbox/testdata/cli/Libby.cs b/tests/auto/blackbox/testdata/cli/Libby.cs new file mode 100755 index 00000000..5ca27efd --- /dev/null +++ b/tests/auto/blackbox/testdata/cli/Libby.cs @@ -0,0 +1,10 @@ +namespace Fake +{ + public class Libby + { + public static int Cool() + { + return 45; + } + } +} diff --git a/tests/auto/blackbox/testdata/cli/Libby2.cs b/tests/auto/blackbox/testdata/cli/Libby2.cs new file mode 100755 index 00000000..cfaf9907 --- /dev/null +++ b/tests/auto/blackbox/testdata/cli/Libby2.cs @@ -0,0 +1,10 @@ +namespace Fake +{ + public class Libby2 + { + public static int Cool() + { + return 22; + } + } +} diff --git a/tests/auto/blackbox/testdata/cli/Module.cs b/tests/auto/blackbox/testdata/cli/Module.cs new file mode 100644 index 00000000..dc08846d --- /dev/null +++ b/tests/auto/blackbox/testdata/cli/Module.cs @@ -0,0 +1,10 @@ +namespace Fake +{ + public class Mod + { + public static int Cool() + { + return 420; + } + } +} diff --git a/tests/auto/blackbox/testdata/cli/Module.vb b/tests/auto/blackbox/testdata/cli/Module.vb new file mode 100755 index 00000000..19763627 --- /dev/null +++ b/tests/auto/blackbox/testdata/cli/Module.vb @@ -0,0 +1,7 @@ +Namespace Fake + Public Class [Mod] + Public Shared Function Cool() As Integer + Return 420 + End Function + End Class +End Namespace diff --git a/tests/auto/blackbox/testdata/cli/dotnettest.qbs b/tests/auto/blackbox/testdata/cli/dotnettest.qbs new file mode 100644 index 00000000..c65a54c8 --- /dev/null +++ b/tests/auto/blackbox/testdata/cli/dotnettest.qbs @@ -0,0 +1,51 @@ +import qbs + +Project { + Application { + Depends { name: "cli" } + Depends { name: "HelloWorldModule"; condition: !qbs.toolchain.contains("mono") } + Depends { name: "NetLib" } + + type: "application" + name: "Hello" + files: ["HelloWorld.cs"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + // Mono's VB compiler doesn't support modules yet, and if we try with C#, it crashes anyways + NetModule { + condition: !qbs.toolchain.contains("mono") + Depends { name: "cli" } + + name: "HelloWorldModule" + + files: ["Module.vb"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + DynamicLibrary { + Depends { name: "cli" } + + name: "NetLib" + files: ["Libby.cs", "Libby2.cs"] + + // fill-in for missing NetModule + Group { + condition: qbs.toolchain.contains("mono") + files: ["Module.cs"] + } + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } +} diff --git a/tests/auto/blackbox/testdata/cli/fshello.fs b/tests/auto/blackbox/testdata/cli/fshello.fs new file mode 100644 index 00000000..0df5ca57 --- /dev/null +++ b/tests/auto/blackbox/testdata/cli/fshello.fs @@ -0,0 +1 @@ +printfn "Hello World from F#!" diff --git a/tests/auto/blackbox/testdata/cli/fshello.qbs b/tests/auto/blackbox/testdata/cli/fshello.qbs new file mode 100644 index 00000000..44dbb5c4 --- /dev/null +++ b/tests/auto/blackbox/testdata/cli/fshello.qbs @@ -0,0 +1,8 @@ +import qbs + +Application { + Depends { name: "cli" } + type: "application" + name: "fshello" + files: "fshello.fs" +} diff --git a/tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs b/tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs new file mode 100644 index 00000000..9031890b --- /dev/null +++ b/tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs @@ -0,0 +1,71 @@ +import qbs +import qbs.File +import qbs.TextFile +import "util.js" as Utils + +Product { + type: ["final1", "final2"] + Group { + files: ["dummy1.input"] + fileTags: ["input1"] + } + Group { + files: ["dummy2.input"] + fileTags: ["input2"] + } + Rule { + inputs: ["input1"] + Artifact { + filePath: project.buildDirectory + "/dummy1.final" + fileTags: ["final1"] + } + prepare: { + var cmds = []; + for (var i = 0; i < 10; ++i) { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.createFile = i == 9; + cmd.sourceCode = function() { + if (createFile) { + console.info("Creating file"); + var file = new TextFile(output.filePath, TextFile.WriteOnly); + file.close(); + } + }; + cmds.push(cmd); + } + return cmds; + } + } + Rule { + inputs: ["input2"] + Artifact { + filePath: "dummy.intermediate" + fileTags: ["intermediate"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { }; + return [cmd]; + } + } + Rule { + inputs: ["intermediate"] + Artifact { + filePath: "dummy2.final" + fileTags: ["final2"] + } + prepare: { + do + Utils.sleep(6000); + while (!File.exists(project.buildDirectory + "/dummy1.final")); + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { }; + return [cmd]; + } + } +} + + diff --git a/tests/auto/blackbox/testdata/concurrent-executor/dummy1.input b/tests/auto/blackbox/testdata/concurrent-executor/dummy1.input new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/concurrent-executor/dummy2.input b/tests/auto/blackbox/testdata/concurrent-executor/dummy2.input new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/concurrent-executor/util.js b/tests/auto/blackbox/testdata/concurrent-executor/util.js new file mode 100644 index 00000000..a37a8cbb --- /dev/null +++ b/tests/auto/blackbox/testdata/concurrent-executor/util.js @@ -0,0 +1,8 @@ +function sleep(timeInMs) +{ + var referenceTime = new Date(); + var time = null; + do { + time = new Date(); + } while (time - referenceTime < timeInMs); +} diff --git a/tests/auto/blackbox/testdata/conditional-export/conditional-export.qbs b/tests/auto/blackbox/testdata/conditional-export/conditional-export.qbs new file mode 100644 index 00000000..c7b6ae09 --- /dev/null +++ b/tests/auto/blackbox/testdata/conditional-export/conditional-export.qbs @@ -0,0 +1,18 @@ +import qbs + +Project { + property bool enableExport: false + Product { + name: "dep" + Export { + condition: project.enableExport + Depends { name: "cpp" } + cpp.defines: ["THE_DEFINE"] + } + } + CppApplication { + name: "theProduct" + Depends { name: "dep" } + files: "main.cpp" + } +} diff --git a/tests/auto/blackbox/testdata/conditional-export/main.cpp b/tests/auto/blackbox/testdata/conditional-export/main.cpp new file mode 100644 index 00000000..1235aa19 --- /dev/null +++ b/tests/auto/blackbox/testdata/conditional-export/main.cpp @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef THE_DEFINE +#error "missing define" +#endif + +int main() { } diff --git a/tests/auto/blackbox/testdata/conflicting-artifacts/conflicting-artifacts.qbs b/tests/auto/blackbox/testdata/conflicting-artifacts/conflicting-artifacts.qbs new file mode 100644 index 00000000..f2c5cb2b --- /dev/null +++ b/tests/auto/blackbox/testdata/conflicting-artifacts/conflicting-artifacts.qbs @@ -0,0 +1,16 @@ +import qbs + +Project { + CppApplication { + name: "a" + targetName: "theName" + destinationDirectory: project.buildDirectory + files: ["main.cpp"] + } + CppApplication { + name: "b" + targetName: "theName" + destinationDirectory: project.buildDirectory + files: ["main.cpp"] + } +} diff --git a/tests/auto/blackbox/testdata/conflicting-artifacts/main.cpp b/tests/auto/blackbox/testdata/conflicting-artifacts/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/conflicting-artifacts/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/dbus-adaptors/THIS.IS.A.STRANGE.FILENAME.CAR.XML b/tests/auto/blackbox/testdata/dbus-adaptors/THIS.IS.A.STRANGE.FILENAME.CAR.XML new file mode 100644 index 00000000..6d8c9d19 --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-adaptors/THIS.IS.A.STRANGE.FILENAME.CAR.XML @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/tests/auto/blackbox/testdata/dbus-adaptors/car.cpp b/tests/auto/blackbox/testdata/dbus-adaptors/car.cpp new file mode 100644 index 00000000..466ed4b8 --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-adaptors/car.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "car.h" +#include +#include + +static const double Pi = 3.14159265358979323846264338327950288419717; + +QRectF Car::boundingRect() const +{ + return QRectF(-35, -81, 70, 115); +} + +Car::Car() : color(Qt::green), wheelsAngle(0), speed(0) +{ + startTimer(1000 / 33); + setFlag(QGraphicsItem::ItemIsMovable, true); + setFlag(QGraphicsItem::ItemIsFocusable, true); +} + +void Car::accelerate() +{ + if (speed < 10) + ++speed; +} + +void Car::decelerate() +{ + if (speed > -10) + --speed; +} + +void Car::turnLeft() +{ + if (wheelsAngle > -30) + wheelsAngle -= 5; +} + +void Car::turnRight() +{ + if (wheelsAngle < 30) + wheelsAngle += 5; +} + +void Car::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + painter->setBrush(Qt::gray); + painter->drawRect(-20, -58, 40, 2); // front axel + painter->drawRect(-20, 7, 40, 2); // rear axel + + painter->setBrush(color); + painter->drawRect(-25, -79, 50, 10); // front wing + + painter->drawEllipse(-25, -48, 50, 20); // side pods + painter->drawRect(-25, -38, 50, 35); // side pods + painter->drawRect(-5, 9, 10, 10); // back pod + + painter->drawEllipse(-10, -81, 20, 100); // main body + + painter->drawRect(-17, 19, 34, 15); // rear wing + + painter->setBrush(Qt::black); + painter->drawPie(-5, -51, 10, 15, 0, 180 * 16); + painter->drawRect(-5, -44, 10, 10); // cocpit + + painter->save(); + painter->translate(-20, -58); + painter->rotate(wheelsAngle); + painter->drawRect(-10, -7, 10, 15); // front left + painter->restore(); + + painter->save(); + painter->translate(20, -58); + painter->rotate(wheelsAngle); + painter->drawRect(0, -7, 10, 15); // front left + painter->restore(); + + painter->drawRect(-30, 0, 12, 17); // rear left + painter->drawRect(19, 0, 12, 17); // rear right +} + +void Car::timerEvent(QTimerEvent *event) +{ + Q_UNUSED(event); + + const qreal axelDistance = 54; + qreal wheelsAngleRads = (wheelsAngle * Pi) / 180; + qreal turnDistance = ::cos(wheelsAngleRads) * axelDistance * 2; + qreal turnRateRads = wheelsAngleRads / turnDistance; // rough estimate + qreal turnRate = (turnRateRads * 180) / Pi; + qreal rotation = speed * turnRate; + + setTransform(QTransform().rotate(rotation), true); + setTransform(QTransform::fromTranslate(0, -speed), true); + update(); +} diff --git a/tests/auto/blackbox/testdata/dbus-adaptors/car.h b/tests/auto/blackbox/testdata/dbus-adaptors/car.h new file mode 100644 index 00000000..f14b1062 --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-adaptors/car.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAR_H +#define CAR_H + +#include +#include + +class Car : public QGraphicsObject +{ + Q_OBJECT +public: + Car(); + QRectF boundingRect() const; + +public Q_SLOTS: + void accelerate(); + void decelerate(); + void turnLeft(); + void turnRight(); + +Q_SIGNALS: + void crashed(); + +protected: + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + void timerEvent(QTimerEvent *event); + +private: + QBrush color; + qreal wheelsAngle; // used when applying rotation + qreal speed; // delta movement along the body axis +}; + +#endif // CAR_H diff --git a/tests/auto/blackbox/testdata/dbus-adaptors/car.qbs b/tests/auto/blackbox/testdata/dbus-adaptors/car.qbs new file mode 100644 index 00000000..67ba5e7e --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-adaptors/car.qbs @@ -0,0 +1,19 @@ +import qbs + +CppApplication { + name: "car" + condition: Qt.dbus.present + Depends { name: "Qt.dbus"; required: false } + Depends { name: "Qt.widgets" } + files: [ + "car.cpp", + "car.h", + "main.cpp", + ] + + Group { + name: "DBUS Adaptor" + files: ["THIS.IS.A.STRANGE.FILENAME.CAR.XML"] + fileTags: ["qt.dbus.adaptor"] + } +} diff --git a/tests/auto/blackbox/testdata/dbus-adaptors/main.cpp b/tests/auto/blackbox/testdata/dbus-adaptors/main.cpp new file mode 100644 index 00000000..2c6db440 --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-adaptors/main.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "car.h" +#include "car_adaptor.h" +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QGraphicsScene scene; + scene.setSceneRect(-500, -500, 1000, 1000); + scene.setItemIndexMethod(QGraphicsScene::NoIndex); + + Car *car = new Car(); + scene.addItem(car); + + QGraphicsView view(&scene); + view.setRenderHint(QPainter::Antialiasing); + view.setBackgroundBrush(Qt::darkGray); + view.setWindowTitle(QT_TRANSLATE_NOOP(QGraphicsView, "Qt DBus Controlled Car")); + view.resize(400, 300); + view.show(); + + new CarInterfaceAdaptor(car); + QDBusConnection connection = QDBusConnection::sessionBus(); + connection.registerObject("/Car", car); + connection.registerService("org.example.CarExample"); + + return app.exec(); +} diff --git a/tests/auto/blackbox/testdata/dbus-interfaces/car.xml b/tests/auto/blackbox/testdata/dbus-interfaces/car.xml new file mode 100644 index 00000000..6d8c9d19 --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-interfaces/car.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/tests/auto/blackbox/testdata/dbus-interfaces/controller.cpp b/tests/auto/blackbox/testdata/dbus-interfaces/controller.cpp new file mode 100644 index 00000000..eaff5c77 --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-interfaces/controller.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "controller.h" +#include "car_interface.h" + +Controller::Controller(QWidget *parent) + : QWidget(parent) +{ + ui.setupUi(this); + car = new org::example::Examples::CarInterface("org.example.CarExample", "/Car", + QDBusConnection::sessionBus(), this); + startTimer(1000); +} + +void Controller::timerEvent(QTimerEvent *event) +{ + Q_UNUSED(event); + if (car->isValid()) + ui.label->setText("connected"); + else + ui.label->setText("disconnected"); +} + +void Controller::on_accelerate_clicked() +{ + car->accelerate(); +} + +void Controller::on_decelerate_clicked() +{ + car->decelerate(); +} + +void Controller::on_left_clicked() +{ + car->turnLeft(); +} + +void Controller::on_right_clicked() +{ + car->turnRight(); +} diff --git a/tests/auto/blackbox/testdata/dbus-interfaces/controller.h b/tests/auto/blackbox/testdata/dbus-interfaces/controller.h new file mode 100644 index 00000000..3a63c6a0 --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-interfaces/controller.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONTROLLER_H +#define CONTROLLER_H + +#include "ui_controller.h" +#include "car_interface.h" + +class Controller : public QWidget +{ + Q_OBJECT + +public: + Controller(QWidget *parent = 0); + +protected: + void timerEvent(QTimerEvent *event); + +private slots: + void on_accelerate_clicked(); + void on_decelerate_clicked(); + void on_left_clicked(); + void on_right_clicked(); + +private: + Ui::Controller ui; + org::example::Examples::CarInterface *car; +}; + +#endif + diff --git a/tests/auto/blackbox/testdata/dbus-interfaces/controller.qbs b/tests/auto/blackbox/testdata/dbus-interfaces/controller.qbs new file mode 100644 index 00000000..b5fb3d69 --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-interfaces/controller.qbs @@ -0,0 +1,20 @@ +import qbs + +CppApplication { + name: "controller" + condition: Qt.dbus.present + Depends { name: "Qt.dbus"; required: false } + Depends { name: "Qt.widgets" } + files: [ + "controller.cpp", + "controller.h", + "controller.ui", + "main.cpp", + ] + + Group { + name: "DBUS Interface" + files: ["car.xml"] + fileTags: ["qt.dbus.interface"] + } +} diff --git a/tests/auto/blackbox/testdata/dbus-interfaces/controller.ui b/tests/auto/blackbox/testdata/dbus-interfaces/controller.ui new file mode 100644 index 00000000..379015bf --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-interfaces/controller.ui @@ -0,0 +1,64 @@ + + Controller + + + + 0 + 0 + 255 + 111 + + + + Controller + + + + 9 + + + 6 + + + + + Controller + + + Qt::AlignCenter + + + + + + + Decelerate + + + + + + + Accelerate + + + + + + + Right + + + + + + + Left + + + + + + + + diff --git a/tests/auto/blackbox/testdata/dbus-interfaces/main.cpp b/tests/auto/blackbox/testdata/dbus-interfaces/main.cpp new file mode 100644 index 00000000..fdd9fc59 --- /dev/null +++ b/tests/auto/blackbox/testdata/dbus-interfaces/main.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "controller.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Controller controller; + controller.show(); + return app.exec(); +} diff --git a/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs b/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs new file mode 100644 index 00000000..d2f3773c --- /dev/null +++ b/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs @@ -0,0 +1,40 @@ +import qbs 1.0 +import qbs.TextFile +import qbs.FileInfo + +Project { + Product { + type: "deps" + name: "product1" + Depends { name: "product2" } + Rule { + multiplex: true + Artifact { + fileTags: ["deps"] + filePath: product.name + '.deps' + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = 'generate ' + FileInfo.fileName(output.filePath); + cmd.highlight = 'codegen'; + cmd.content = JSON.stringify(product.dependencies); + cmd.sourceCode = function() { + file = new TextFile(output.filePath, TextFile.WriteOnly); + file.truncate(); + file.write(content); + file.close(); + } + return cmd; + } + } + } + Product { + type: "application" + consoleApplication: true + name: "product2" + property string narf: "zort" + Depends { name: "cpp" } + cpp.defines: ["SMURF"] + files: ["product2.cpp"] + } +} diff --git a/tests/auto/blackbox/testdata/dependenciesProperty/product2.cpp b/tests/auto/blackbox/testdata/dependenciesProperty/product2.cpp new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/dependenciesProperty/product2.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/dependency-profile-mismatch/dependency-profile-mismatch.qbs b/tests/auto/blackbox/testdata/dependency-profile-mismatch/dependency-profile-mismatch.qbs new file mode 100644 index 00000000..ad39a8c5 --- /dev/null +++ b/tests/auto/blackbox/testdata/dependency-profile-mismatch/dependency-profile-mismatch.qbs @@ -0,0 +1,14 @@ +import qbs + +Project { + property string mainProfile + property string depProfile + Product { + name: "dep" + profiles: [project.depProfile] + } + Product { + name: "main" + Depends { name: "dep"; profiles: [project.mainProfile]; } + } +} diff --git a/tests/auto/blackbox/testdata/deploymentTarget/deployment.qbs b/tests/auto/blackbox/testdata/deploymentTarget/deployment.qbs new file mode 100644 index 00000000..2abdaeaf --- /dev/null +++ b/tests/auto/blackbox/testdata/deploymentTarget/deployment.qbs @@ -0,0 +1,9 @@ +import qbs + +CppApplication { + files: ["main.c"] + cpp.minimumMacosVersion: "10.4" + cpp.minimumIosVersion: "5.0" + cpp.cFlags: ["-v"] + cpp.linkerFlags: ["-v"] +} diff --git a/tests/auto/blackbox/testdata/deploymentTarget/main.c b/tests/auto/blackbox/testdata/deploymentTarget/main.c new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/deploymentTarget/main.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/deprecated-property/deprecated-property.qbs b/tests/auto/blackbox/testdata/deprecated-property/deprecated-property.qbs new file mode 100644 index 00000000..a8efb97b --- /dev/null +++ b/tests/auto/blackbox/testdata/deprecated-property/deprecated-property.qbs @@ -0,0 +1,8 @@ +import qbs + +Product { + Depends { name: "themodule" } + themodule.newProp: true + themodule.oldProp: false + themodule.veryOldProp: false +} diff --git a/tests/auto/blackbox/testdata/deprecated-property/modules/themodule/m.qbs b/tests/auto/blackbox/testdata/deprecated-property/modules/themodule/m.qbs new file mode 100644 index 00000000..47f1ea66 --- /dev/null +++ b/tests/auto/blackbox/testdata/deprecated-property/modules/themodule/m.qbs @@ -0,0 +1,22 @@ +import qbs + +Module { + property bool newProp + property bool oldProp + property bool veryOldProp + + PropertyOptions { + name: "newProp" + description: "Use this, it's good!" + } + PropertyOptions { + name: "oldProp" + description: "Use newProp instead." + removalVersion: "99.9" + } + PropertyOptions { + name: "veryOldProp" + description: "Use newProp instead." + removalVersion: "1.3" + } +} diff --git a/tests/auto/blackbox/testdata/dynamicMultiplexRule/dynamicMultiplexRule.qbs b/tests/auto/blackbox/testdata/dynamicMultiplexRule/dynamicMultiplexRule.qbs new file mode 100644 index 00000000..d5315ac4 --- /dev/null +++ b/tests/auto/blackbox/testdata/dynamicMultiplexRule/dynamicMultiplexRule.qbs @@ -0,0 +1,33 @@ +import qbs +import qbs.TextFile + +Project { + Product { + type: ["stuff"] + Group { + files: ["one.txt", "two.txt", "three.txt"] + fileTags: ["text"] + } + Rule { + multiplex: true + inputs: "text" + outputFileTags: ["stuff"] + outputArtifacts: { + return [{ + filePath: "stuff-from-" + inputs.text.length + "-inputs", + fileTags: ["stuff"] + }]; + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.write("narf!"); + f.close(); + } + return cmd; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/dynamicMultiplexRule/one.txt b/tests/auto/blackbox/testdata/dynamicMultiplexRule/one.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/dynamicMultiplexRule/three.txt b/tests/auto/blackbox/testdata/dynamicMultiplexRule/three.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/dynamicMultiplexRule/two.txt b/tests/auto/blackbox/testdata/dynamicMultiplexRule/two.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/after/numbers.l b/tests/auto/blackbox/testdata/dynamicRuleOutputs/after/numbers.l new file mode 100644 index 00000000..d0cb4877 --- /dev/null +++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/after/numbers.l @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +/* scanner for integer and float numbers */ + +%option noyywrap + +%{ +#ifndef CRUCIAL_DEFINE +# error CRUCIAL_DEFINE is missing! +#endif + +/* need this for the call to atof() below */ +#include +%} + +DIGIT [0-9] + +%% + +{DIGIT}+ { + printf("integer: %s (%d)\n", yytext, atoi(yytext)); + } + +{DIGIT}+"."{DIGIT}* { + printf("float: %s (%g)\n", yytext, atof(yytext)); + } + +"{"[\^{}}\n]*"}" /* eat up one-line comments */ + +[ \t\n]+ /* eat up whitespace */ + +. printf("Unexpected character: %s\n", yytext); + +%% + +int main(int argc, char **argv) +{ + if (argc > 1) + yyin = fopen(argv[1], "r"); + else + yyin = stdin; + + yylex(); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js new file mode 100644 index 00000000..bd596fbc --- /dev/null +++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +// needs import qbs.TextFile + +function readFlexOptions(filePath) +{ + function splitOptions(str) + { + var options = []; + var opt = ""; + var inquote = false; + for (var i = 0; i < str.length; ++i) { + if (str[i] === '"') { + opt += '"'; + inquote = !inquote; + } else if (str[i] === ' ' && !inquote) { + options.push(opt); + opt = ""; + } else { + opt += str[i]; + } + } + if (opt.length) + options.push(opt); + return options; + } + + function unquote(str) + { + var l = str.length; + if (l > 2 && str[0] === '"' && str[l - 1] === '"') + return str.substr(1, l - 2); + return str; + } + + function parseOptionLine(result, str) + { + var options = splitOptions(str); + var re = /^(outfile|header-file)=(.*)$/; + var reres; + for (var k in options) { + re.lastIndex = 0; + reres = re.exec(options[k]); + if (reres === null) + continue; + result[reres[1]] = unquote(reres[2]); + } + } + + var tf = new TextFile(input.filePath); + var line; + var optrex = /^%option\s+(.*$)/; + var res; + var options = {}; + while (!tf.atEof()) { + line = tf.readLine(); + if (line === "%%") + break; + optrex.lastIndex = 0; + res = optrex.exec(line); + if (res === null) + continue; + parseOptionLine(options, res[1]); + } + tf.close(); + return options; +} + diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs new file mode 100644 index 00000000..04820e18 --- /dev/null +++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/genlexer.qbs @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +import qbs 1.0 +import qbs.FileInfo +import qbs.Probes +import qbs.TextFile +import "flexoptionsreader.js" as FlexOptionsReader + +Project { + Product { + name: "genlexer" + type: "application" + consoleApplication: true + Depends { name: "cpp" } + Group { + files: ["numbers.l"] + fileTags: ["flex"] + } + Probes.PathProbe { + id: flexProbe + names: ["flex"] + platformPaths: ["/usr/local/bin", "/usr/bin", "/bin"] + } + property bool isFlexAvailable: flexProbe.found + Rule { + inputs: ["flex"] + outputFileTags: ["c", "hpp"] + outputArtifacts: { + var options = FlexOptionsReader.readFlexOptions(input.filePath); + var sourceFileName = options["outfile"] || "lex.yy.c"; + var headerFileName = options["header-file"]; + var result = [{ + filePath: "GeneratedFiles/" + sourceFileName, + fileTags: ["c"], + cpp: { + defines: ["CRUCIAL_DEFINE"] + } + }]; + if (headerFileName) { + result.push({ + filePath: "GeneratedFiles/" + headerFileName, + fileTags: ["hpp"] + }); + } + return result; + } + prepare: { + var cmd; + if (product.isFlexAvailable) { + // flex is available. Let's call it. + cmd = new Command("flex", [input.filePath]); + cmd.workingDirectory = product.buildDirectory + "/GeneratedFiles"; + } else { + // No flex available here, generate some C source and header. + cmd = new JavaScriptCommand(); + cmd.sourceFileName = outputs["c"][0].filePath; + cmd.headerFileName = outputs["hpp"] ? outputs["hpp"][0].filePath : ""; + cmd.sourceCode = function() { + var fsrc = new TextFile(sourceFileName, TextFile.WriteOnly); + if (headerFileName) { + fsrc.write("#include \"" + FileInfo.fileName(headerFileName) + + "\"\n\n"); + var fhdr = new TextFile(headerFileName, TextFile.WriteOnly); + fhdr.write("// a rather empty header file\n"); + fhdr.close(); + } + fsrc.write("\n#ifndef CRUCIAL_DEFINE\n"); + fsrc.write("# error CRUCIAL_DEFINE is missing!\n"); + fsrc.write("#endif\n\n"); + fsrc.write("int main() { return 0; }\n"); + fsrc.close(); + }; + } + cmd.description = "flexing " + FileInfo.fileName(input.filePath); + return cmd; + } + } + } +} + diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/numbers.l b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/numbers.l new file mode 100644 index 00000000..26419b6c --- /dev/null +++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/numbers.l @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of the examples of Qbs. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +****************************************************************************/ + +/* scanner for integer and float numbers */ + +%option noyywrap +%option outfile="numberscanner.c" header-file="numberscanner.h" + +%{ +#ifndef CRUCIAL_DEFINE +# error CRUCIAL_DEFINE is missing! +#endif + +/* need this for the call to atof() below */ +#include +%} + +DIGIT [0-9] + +%% + +{DIGIT}+ { + printf("integer: %s (%d)\n", yytext, atoi(yytext)); + } + +{DIGIT}+"."{DIGIT}* { + printf("float: %s (%g)\n", yytext, atof(yytext)); + } + +"{"[\^{}}\n]*"}" /* eat up one-line comments */ + +[ \t\n]+ /* eat up whitespace */ + +. printf("Unexpected character: %s\n", yytext); + +%% + +int main(int argc, char **argv) +{ + if (argc > 1) + yyin = fopen(argv[1], "r"); + else + yyin = stdin; + + yylex(); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/embedInfoPlist/embedInfoPlist.qbs b/tests/auto/blackbox/testdata/embedInfoPlist/embedInfoPlist.qbs new file mode 100644 index 00000000..44106c85 --- /dev/null +++ b/tests/auto/blackbox/testdata/embedInfoPlist/embedInfoPlist.qbs @@ -0,0 +1,53 @@ +import qbs + +Project { + CppApplication { + Depends { name: "lib" } + Depends { name: "mod" } + name: "app" + bundle.isBundle: false + files: ["main.m"] + cpp.frameworks: ["Foundation"] + cpp.rpaths: ["@loader_path"] + bundle.infoPlist: ({ + "QBS": "org.qt-project.qbs.testdata.embedInfoPlist" + }) + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + DynamicLibrary { + Depends { name: "cpp" } + name: "lib" + bundle.isBundle: false + bundle.embedInfoPlist: true + files: ["main.m"] + cpp.frameworks: ["Foundation"] + cpp.sonamePrefix: "@rpath" + bundle.infoPlist: ({ + "QBS": "org.qt-project.qbs.testdata.embedInfoPlist.dylib" + }) + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + LoadableModule { + Depends { name: "cpp" } + name: "mod" + bundle.isBundle: false + bundle.embedInfoPlist: true + files: ["main.m"] + cpp.frameworks: ["Foundation"] + bundle.infoPlist: ({ + "QBS": "org.qt-project.qbs.testdata.embedInfoPlist.bundle" + }) + Group { + fileTagsFilter: product.type + qbs.install: true + } + } +} diff --git a/tests/auto/blackbox/testdata/embedInfoPlist/main.m b/tests/auto/blackbox/testdata/embedInfoPlist/main.m new file mode 100644 index 00000000..b3f36222 --- /dev/null +++ b/tests/auto/blackbox/testdata/embedInfoPlist/main.m @@ -0,0 +1,9 @@ +#import + +int main() +{ + NSDictionary *infoPlist = [[NSBundle mainBundle] infoDictionary]; + BOOL success = [[infoPlist objectForKey:@"QBS"] isEqualToString:@"org.qt-project.qbs.testdata.embedInfoPlist"]; + fprintf(success ? stdout : stderr, "%s\n", [[infoPlist description] UTF8String]); + return success ? 0 : 1; +} diff --git a/tests/auto/blackbox/testdata/enableExceptions/empty.m b/tests/auto/blackbox/testdata/enableExceptions/empty.m new file mode 100644 index 00000000..d3714dc0 --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/empty.m @@ -0,0 +1 @@ +int main2() { return 0; } diff --git a/tests/auto/blackbox/testdata/enableExceptions/empty.mm b/tests/auto/blackbox/testdata/enableExceptions/empty.mm new file mode 100644 index 00000000..fe94a49a --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/empty.mm @@ -0,0 +1 @@ +int main3() { return 0; } diff --git a/tests/auto/blackbox/testdata/enableExceptions/emptymain.cpp b/tests/auto/blackbox/testdata/enableExceptions/emptymain.cpp new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/emptymain.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/enableExceptions/exceptions-objc.qbs b/tests/auto/blackbox/testdata/enableExceptions/exceptions-objc.qbs new file mode 100644 index 00000000..74738ad1 --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/exceptions-objc.qbs @@ -0,0 +1,6 @@ +import qbs + +CppApplication { + files: ["main.m"] + cpp.frameworks: ["Foundation"] +} diff --git a/tests/auto/blackbox/testdata/enableExceptions/exceptions-objcpp-cpp.qbs b/tests/auto/blackbox/testdata/enableExceptions/exceptions-objcpp-cpp.qbs new file mode 100644 index 00000000..679cdebb --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/exceptions-objcpp-cpp.qbs @@ -0,0 +1,6 @@ +import qbs + +CppApplication { + files: ["main.cpp"] + fileTags: ["objcpp"] +} diff --git a/tests/auto/blackbox/testdata/enableExceptions/exceptions-objcpp.qbs b/tests/auto/blackbox/testdata/enableExceptions/exceptions-objcpp.qbs new file mode 100644 index 00000000..6856f53b --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/exceptions-objcpp.qbs @@ -0,0 +1,7 @@ +import qbs + +CppApplication { + files: ["main.m"] + fileTags: ["objcpp"] + cpp.frameworks: ["Foundation"] +} diff --git a/tests/auto/blackbox/testdata/enableExceptions/exceptions.qbs b/tests/auto/blackbox/testdata/enableExceptions/exceptions.qbs new file mode 100644 index 00000000..da6fdbc2 --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/exceptions.qbs @@ -0,0 +1,7 @@ +import qbs + +CppApplication { + files: ["main.cpp"] + cpp.treatWarningsAsErrors: true + cpp.defines: qbs.toolchain.contains("msvc") && !cpp.enableExceptions ? ["FORCE_FAIL_VS"] : [] +} diff --git a/tests/auto/blackbox/testdata/enableExceptions/main.cpp b/tests/auto/blackbox/testdata/enableExceptions/main.cpp new file mode 100644 index 00000000..70511e9f --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() { +#ifdef FORCE_FAIL_VS +#error "Microsoft Visual C++ cannot disable exceptions at compile-time" +#endif + throw std::runtime_error("failed"); +} diff --git a/tests/auto/blackbox/testdata/enableExceptions/main.m b/tests/auto/blackbox/testdata/enableExceptions/main.m new file mode 100644 index 00000000..86b45fc3 --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/main.m @@ -0,0 +1,5 @@ +#import + +int main() { + @throw [NSError new]; +} diff --git a/tests/auto/blackbox/testdata/enableExceptions/none.qbs b/tests/auto/blackbox/testdata/enableExceptions/none.qbs new file mode 100644 index 00000000..332af761 --- /dev/null +++ b/tests/auto/blackbox/testdata/enableExceptions/none.qbs @@ -0,0 +1,10 @@ +import qbs + +CppApplication { + files: ["emptymain.cpp"] + + Group { + condition: qbs.targetOS.contains("darwin") + files: ["empty.m", "empty.mm"] + } +} diff --git a/tests/auto/blackbox/testdata/enableRtti/main.cpp b/tests/auto/blackbox/testdata/enableRtti/main.cpp new file mode 100644 index 00000000..1a30228d --- /dev/null +++ b/tests/auto/blackbox/testdata/enableRtti/main.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class I { +public: + virtual ~I() { } + virtual void x() { } +}; + +class A : public I { + void x() override { } +}; + +class B : public I { + void x() override { } +}; + +int main() { + I *a = new A(); + B *b = dynamic_cast(a); + (void)b; + delete a; + return 0; +} diff --git a/tests/auto/blackbox/testdata/enableRtti/rtti.qbs b/tests/auto/blackbox/testdata/enableRtti/rtti.qbs new file mode 100644 index 00000000..f032bb16 --- /dev/null +++ b/tests/auto/blackbox/testdata/enableRtti/rtti.qbs @@ -0,0 +1,13 @@ +import qbs + +Project { + property bool treatAsObjcpp: false + CppApplication { + cpp.cxxLanguageVersion: "c++11" + cpp.treatWarningsAsErrors: true + Group { + files: ["main.cpp"] + fileTags: [project.treatAsObjcpp ? "objcpp" : "cpp"] + } + } +} diff --git a/tests/auto/blackbox/testdata/erroneous/nonexistentWorkingDir/project.qbs b/tests/auto/blackbox/testdata/erroneous/nonexistentWorkingDir/project.qbs new file mode 100644 index 00000000..8cf1bfaa --- /dev/null +++ b/tests/auto/blackbox/testdata/erroneous/nonexistentWorkingDir/project.qbs @@ -0,0 +1,20 @@ +import qbs + +Application { + name: "kaputt" + type: ["nutritious"] + Rule { + multiplex: true + Artifact { + filePath: "Stulle" + fileTags: ["nutritious"] + } + prepare: { + var cmd = new Command("ls"); + cmd.workingDirectory = "/does/not/exist"; + cmd.silent = true; + return [cmd]; + } + } +} + diff --git a/tests/auto/blackbox/testdata/error-info/helper.js b/tests/auto/blackbox/testdata/error-info/helper.js new file mode 100644 index 00000000..18323df4 --- /dev/null +++ b/tests/auto/blackbox/testdata/error-info/helper.js @@ -0,0 +1,9 @@ +var x; + +function doSomethingBad() { + nothinghere.works; +} + +function doSomethingEvil() { + throw "OUCH!"; +} diff --git a/tests/auto/blackbox/testdata/error-info/project.qbs b/tests/auto/blackbox/testdata/error-info/project.qbs new file mode 100644 index 00000000..a830be36 --- /dev/null +++ b/tests/auto/blackbox/testdata/error-info/project.qbs @@ -0,0 +1,72 @@ +import qbs +import "helper.js" as Helper + +Project { + property bool fail1: false + property bool fail2: false + property bool fail3: false + property bool fail4: false + property bool fail5: false + property bool fail6: false + property bool fail7: false + + Product { + name: "myproduct" + type: ["foo", "bar"] + + Rule { + inputs: ["qbs"] + + Artifact { + fileTags: ["foo"] + filePath: { + var path = "foo"; + if (project.fail1) + throw "fail1"; + return path; + } + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.sourceCode = function () { + + }; + cmd.silent = true; + if (project.fail2) + generate.an.error; + if (project.fail6) + Helper.doSomethingEvil(); + return cmd; + } + } + + Rule { + inputs: ["qbs"] + + outputFileTags: ["bar"] + outputArtifacts: { + var list = []; + list.push({ fileTags: ["bar"], filePath: "bar" }); + if (project.fail3) + throw "fail3"; + if (project.fail5) + Helper.doSomethingBad(); + return list; + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.fail7 = project.fail7; + cmd.sourceCode = function () { + if (fail7) + will.fail; + }; + cmd.silent = true; + if (project.fail4) + generate.an.error; + return cmd; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/export-rule/blubber.cpp b/tests/auto/blackbox/testdata/export-rule/blubber.cpp new file mode 100644 index 00000000..e7beea33 --- /dev/null +++ b/tests/auto/blackbox/testdata/export-rule/blubber.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void justSomeFunction() {} diff --git a/tests/auto/blackbox/testdata/export-rule/export-rule.qbs b/tests/auto/blackbox/testdata/export-rule/export-rule.qbs new file mode 100644 index 00000000..51dd787a --- /dev/null +++ b/tests/auto/blackbox/testdata/export-rule/export-rule.qbs @@ -0,0 +1,37 @@ +import qbs +import qbs.File + +Project { + Application { + name: "MyApp" + files: ["myapp.blubb"] + Depends { name: "blubber" } + Depends { name: "cpp" } + } + StaticLibrary { + name: "blubber" + files: ["blubber.cpp"] + Depends { name: "cpp" } + Export { + FileTagger { + patterns: ["*.blubb"] + fileTags: ["blubb"] + } + Rule { + inputs: ["blubb"] + Artifact { + filePath: input.completeBaseName + ".cpp" + fileTags: ["cpp"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating C++ source file."; + cmd.sourceCode = function() { + File.copy(input.filePath, output.filePath); + } + return [cmd]; + } + } + } + } +} diff --git a/tests/auto/blackbox/testdata/export-rule/myapp.blubb b/tests/auto/blackbox/testdata/export-rule/myapp.blubb new file mode 100644 index 00000000..ad87b5e8 --- /dev/null +++ b/tests/auto/blackbox/testdata/export-rule/myapp.blubb @@ -0,0 +1,8 @@ +extern void justSomeFunction(); + +int main() +{ + justSomeFunction(); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/export-to-outside-searchpath/export-to-outside-searchpath.qbs b/tests/auto/blackbox/testdata/export-to-outside-searchpath/export-to-outside-searchpath.qbs new file mode 100644 index 00000000..72c48b75 --- /dev/null +++ b/tests/auto/blackbox/testdata/export-to-outside-searchpath/export-to-outside-searchpath.qbs @@ -0,0 +1,21 @@ +import qbs + +Project { + Project { + qbsSearchPaths: ["qbs-resources"] + Product { + name: "otherProduct" + Depends { name: "dep" } + } + Product { + name: "dep" + Export { Depends { name: "aModule" } } + } + } + Project { + Product { + name: "theProduct" + Depends { name: "dep" } + } + } +} diff --git a/tests/auto/blackbox/testdata/export-to-outside-searchpath/qbs-resources/modules/aModule/aModule.qbs b/tests/auto/blackbox/testdata/export-to-outside-searchpath/qbs-resources/modules/aModule/aModule.qbs new file mode 100644 index 00000000..3e67ba10 --- /dev/null +++ b/tests/auto/blackbox/testdata/export-to-outside-searchpath/qbs-resources/modules/aModule/aModule.qbs @@ -0,0 +1,4 @@ +import qbs + +Module { +} diff --git a/tests/auto/blackbox/testdata/fileDependencies/awesomelib/awesome.h b/tests/auto/blackbox/testdata/fileDependencies/awesomelib/awesome.h new file mode 100644 index 00000000..4633f974 --- /dev/null +++ b/tests/auto/blackbox/testdata/fileDependencies/awesomelib/awesome.h @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "magnificent.h" + +void doAwesomeStuff() +{ + printf(magnificentMessage); +} + diff --git a/tests/auto/blackbox/testdata/fileDependencies/awesomelib/magnificent.h b/tests/auto/blackbox/testdata/fileDependencies/awesomelib/magnificent.h new file mode 100644 index 00000000..3b1f4692 --- /dev/null +++ b/tests/auto/blackbox/testdata/fileDependencies/awesomelib/magnificent.h @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MAGNIFICENT_H +#define MAGNIFICENT_H + +const char magnificentMessage[] = "Just. Wow."; + +#endif // MAGNIFICENT_H diff --git a/tests/auto/blackbox/testdata/fileDependencies/fileDependencies.qbs b/tests/auto/blackbox/testdata/fileDependencies/fileDependencies.qbs new file mode 100644 index 00000000..662879cc --- /dev/null +++ b/tests/auto/blackbox/testdata/fileDependencies/fileDependencies.qbs @@ -0,0 +1,13 @@ +import qbs 1.0 + +Project { + Product { + type: "application" + consoleApplication: true + name: "myapp" + Depends { name: "cpp" } + cpp.includePaths: ["awesomelib"] + files: ["src/narf.h", "src/narf.cpp", "src/zort.cpp"] + } +} + diff --git a/tests/auto/blackbox/testdata/fileDependencies/src/narf.cpp b/tests/auto/blackbox/testdata/fileDependencies/src/narf.cpp new file mode 100644 index 00000000..54f559ca --- /dev/null +++ b/tests/auto/blackbox/testdata/fileDependencies/src/narf.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +void doStuff() +{ + doAwesomeStuff(); +} + diff --git a/tests/auto/blackbox/testdata/fileDependencies/src/narf.h b/tests/auto/blackbox/testdata/fileDependencies/src/narf.h new file mode 100644 index 00000000..d247cfd1 --- /dev/null +++ b/tests/auto/blackbox/testdata/fileDependencies/src/narf.h @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void doStuff(); + diff --git a/tests/auto/blackbox/testdata/fileDependencies/src/zort.cpp b/tests/auto/blackbox/testdata/fileDependencies/src/zort.cpp new file mode 100644 index 00000000..1f9bde90 --- /dev/null +++ b/tests/auto/blackbox/testdata/fileDependencies/src/zort.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "narf.h" + +int main() +{ + doStuff(); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/find/find-cli.qbs b/tests/auto/blackbox/testdata/find/find-cli.qbs new file mode 100644 index 00000000..ab33fbf8 --- /dev/null +++ b/tests/auto/blackbox/testdata/find/find-cli.qbs @@ -0,0 +1,34 @@ +import qbs +import qbs.TextFile + +Product { + Depends { name: "cli"; required: false } + type: ["json"] + Rule { + inputs: ["qbs"] + Artifact { + filePath: ["cli.json"] + fileTags: ["json"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = output.filePath; + cmd.sourceCode = function() { + var tools = {}; + if (product.moduleProperty("cli", "present")) + tools["toolchainInstallPath"] = product.moduleProperty("cli", + "toolchainInstallPath"); + + var tf; + try { + tf = new TextFile(output.filePath, TextFile.WriteOnly); + tf.writeLine(JSON.stringify(tools, undefined, 4)); + } finally { + if (tf) + tf.close(); + } + }; + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata/frameworkStructure/BaseResource b/tests/auto/blackbox/testdata/frameworkStructure/BaseResource new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/frameworkStructure/Widget.cpp b/tests/auto/blackbox/testdata/frameworkStructure/Widget.cpp new file mode 100644 index 00000000..e17b9675 --- /dev/null +++ b/tests/auto/blackbox/testdata/frameworkStructure/Widget.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int foo() { return 0; } diff --git a/tests/auto/blackbox/testdata/frameworkStructure/Widget.h b/tests/auto/blackbox/testdata/frameworkStructure/Widget.h new file mode 100644 index 00000000..e5dacdb5 --- /dev/null +++ b/tests/auto/blackbox/testdata/frameworkStructure/Widget.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int foo(); diff --git a/tests/auto/blackbox/testdata/frameworkStructure/WidgetPrivate.h b/tests/auto/blackbox/testdata/frameworkStructure/WidgetPrivate.h new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/blackbox/testdata/frameworkStructure/WidgetPrivate.h @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/blackbox/testdata/frameworkStructure/en.lproj/EnglishResource b/tests/auto/blackbox/testdata/frameworkStructure/en.lproj/EnglishResource new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/frameworkStructure/frameworkStructure.qbs b/tests/auto/blackbox/testdata/frameworkStructure/frameworkStructure.qbs new file mode 100644 index 00000000..39b3a1b5 --- /dev/null +++ b/tests/auto/blackbox/testdata/frameworkStructure/frameworkStructure.qbs @@ -0,0 +1,15 @@ +import qbs + +Project { + property bool includeHeaders: true + Library { + Depends { name: "cpp" } + + name: "Widget" + bundle.isBundle: true + bundle.publicHeaders: project.includeHeaders ? ["Widget.h"] : undefined + bundle.privateHeaders: project.includeHeaders ? ["WidgetPrivate.h"] : base + bundle.resources: ["BaseResource", "en.lproj/EnglishResource"] + files: ["Widget.cpp"].concat(bundle.publicHeaders || []).concat(bundle.privateHeaders || []) + } +} diff --git a/tests/auto/blackbox/testdata/generated-artifact-as-input-to-dynamic-rule/input.txt b/tests/auto/blackbox/testdata/generated-artifact-as-input-to-dynamic-rule/input.txt new file mode 100644 index 00000000..37081c64 --- /dev/null +++ b/tests/auto/blackbox/testdata/generated-artifact-as-input-to-dynamic-rule/input.txt @@ -0,0 +1 @@ +old.txt diff --git a/tests/auto/blackbox/testdata/generated-artifact-as-input-to-dynamic-rule/p.qbs b/tests/auto/blackbox/testdata/generated-artifact-as-input-to-dynamic-rule/p.qbs new file mode 100644 index 00000000..f26d9226 --- /dev/null +++ b/tests/auto/blackbox/testdata/generated-artifact-as-input-to-dynamic-rule/p.qbs @@ -0,0 +1,55 @@ +import qbs +import qbs.File +import qbs.TextFile + +Product { + type: ["mytype.final"] + + Group { + files: ["input.txt"] + fileTags: ["mytype.in"] + } + + Rule { + inputs: ["mytype.in"] + Artifact { + filePath: "output.txt" + fileTags: ["mytype.out"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating " + output.fileName; + cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); }; + return [cmd]; + } + } + + Rule { + inputs: ["mytype.out"] + outputFileTags: ["mytype.final", "dummy"] + outputArtifacts: { + var file; + var inFile = new TextFile(input.filePath, TextFile.ReadOnly); + try { + file = inFile.readLine(); + if (!file) + throw "no file name found"; + } finally { + inFile.close(); + } + return [ + { filePath: file, fileTags: ["mytype.final"] }, + { filePath: "dummy", fileTags: ["dummy"], alwaysUpdated: false } + ]; + } + prepare: { + var cmd = new JavaScriptCommand(); + var output = outputs["mytype.final"][0]; + cmd.description = "generating " + output.fileName; + cmd.output = output; + cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); }; + return [cmd]; + } + } +} + diff --git a/tests/auto/blackbox/testdata/group-condition-change/group-condition-change.qbs b/tests/auto/blackbox/testdata/group-condition-change/group-condition-change.qbs new file mode 100644 index 00000000..d8dccde3 --- /dev/null +++ b/tests/auto/blackbox/testdata/group-condition-change/group-condition-change.qbs @@ -0,0 +1,27 @@ +import qbs + +Project { + property bool kaputt: true + Product { + type: ["kaputt"] + Group { + name: "kaputt" + condition: project.kaputt + files: "input_kaputt.txt" + fileTags: "input.kaputt" + } + Rule { + inputs: "input.kaputt" + Artifact { + filePath: "output.kaputt" + fileTags: "kaputt" + } + prepare: { + var cmd = new Command("jibbetnich", [output.filePath]); + cmd.silent = true; + return cmd; + } + } + } +} + diff --git a/tests/auto/blackbox/testdata/group-condition-change/input_kaputt.txt b/tests/auto/blackbox/testdata/group-condition-change/input_kaputt.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/group-location-warning/GroupInSameDir.qbs b/tests/auto/blackbox/testdata/group-location-warning/GroupInSameDir.qbs new file mode 100644 index 00000000..bfcc4789 --- /dev/null +++ b/tests/auto/blackbox/testdata/group-location-warning/GroupInSameDir.qbs @@ -0,0 +1,5 @@ +import qbs + +Group { + files: ["referenced-from-group-in-same-dir.txt"] +} diff --git a/tests/auto/blackbox/testdata/group-location-warning/group-location-warning.qbs b/tests/auto/blackbox/testdata/group-location-warning/group-location-warning.qbs new file mode 100644 index 00000000..3a43d54a --- /dev/null +++ b/tests/auto/blackbox/testdata/group-location-warning/group-location-warning.qbs @@ -0,0 +1,27 @@ +import qbs +import "subdir/ParentInOtherDir.qbs" as ParentInOtherDir +import "subdir/AGroupInOtherDir.qbs" as AGroupInOtherDir +import "subdir/OtherGroupInOtherDir.qbs" as OtherGroupInOtherDir +import "subdir/YetAnotherGroupInOtherDir.qbs" as YetAnotherGroupInOtherDir +import "subdir/AndAnotherGroupInOtherDir.qbs" as AndAnotherGroupInOtherDir + +Project { + ParentInOtherDir { + name: "p1" + Depends { name: "gm" } + + Group { + files: ["referenced-from-product.txt"] + } + GroupInSameDir { } + AGroupInOtherDir { } + OtherGroupInOtherDir { } + YetAnotherGroupInOtherDir { } + AndAnotherGroupInOtherDir { } + } + + Product { + name: "p2" + AGroupInOtherDir { } + } +} diff --git a/tests/auto/blackbox/testdata/group-location-warning/modules/gm/gm.qbs b/tests/auto/blackbox/testdata/group-location-warning/modules/gm/gm.qbs new file mode 100644 index 00000000..8bdbbaf5 --- /dev/null +++ b/tests/auto/blackbox/testdata/group-location-warning/modules/gm/gm.qbs @@ -0,0 +1,7 @@ +import qbs + +Module { + Group { + files: ["referenced-from-module.txt"] + } +} diff --git a/tests/auto/blackbox/testdata/group-location-warning/referenced-from-group-in-other-dir-via-wildcard.wc b/tests/auto/blackbox/testdata/group-location-warning/referenced-from-group-in-other-dir-via-wildcard.wc new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/group-location-warning/referenced-from-group-in-other-dir.txt b/tests/auto/blackbox/testdata/group-location-warning/referenced-from-group-in-other-dir.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/group-location-warning/referenced-from-group-in-same-dir.txt b/tests/auto/blackbox/testdata/group-location-warning/referenced-from-group-in-same-dir.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/group-location-warning/referenced-from-module.txt b/tests/auto/blackbox/testdata/group-location-warning/referenced-from-module.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/group-location-warning/referenced-from-parent-in-other-dir.txt b/tests/auto/blackbox/testdata/group-location-warning/referenced-from-parent-in-other-dir.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/group-location-warning/referenced-from-product.txt b/tests/auto/blackbox/testdata/group-location-warning/referenced-from-product.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/group-location-warning/referenced-via-absolute-path.txt b/tests/auto/blackbox/testdata/group-location-warning/referenced-via-absolute-path.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/group-location-warning/referenced-via-absolute-prefix.txt b/tests/auto/blackbox/testdata/group-location-warning/referenced-via-absolute-prefix.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/group-location-warning/subdir/AGroupInOtherDir.qbs b/tests/auto/blackbox/testdata/group-location-warning/subdir/AGroupInOtherDir.qbs new file mode 100644 index 00000000..104b389b --- /dev/null +++ b/tests/auto/blackbox/testdata/group-location-warning/subdir/AGroupInOtherDir.qbs @@ -0,0 +1,5 @@ +import qbs + +Group { + files: ["referenced-from-group-in-other-dir.txt"] +} diff --git a/tests/auto/blackbox/testdata/group-location-warning/subdir/AndAnotherGroupInOtherDir.qbs b/tests/auto/blackbox/testdata/group-location-warning/subdir/AndAnotherGroupInOtherDir.qbs new file mode 100644 index 00000000..f122fc39 --- /dev/null +++ b/tests/auto/blackbox/testdata/group-location-warning/subdir/AndAnotherGroupInOtherDir.qbs @@ -0,0 +1,6 @@ +import qbs + +Group { + prefix: "./" + files: ["*.wc"] +} diff --git a/tests/auto/blackbox/testdata/group-location-warning/subdir/OtherGroupInOtherDir.qbs b/tests/auto/blackbox/testdata/group-location-warning/subdir/OtherGroupInOtherDir.qbs new file mode 100644 index 00000000..8c51ef2f --- /dev/null +++ b/tests/auto/blackbox/testdata/group-location-warning/subdir/OtherGroupInOtherDir.qbs @@ -0,0 +1,5 @@ +import qbs + +Group { + files: [product.sourceDirectory + "/referenced-via-absolute-path.txt"] +} diff --git a/tests/auto/blackbox/testdata/group-location-warning/subdir/ParentInOtherDir.qbs b/tests/auto/blackbox/testdata/group-location-warning/subdir/ParentInOtherDir.qbs new file mode 100644 index 00000000..c1458624 --- /dev/null +++ b/tests/auto/blackbox/testdata/group-location-warning/subdir/ParentInOtherDir.qbs @@ -0,0 +1,7 @@ +import qbs + +Product { + Group { + files: ["referenced-from-parent-in-other-dir.txt"] + } +} diff --git a/tests/auto/blackbox/testdata/group-location-warning/subdir/YetAnotherGroupInOtherDir.qbs b/tests/auto/blackbox/testdata/group-location-warning/subdir/YetAnotherGroupInOtherDir.qbs new file mode 100644 index 00000000..a3e97768 --- /dev/null +++ b/tests/auto/blackbox/testdata/group-location-warning/subdir/YetAnotherGroupInOtherDir.qbs @@ -0,0 +1,6 @@ +import qbs + +Group { + prefix: product.sourceDirectory + '/' + files: ["referenced-via-absolute-prefix.txt"] +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/groups-in-modules.qbs b/tests/auto/blackbox/testdata/groups-in-modules/groups-in-modules.qbs new file mode 100644 index 00000000..5fa09c4d --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/groups-in-modules.qbs @@ -0,0 +1,24 @@ +import qbs + +Project { + Product { + Depends { name: "dep" } + Depends { name: "helper" } + Depends { + name: "helper3" + required: false + } + type: ["diamond"] + + files: [ + "rock.coal" + ] + } + + Product { + name: "dep" + Export { + Depends { name: "helper4" } + } + } +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/chunk.coal b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/chunk.coal new file mode 100644 index 00000000..9bed41ac --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/chunk.coal @@ -0,0 +1 @@ +chunk diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/diamondc.c b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/diamondc.c new file mode 100644 index 00000000..448a9404 --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/diamondc.c @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(stderr, "usage: diamondc input.coal output.diamond"); + return 1; + } + + FILE *in = fopen(argv[1], "r"); + if (in) { + fclose(in); + } else { + return 1; + } + + FILE *out = fopen(argv[2], "w"); + if (out) { + fprintf(out, "diamond\n"); + fclose(out); + } else { + return 1; + } + + return 0; +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/helper.qbs b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/helper.qbs new file mode 100644 index 00000000..cd064d76 --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/helper.qbs @@ -0,0 +1,48 @@ +import qbs +import qbs.FileInfo + +Module { + Depends { name: "cpp" } + Depends { name: "helper2" } + + additionalProductTypes: ["diamond"] + + Group { + name: "Helper Sources" + prefix: path + "/" + files: ["diamondc.c"] + } + + Group { + name: "Additional Chunk" + prefix: path + "/" + files: ["chunk.coal"] + } + + Group { + name: "some other file from helper" + prefix: project.sourceDirectory + '/' + files: ["someotherfile2.txt"] + } + + FileTagger { + patterns: ["*.coal"] + fileTags: ["coal"] + } + + Rule { + inputs: ["coal"] + explicitlyDependsOn: ["application"] + + Artifact { + filePath: FileInfo.joinPaths(product.destinationDirectory, input.baseName + ".diamond") + fileTags: ["diamond"] + } + + prepare: { + var cmd = new Command(FileInfo.joinPaths(product.buildDirectory, product.targetName), [input.filePath, output.filePath]); + cmd.description = "compile " + input.fileName + " => " + output.fileName; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper2/helper2.c b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper2/helper2.c new file mode 100644 index 00000000..36f0b85f --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper2/helper2.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void helper2() {} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper2/helper2.qbs b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper2/helper2.qbs new file mode 100644 index 00000000..d1de5a11 --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper2/helper2.qbs @@ -0,0 +1,17 @@ +import qbs + +Module { + Depends { name: "cpp" } + + Group { + name: "Helper2 Sources" + prefix: path + "/" + files: ["helper2.c"] + } + + Group { + name: "some other file from helper2" + prefix: product.sourceDirectory + '/' + files: ["someotherfile.txt"] + } +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper3/helper3.c b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper3/helper3.c new file mode 100644 index 00000000..475e788e --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper3/helper3.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void helper3() {} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper3/helper3.qbs b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper3/helper3.qbs new file mode 100644 index 00000000..c9212009 --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper3/helper3.qbs @@ -0,0 +1,13 @@ +import qbs + +Module { + Depends { name: "cpp" } + + Group { + name: "Helper3 Sources" + prefix: path + "/" + files: ["helper3.c"] + } + + validate: { throw "Nope."; } +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper4/helper4.c b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper4/helper4.c new file mode 100644 index 00000000..9605e6cb --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper4/helper4.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void helper4() {} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper4/helper4.qbs b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper4/helper4.qbs new file mode 100644 index 00000000..568ccb5f --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper4/helper4.qbs @@ -0,0 +1,12 @@ +import qbs + +Module { + Depends { name: "cpp" } + Depends { name: "helper5" } + + Group { + name: "Helper4 Sources" + prefix: path + "/" + files: ["helper4.c"] + } +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper5/helper5.c b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper5/helper5.c new file mode 100644 index 00000000..064e0f9a --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper5/helper5.c @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void helper5() { +#ifndef COMPILE_FIX + nothatcantwork +#endif +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper5/helper5.qbs b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper5/helper5.qbs new file mode 100644 index 00000000..d74f9890 --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper5/helper5.qbs @@ -0,0 +1,16 @@ +import qbs + +Module { + Depends { name: "cpp" } + Depends { + name: "helper6" + required: false + } + + Group { + name: "Helper5 Sources" + prefix: path + "/" + files: ["helper5.c"] + cpp.defines: ["COMPILE_FIX"] + } +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper6/helper6.c b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper6/helper6.c new file mode 100644 index 00000000..da3ac96d --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper6/helper6.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void helper6() {} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper6/helper6.qbs b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper6/helper6.qbs new file mode 100644 index 00000000..6c72ab79 --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper6/helper6.qbs @@ -0,0 +1,13 @@ +import qbs + +Module { + Depends { name: "cpp" } + + Group { + name: "Helper6 Sources" + prefix: path + "/" + files: ["helper6.c"] + } + + validate: { throw "Go away."; } +} diff --git a/tests/auto/blackbox/testdata/groups-in-modules/rock.coal b/tests/auto/blackbox/testdata/groups-in-modules/rock.coal new file mode 100644 index 00000000..d816c96f --- /dev/null +++ b/tests/auto/blackbox/testdata/groups-in-modules/rock.coal @@ -0,0 +1 @@ +coal diff --git a/tests/auto/blackbox/testdata/groups-in-modules/someotherfile.txt b/tests/auto/blackbox/testdata/groups-in-modules/someotherfile.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/groups-in-modules/someotherfile2.txt b/tests/auto/blackbox/testdata/groups-in-modules/someotherfile2.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/ib/assetcatalog/EmptyStoryboard.storyboard b/tests/auto/blackbox/testdata/ib/assetcatalog/EmptyStoryboard.storyboard new file mode 100644 index 00000000..71b6b685 --- /dev/null +++ b/tests/auto/blackbox/testdata/ib/assetcatalog/EmptyStoryboard.storyboard @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/auto/blackbox/testdata/ib/assetcatalog/MainMenu.xib b/tests/auto/blackbox/testdata/ib/assetcatalog/MainMenu.xib new file mode 100644 index 00000000..14312411 --- /dev/null +++ b/tests/auto/blackbox/testdata/ib/assetcatalog/MainMenu.xib @@ -0,0 +1,680 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/auto/blackbox/testdata/ib/assetcatalog/Storyboard.storyboard b/tests/auto/blackbox/testdata/ib/assetcatalog/Storyboard.storyboard new file mode 100644 index 00000000..41510ae2 --- /dev/null +++ b/tests/auto/blackbox/testdata/ib/assetcatalog/Storyboard.storyboard @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/auto/blackbox/testdata/ib/assetcatalog/assetcatalogempty.qbs b/tests/auto/blackbox/testdata/ib/assetcatalog/assetcatalogempty.qbs new file mode 100644 index 00000000..e79d0dd1 --- /dev/null +++ b/tests/auto/blackbox/testdata/ib/assetcatalog/assetcatalogempty.qbs @@ -0,0 +1,19 @@ +import qbs + +Project { + property bool includeIconset + + CppApplication { + Depends { name: "ib" } + files: { + var filez = ["main.c", "MainMenu.xib"]; + if (project.includeIconset) + filez.push("empty.xcassets/empty.iconset"); + else + filez.push("empty.xcassets"); + if (qbs.hostOSVersionMinor >= 10) // need macOS 10.10 to build SBs + filez.push("Storyboard.storyboard"); + return filez; + } + } +} diff --git a/tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16.png b/tests/auto/blackbox/testdata/ib/assetcatalog/empty.xcassets/empty.iconset/icon_16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..60365798f18a376a1193bca9e6044cc778c6042a GIT binary patch literal 649 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z81_maE%#etZ2wxwolpi<;HsXMd|v6mX?>t*GR!IU;uq7Dc2xlVci@>1|NgPQZ$Q4%pMVYC< z00ISrouQ3Bh8R@6jXo%hkirZSAz)EpjM#D6=)+^zj!XI29Z;fC@^o+tIX_n~F(p4KRj(qq0H~UQ!KT6r$jnVGNmQuF&B-ga zs<2f8tFQvHLBje<3ScEA*|tg%z5xo(`9-M;rg|oN21<5Z3JMA~MJZ`kK`w4k?LeNb zQbtKhft9{~d3m{Bxv^e;QM$gNrKP35fswwEkuFe$ZgFK^Nn(X=Ua>O75STeGsl~}f znFS@8`FRQ;a}$&DOG|8(lt3220mPlD6`2T|@`|C}0(wv%B%^PrXP}QwTS;ab4s9SA zh&HglAlBJ{46_QztVqp?bji$3%_{~v&CbLIYzc-q!kI|=B5>$K5=YVpa)p(DQD!PI zfIz`uXK163AqG`%qYnxrq%ea-2v`&tBX(Ri`taDb<5E6$2b5@xJY5_^A~@e(aO7oR z;9xd9us>a4#)Hd}JC@$uT@houP5&(eBNGdUfPzBlpi<;HsXMd|v6mX?>t*GR!IU;uq7Dc2xlVci@>1|NgPQZ$Q4%pMVYC< z00ISrouQ3Bh8R@6jXo%hkirZSAz)EpjM#D6=)+^zj!XI29Z;fC@^o+tIX_n~F(p4KRj(qq0H~UQ!KT6r$jnVGNmQuF&B-ga zs<2f8tFQvHLBje<3ScEA*|tg%z5xo(`9-M;rg|oN21<5Z3JMA~MJZ`kK`w4k?LeNb zQbtKhft9{~d3m{Bxv^e;QM$gNrKP35fswwEkuFe$ZgFK^Nn(X=Ua>O75STeGsl~}f znFS@8`FRQ;a}$&DOG|8(lt3220mPlD6`2T|@`|C}0(wv%B%^PrXP}QwTS;ab4s9SA zh&HglAlBJ{46_QztVqp?bji$3%_{~v&CbLIYzc-q!kI|=B5>$K5=YVpa)p(DQD!PI zfIz`uXK163AqG`%qYnxrq%ea-2v`&tBX(Ri`taDb<5E6$2b5@xJY5_^A~@e(aO7oR z;9xd9us>a4#)Hd}JC@$uT@houP5&(eBNGdUfPzBlpi<;HsXMd|v6mX?>t*GR!IU;uq7Dc2xlVci@>1|NgPQZ$Q4%pMVYC< z00ISrouQ3Bh8R@6jXo%hkirZSAz)EpjM#D6=)+^zj!XI29Z;fC@^o+tIX_n~F(p4KRj(qq0H~UQ!KT6r$jnVGNmQuF&B-ga zs<2f8tFQvHLBje<3ScEA*|tg%z5xo(`9-M;rg|oN21<5Z3JMA~MJZ`kK`w4k?LeNb zQbtKhft9{~d3m{Bxv^e;QM$gNrKP35fswwEkuFe$ZgFK^Nn(X=Ua>O75STeGsl~}f znFS@8`FRQ;a}$&DOG|8(lt3220mPlD6`2T|@`|C}0(wv%B%^PrXP}QwTS;ab4s9SA zh&HglAlBJ{46_QztVqp?bji$3%_{~v&CbLIYzc-q!kI|=B5>$K5=YVpa)p(DQD!PI zfIz`uXK163AqG`%qYnxrq%ea-2v`&tBX(Ri`taDb<5E6$2b5@xJY5_^A~@e(aO7oR z;9xd9us>a4#)Hd}JC@$uT@houP5&(eBNGdUfPzBlpi<;HsXMd|v6mX?>t*GR!IU;uq7Dc2xlVci@>1|NgPQZ$Q4%pMVYC< z00ISrouQ3Bh8R@6jXo%hkirZSAz)EpjM#D6=)+^zj!XI29Z;fC@^o+tIX_n~F(p4KRj(qq0H~UQ!KT6r$jnVGNmQuF&B-ga zs<2f8tFQvHLBje<3ScEA*|tg%z5xo(`9-M;rg|oN21<5Z3JMA~MJZ`kK`w4k?LeNb zQbtKhft9{~d3m{Bxv^e;QM$gNrKP35fswwEkuFe$ZgFK^Nn(X=Ua>O75STeGsl~}f znFS@8`FRQ;a}$&DOG|8(lt3220mPlD6`2T|@`|C}0(wv%B%^PrXP}QwTS;ab4s9SA zh&HglAlBJ{46_QztVqp?bji$3%_{~v&CbLIYzc-q!kI|=B5>$K5=YVpa)p(DQD!PI zfIz`uXK163AqG`%qYnxrq%ea-2v`&tBX(Ri`taDb<5E6$2b5@xJY5_^A~@e(aO7oR z;9xd9us>a4#)Hd}JC@$uT@houP5&(eBNGdUfPzBlpi<;HsXMd|v6mX?>t*GR!IU;uq7Dc2xlVci@>1|NgPQZ$Q4%pMVYC< z00ISrouQ3Bh8R@6jXo%hkirZSAz)EpjM#D6=)+^zj!XI29Z;fC@^o+tIX_n~F(p4KRj(qq0H~UQ!KT6r$jnVGNmQuF&B-ga zs<2f8tFQvHLBje<3ScEA*|tg%z5xo(`9-M;rg|oN21<5Z3JMA~MJZ`kK`w4k?LeNb zQbtKhft9{~d3m{Bxv^e;QM$gNrKP35fswwEkuFe$ZgFK^Nn(X=Ua>O75STeGsl~}f znFS@8`FRQ;a}$&DOG|8(lt3220mPlD6`2T|@`|C}0(wv%B%^PrXP}QwTS;ab4s9SA zh&HglAlBJ{46_QztVqp?bji$3%_{~v&CbLIYzc-q!kI|=B5>$K5=YVpa)p(DQD!PI zfIz`uXK163AqG`%qYnxrq%ea-2v`&tBX(Ri`taDb<5E6$2b5@xJY5_^A~@e(aO7oR z;9xd9us>a4#)Hd}JC@$uT@houP5&(eBNGdUfPzBlpi<;HsXMd|v6mX?>t*GR!IU;uq7Dc2xlVci@>1|NgPQZ$Q4%pMVYC< z00ISrouQ3Bh8R@6jXo%hkirZSAz)EpjM#D6=)+^zj!XI29Z;fC@^o+tIX_n~F(p4KRj(qq0H~UQ!KT6r$jnVGNmQuF&B-ga zs<2f8tFQvHLBje<3ScEA*|tg%z5xo(`9-M;rg|oN21<5Z3JMA~MJZ`kK`w4k?LeNb zQbtKhft9{~d3m{Bxv^e;QM$gNrKP35fswwEkuFe$ZgFK^Nn(X=Ua>O75STeGsl~}f znFS@8`FRQ;a}$&DOG|8(lt3220mPlD6`2T|@`|C}0(wv%B%^PrXP}QwTS;ab4s9SA zh&HglAlBJ{46_QztVqp?bji$3%_{~v&CbLIYzc-q!kI|=B5>$K5=YVpa)p(DQD!PI zfIz`uXK163AqG`%qYnxrq%ea-2v`&tBX(Ri`taDb<5E6$2b5@xJY5_^A~@e(aO7oR z;9xd9us>a4#)Hd}JC@$uT@houP5&(eBNGdUfPzB diff --git a/tests/auto/blackbox/testdata/imports-conflict/imports-conflict.qbs b/tests/auto/blackbox/testdata/imports-conflict/imports-conflict.qbs new file mode 100644 index 00000000..1c6be24b --- /dev/null +++ b/tests/auto/blackbox/testdata/imports-conflict/imports-conflict.qbs @@ -0,0 +1,11 @@ +import qbs + +Project { + Product { + name: "Utils" + } + Product { + Depends { name: "Utils" } + Depends { name: "themodule" } + } +} diff --git a/tests/auto/blackbox/testdata/imports-conflict/modules/themodule/m.qbs b/tests/auto/blackbox/testdata/imports-conflict/modules/themodule/m.qbs new file mode 100644 index 00000000..48145d38 --- /dev/null +++ b/tests/auto/blackbox/testdata/imports-conflict/modules/themodule/m.qbs @@ -0,0 +1,5 @@ +import "utils.js" as Utils + +Module { + validate: { Utils.helper(); } +} diff --git a/tests/auto/blackbox/testdata/imports-conflict/modules/themodule/utils.js b/tests/auto/blackbox/testdata/imports-conflict/modules/themodule/utils.js new file mode 100644 index 00000000..ad54d4d0 --- /dev/null +++ b/tests/auto/blackbox/testdata/imports-conflict/modules/themodule/utils.js @@ -0,0 +1 @@ +function helper() { console.info("helper"); } diff --git a/tests/auto/blackbox/testdata/infoplist/infoplist.qbs b/tests/auto/blackbox/testdata/infoplist/infoplist.qbs new file mode 100644 index 00000000..58cb361d --- /dev/null +++ b/tests/auto/blackbox/testdata/infoplist/infoplist.qbs @@ -0,0 +1,6 @@ +import qbs + +CppApplication { + cpp.minimumMacosVersion: "10.7" + files: ["main.c"] +} diff --git a/tests/auto/blackbox/testdata/infoplist/main.c b/tests/auto/blackbox/testdata/infoplist/main.c new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/infoplist/main.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/innosetup/inc/qbsinc.iss b/tests/auto/blackbox/testdata/innosetup/inc/qbsinc.iss new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/innosetup/innosetup.qbs b/tests/auto/blackbox/testdata/innosetup/innosetup.qbs new file mode 100644 index 00000000..5fe23a73 --- /dev/null +++ b/tests/auto/blackbox/testdata/innosetup/innosetup.qbs @@ -0,0 +1,21 @@ +import qbs +import qbs.FileInfo + +Project { + InnoSetup { + name: "QbsSetup" + targetName: "qbs.setup.test" + version: "1.5" + files: [ + "test.iss" + ] + innosetup.verboseOutput: true + innosetup.includePaths: ["inc"] + innosetup.defines: ["MyProgram=" + name, "MyProgramVersion=" + version] + innosetup.compilerFlags: ["/V9"] + } + InnoSetup { + name: "Example1" + files: [FileInfo.joinPaths(innosetup.toolchainInstallPath, "Examples", name + ".iss")] + } +} diff --git a/tests/auto/blackbox/testdata/innosetup/test.iss b/tests/auto/blackbox/testdata/innosetup/test.iss new file mode 100644 index 00000000..f9f9195a --- /dev/null +++ b/tests/auto/blackbox/testdata/innosetup/test.iss @@ -0,0 +1,6 @@ +#include "qbsinc.iss" + +[Setup] +AppName={#MyProgram} +AppVersion={#MyProgramVersion} +DefaultDirName={pf}\{#MyProgram} diff --git a/tests/auto/blackbox/testdata/inputs-from-dependencies/file1.txt b/tests/auto/blackbox/testdata/inputs-from-dependencies/file1.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/inputs-from-dependencies/file2.txt b/tests/auto/blackbox/testdata/inputs-from-dependencies/file2.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/inputs-from-dependencies/file3.txt b/tests/auto/blackbox/testdata/inputs-from-dependencies/file3.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/inputs-from-dependencies/file4.txt b/tests/auto/blackbox/testdata/inputs-from-dependencies/file4.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/inputs-from-dependencies/project.qbs b/tests/auto/blackbox/testdata/inputs-from-dependencies/project.qbs new file mode 100644 index 00000000..1b40abae --- /dev/null +++ b/tests/auto/blackbox/testdata/inputs-from-dependencies/project.qbs @@ -0,0 +1,51 @@ +import qbs + +Project { + Product { + name: "TextFileContainer1" + type: "txt_container" + Group { + files: ["file1.txt", "file2.txt"] + fileTags: "txt" + } + } + Product { + name: "TextFileContainer2" + Group { + files: ["file3.txt"] + fileTags: "txt" + } + } + Product { + name: "TextFileContainer3" + Group { + files: ["file4.txt"] + fileTags: "txt" + } + } + + Product { + name: "TextFileGatherer" + type: "printed_txt" + Depends { name: "TextFileContainer1" } + Depends { name: "TextFileContainer2" } + Rule { + inputsFromDependencies: "txt" + multiplex: true + Artifact { + filePath: "blubb" + fileTags: "printed_txt" + alwaysUpdated: false + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Gathering text files"; + cmd.sourceCode = function() { + for (i in inputs.txt) + console.info(inputs.txt[i].filePath); + }; + return cmd; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/install-duplicates/dir1/file1.txt b/tests/auto/blackbox/testdata/install-duplicates/dir1/file1.txt new file mode 100644 index 00000000..77621b33 --- /dev/null +++ b/tests/auto/blackbox/testdata/install-duplicates/dir1/file1.txt @@ -0,0 +1 @@ +dir1/file1 diff --git a/tests/auto/blackbox/testdata/install-duplicates/dir1/file2.txt b/tests/auto/blackbox/testdata/install-duplicates/dir1/file2.txt new file mode 100644 index 00000000..e66d24b3 --- /dev/null +++ b/tests/auto/blackbox/testdata/install-duplicates/dir1/file2.txt @@ -0,0 +1 @@ +dir1/file2 diff --git a/tests/auto/blackbox/testdata/install-duplicates/dir2/file1.txt b/tests/auto/blackbox/testdata/install-duplicates/dir2/file1.txt new file mode 100644 index 00000000..2600abca --- /dev/null +++ b/tests/auto/blackbox/testdata/install-duplicates/dir2/file1.txt @@ -0,0 +1 @@ +dir2/file1 diff --git a/tests/auto/blackbox/testdata/install-duplicates/dir2/file2.txt b/tests/auto/blackbox/testdata/install-duplicates/dir2/file2.txt new file mode 100644 index 00000000..1347b5fb --- /dev/null +++ b/tests/auto/blackbox/testdata/install-duplicates/dir2/file2.txt @@ -0,0 +1 @@ +dir2/file2 diff --git a/tests/auto/blackbox/testdata/install-duplicates/dir2/file3.txt b/tests/auto/blackbox/testdata/install-duplicates/dir2/file3.txt new file mode 100644 index 00000000..fe4ab6e3 --- /dev/null +++ b/tests/auto/blackbox/testdata/install-duplicates/dir2/file3.txt @@ -0,0 +1 @@ +dir2/file3 diff --git a/tests/auto/blackbox/testdata/install-duplicates/install-duplicates.qbs b/tests/auto/blackbox/testdata/install-duplicates/install-duplicates.qbs new file mode 100644 index 00000000..2f91ba56 --- /dev/null +++ b/tests/auto/blackbox/testdata/install-duplicates/install-duplicates.qbs @@ -0,0 +1,10 @@ +import qbs + +Product { + Group { + files: ["*.txt"] + prefix: "**/" + qbs.install: true + qbs.installDir: "files" + } +} diff --git a/tests/auto/blackbox/testdata/install-tree/data/foo.txt b/tests/auto/blackbox/testdata/install-tree/data/foo.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/install-tree/data/subdir1/bar.txt b/tests/auto/blackbox/testdata/install-tree/data/subdir1/bar.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/install-tree/data/subdir2/baz.txt b/tests/auto/blackbox/testdata/install-tree/data/subdir2/baz.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/install-tree/main.cpp b/tests/auto/blackbox/testdata/install-tree/main.cpp new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/install-tree/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/install-tree/project.qbs b/tests/auto/blackbox/testdata/install-tree/project.qbs new file mode 100644 index 00000000..cbd7300d --- /dev/null +++ b/tests/auto/blackbox/testdata/install-tree/project.qbs @@ -0,0 +1,11 @@ +import qbs + +CppApplication { + files: ["main.cpp"] + Group { + files: ["data/**/*.txt"] + qbs.install: true + qbs.installDir: "content" + qbs.installSourceBase: "data" + } +} diff --git a/tests/auto/blackbox/testdata/installable/installable.qbs b/tests/auto/blackbox/testdata/installable/installable.qbs new file mode 100644 index 00000000..c9d579f6 --- /dev/null +++ b/tests/auto/blackbox/testdata/installable/installable.qbs @@ -0,0 +1,43 @@ +import qbs +import qbs.TextFile + +Project { + CppApplication { + type: ["application"] + name: "app" + Group { + files: ["main.cpp"] + qbs.install: true + } + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + Product { + name: "install-list" + type: ["install-list"] + Depends { name: "app" } + Rule { + multiplex: true + inputsFromDependencies: ["installable"] + Artifact { + filePath: "installed-files.txt" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating " + output.fileName; + cmd.sourceCode = function() { + var file = new TextFile(output.filePath, TextFile.WriteOnly); + for (var i = 0; i < inputs.installable.length; ++i) + file.writeLine(inputs.installable[i].filePath); + file.close(); + }; + return [cmd]; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/installable/main.cpp b/tests/auto/blackbox/testdata/installable/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/installable/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/installed-source-files/main.cpp b/tests/auto/blackbox/testdata/installed-source-files/main.cpp new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/installed-source-files/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/installed-source-files/project.qbs b/tests/auto/blackbox/testdata/installed-source-files/project.qbs new file mode 100644 index 00000000..1aa56455 --- /dev/null +++ b/tests/auto/blackbox/testdata/installed-source-files/project.qbs @@ -0,0 +1,20 @@ +import qbs + +CppApplication { + consoleApplication: true + files: ["main.cpp"] + Group { + fileTagsFilter: ["cpp"] + qbs.install: true + } + Group { // this overwrites the properties of the group below + fileTagsFilter: ["text"] + qbs.install: true + } + Group { + files: ["readme.txt"] + fileTags: ["text"] + qbs.install: false + } +} + diff --git a/tests/auto/blackbox/testdata/installed-source-files/readme.txt b/tests/auto/blackbox/testdata/installed-source-files/readme.txt new file mode 100644 index 00000000..854a4b65 --- /dev/null +++ b/tests/auto/blackbox/testdata/installed-source-files/readme.txt @@ -0,0 +1 @@ +important information diff --git a/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs b/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs new file mode 100644 index 00000000..99acc558 --- /dev/null +++ b/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs @@ -0,0 +1,31 @@ +import qbs 1.0 +import qbs.TextFile + +Product { + name: "install-test" + type: ["text"] + Group { + qbs.install: true + qbs.installDir: "textfiles" + fileTagsFilter: "text" + } + + Rule { + multiplex: true + Artifact { + filePath: "HelloWorld.txt" + fileTags: ["text"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating file:'" + output.fileName + "'"; + cmd.highlight = "codegen"; + cmd.sourceCode = function() { + var file = new TextFile(output.filePath, TextFile.WriteOnly); + file.writeLine("Hello World!") + file.close(); + } + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata/installed_artifact/installed_artifact.qbs b/tests/auto/blackbox/testdata/installed_artifact/installed_artifact.qbs new file mode 100644 index 00000000..d1f9f54e --- /dev/null +++ b/tests/auto/blackbox/testdata/installed_artifact/installed_artifact.qbs @@ -0,0 +1,19 @@ +import qbs 1.0 + +Application { + name: "installedApp" + type: "application" + consoleApplication: true + Depends { name: "cpp" } + Group { + files: "main.cpp" + qbs.install: true + qbs.installDir: "src" + } + qbs.installPrefix: "/usr" + Group { + fileTagsFilter: "application" + qbs.install: true + qbs.installDir: "bin" + } +} diff --git a/tests/auto/blackbox/testdata/installed_artifact/main.cpp b/tests/auto/blackbox/testdata/installed_artifact/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/installed_artifact/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/installpackage/installpackage.qbs b/tests/auto/blackbox/testdata/installpackage/installpackage.qbs new file mode 100644 index 00000000..9ea561fc --- /dev/null +++ b/tests/auto/blackbox/testdata/installpackage/installpackage.qbs @@ -0,0 +1,43 @@ +import qbs + +Project { + QtApplication { + name: "public_tool" + Depends { name: "mylib" } + files: ["main.cpp"] + Group { + fileTagsFilter: product.type + qbs.install: true + qbs.installDir: "bin" + } + } + QtApplication { + name: "internal_tool" + Depends { name: "mylib" } + files: ["main.cpp"] + } + DynamicLibrary { + name: "mylib" + Depends { name: "Qt.core" } + files: ["lib.cpp"] + Group { + name: "public headers" + files: ["lib.h"] + qbs.install: true + qbs.installDir: "include" + } + Group { + fileTagsFilter: product.type + qbs.install: true + qbs.installDir: "lib" + } + } + + InstallPackage { + archiver.type: "tar" + builtByDefault: true + name: "tar-package" + Depends { name: "public_tool" } + Depends { name: "mylib" } + } +} diff --git a/tests/auto/blackbox/testdata/installpackage/lib.cpp b/tests/auto/blackbox/testdata/installpackage/lib.cpp new file mode 100644 index 00000000..1a34ab3a --- /dev/null +++ b/tests/auto/blackbox/testdata/installpackage/lib.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lib.h" + +void f() {} diff --git a/tests/auto/blackbox/testdata/installpackage/lib.h b/tests/auto/blackbox/testdata/installpackage/lib.h new file mode 100644 index 00000000..a464118a --- /dev/null +++ b/tests/auto/blackbox/testdata/installpackage/lib.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifdef MYLIB +#define MYLIB_EXPORT Q_DECL_EXPORT +#else +#define MYLIB_EXPORT Q_DECL_IMPORT +#endif + +MYLIB_EXPORT void f(); diff --git a/tests/auto/blackbox/testdata/installpackage/main.cpp b/tests/auto/blackbox/testdata/installpackage/main.cpp new file mode 100644 index 00000000..145b3ed6 --- /dev/null +++ b/tests/auto/blackbox/testdata/installpackage/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lib.h" + +int main() +{ + f(); +} diff --git a/tests/auto/blackbox/testdata/invalid-command-property/input.txt b/tests/auto/blackbox/testdata/invalid-command-property/input.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/invalid-command-property/invalid-command-property.qbs b/tests/auto/blackbox/testdata/invalid-command-property/invalid-command-property.qbs new file mode 100644 index 00000000..65437311 --- /dev/null +++ b/tests/auto/blackbox/testdata/invalid-command-property/invalid-command-property.qbs @@ -0,0 +1,27 @@ +import qbs +import qbs.TextFile + +Product { + type: ["output"] + Group { + files: ["input.txt"] + fileTags: ["input"] + } + Rule { + inputs: ["input"] + Artifact { + filePath: "dummy" + fileTags: ["output"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating output"; + cmd.textFile = new TextFile(input.filePath, TextFile.ReadOnly); + cmd.sourceCode = function() { + var content = textFile.readAll(); + textFile.close(); + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/invalid-extension-instantiation/invalid-extension-instantiation.qbs b/tests/auto/blackbox/testdata/invalid-extension-instantiation/invalid-extension-instantiation.qbs new file mode 100644 index 00000000..ad2f55da --- /dev/null +++ b/tests/auto/blackbox/testdata/invalid-extension-instantiation/invalid-extension-instantiation.qbs @@ -0,0 +1,27 @@ +import qbs +import qbs.Environment +import qbs.File +import qbs.FileInfo +import qbs.Utilities + +Product { + name: "theProduct" + type: ["mytype"] + property string extension + + Rule { + multiplex: true + Artifact { + filePath: "dummy" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + var f = eval("new " + product.extension); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/invalid-library-names/invalid-library-names.qbs b/tests/auto/blackbox/testdata/invalid-library-names/invalid-library-names.qbs new file mode 100644 index 00000000..1ddfecd3 --- /dev/null +++ b/tests/auto/blackbox/testdata/invalid-library-names/invalid-library-names.qbs @@ -0,0 +1,12 @@ +import qbs + +Project { + minimumQbsVersion: "1.6" + property var values: [null, undefined, 5, [], ""] + property int valueIndex + CppApplication { + cpp.dynamicLibraries: [project.values[project.valueIndex]] + cpp.staticLibraries: [project.values[project.valueIndex]] + files: ["main.cpp"] + } +} diff --git a/tests/auto/blackbox/testdata/invalid-library-names/main.cpp b/tests/auto/blackbox/testdata/invalid-library-names/main.cpp new file mode 100644 index 00000000..e01d8252 --- /dev/null +++ b/tests/auto/blackbox/testdata/invalid-library-names/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/jsextensions-file/file.qbs b/tests/auto/blackbox/testdata/jsextensions-file/file.qbs new file mode 100644 index 00000000..5d6ce07c --- /dev/null +++ b/tests/auto/blackbox/testdata/jsextensions-file/file.qbs @@ -0,0 +1,49 @@ +import qbs +import qbs.File +import qbs.FileInfo +import qbs.TextFile + +Product { + type: ["dummy"] + Rule { + multiplex: true + Artifact { + filePath: "dummy.txt" + fileTags: ["dummy"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + var origPath = FileInfo.joinPaths(product.sourceDirectory, "original.txt"); + var copyPath = FileInfo.joinPaths(product.sourceDirectory, "copy.txt"); + console.info("copy path: "+copyPath); + var original = new TextFile(origPath, TextFile.WriteOnly); + original.close(); + File.copy(origPath, copyPath); + var origTimestamp = File.lastModified(origPath); + var copyTimestamp = File.lastModified(copyPath); + if (origTimestamp > copyTimestamp) + throw new Error("Older file has newer timestamp."); + File.remove(origPath); + var copy = new TextFile(copyPath, TextFile.WriteOnly); + copy.writeLine(File.exists(origPath)); + copy.writeLine(File.exists(copyPath)); + copy.close(); + var zePath = FileInfo.joinPaths(product.sourceDirectory, "zePath"); + if (File.exists(zePath)) + throw new Error(zePath + " already exists."); + var created = File.makePath(zePath); + if (!created || !File.exists(zePath)) + throw new Error("zePath was not created."); + var entries = File.directoryEntries(product.sourceDirectory, File.AllEntries | File.NoDotAndDotDot); + if (entries.length < 3 || !entries.contains("file.qbs")) + throw new Error("Directory did not contain file.qbs"); + entries = File.directoryEntries(product.sourceDirectory, File.Dirs | File.NoDotAndDotDot); + if (entries.length < 1 || !entries.contains("zePath")) + throw new Error("Directory did not contain only zePath"); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs b/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs new file mode 100644 index 00000000..c42c7312 --- /dev/null +++ b/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs @@ -0,0 +1,47 @@ +import qbs +import qbs.FileInfo +import qbs.TextFile + +Product { + type: ["dummy"] + Rule { + multiplex: true + Artifact { + filePath: "dummy.txt" + fileTags: ["dummy"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + var output = new TextFile(FileInfo.joinPaths(product.sourceDirectory, "output.txt"), + TextFile.WriteOnly); + output.writeLine(FileInfo.baseName("/tmp/blubb.tar.gz")); + output.writeLine(FileInfo.completeBaseName("/tmp/blubb.tar.gz")); + output.writeLine(FileInfo.fileName("/tmp/blubb.tar.gz")); + output.writeLine(FileInfo.fromWindowsSeparators("/tmp/blubb.tar.gz")); + output.writeLine(FileInfo.fromWindowsSeparators("c:\\tmp\\blubb.tar.gz")); + output.writeLine(FileInfo.isAbsolutePath("/tmp/blubb.tar.gz")); + output.writeLine(FileInfo.isAbsolutePath("c:\\tmp\\blubb.tar.gz")); + output.writeLine(FileInfo.isAbsolutePath("c:\\tmp\\blubb.tar.gz", ["unix"])); + output.writeLine(FileInfo.isAbsolutePath("c:\\tmp\\blubb.tar.gz", ["windows"])); + output.writeLine(FileInfo.isAbsolutePath("blubb.tar.gz")); + output.writeLine(FileInfo.isAbsolutePath("../blubb.tar.gz")); + output.writeLine(FileInfo.joinPaths("/", "tmp", "blubb.tar.gz")); + output.writeLine(FileInfo.path("/tmp/blubb.tar.gz")); + output.writeLine(FileInfo.path("/tmp/")); + output.writeLine(FileInfo.path("/")); + output.writeLine(FileInfo.path("d:/")); + output.writeLine(FileInfo.path("d:/", ["unix"])); + output.writeLine(FileInfo.path("d:/", ["windows"])); + output.writeLine(FileInfo.relativePath("/tmp", "/tmp/blubb.tar.gz")); + output.writeLine(FileInfo.relativePath("/", "/tmp/blubb.tar.gz")); + output.writeLine(FileInfo.relativePath("/tmp", "/blubb.tar.gz")); + output.writeLine(FileInfo.toWindowsSeparators("/tmp/blubb.tar.gz")); + output.writeLine(FileInfo.toWindowsSeparators("c:\\tmp\\blubb.tar.gz")); + output.close(); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/jsextensions-process/process.qbs b/tests/auto/blackbox/testdata/jsextensions-process/process.qbs new file mode 100644 index 00000000..da3a6681 --- /dev/null +++ b/tests/auto/blackbox/testdata/jsextensions-process/process.qbs @@ -0,0 +1,69 @@ +import qbs +import qbs.FileInfo +import qbs.Process +import qbs.TextFile + +Project { + property string qbsFilePath + Product { + Depends { name: "Qt.core" } + type: ["dummy"] + Rule { + multiplex: true + Artifact { + filePath: "dummy.txt" + fileTags: ["dummy"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + // Synchronous run, successful. + var process = new Process(); + var pathVal = [process.getEnv("PATH"), + product.moduleProperty("Qt.core", "binPath")] + .join(product.moduleProperty("qbs", "pathListSeparator")); + process.setEnv("PATH", pathVal); + process.exec(project.qbsFilePath, ["help"], true); + var output = new TextFile("output.txt", TextFile.WriteOnly); + output.writeLine(process.exitCode()); + output.writeLine(process.readLine()); + process.close(); + + // Asynchronous run, successful. + process = new Process(); + process.setEnv("PATH", pathVal); + output.writeLine(process.start(project.qbsFilePath, ["help"])); + output.writeLine(process.waitForFinished()); + output.writeLine(process.exitCode()); + output.writeLine(process.readLine()); + process.close(); + + // Asynchronous run, unsuccessful. + process = new Process(); + output.writeLine(process.start("blubb", [])); + process.close(); + + // closeWriteChannel test + process = new Process(); + if (product.moduleProperty("qbs", "hostOS").contains("windows")) + process.start("cmd", ["/C", "c:\\windows\\system32\\sort.exe"]); + else + process.start("cat", []); + process.writeLine("should be"); + process.closeWriteChannel(); + process.writeLine("should not be"); + if (!process.waitForFinished()) + process.kill(); + output.write(process.readStdOut()); + process.close(); + + // TODO: Test all the other Process methods as well. + + output.close(); + }; + return [cmd]; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/jsextensions-propertylist/propertylist.qbs b/tests/auto/blackbox/testdata/jsextensions-propertylist/propertylist.qbs new file mode 100644 index 00000000..a2898f69 --- /dev/null +++ b/tests/auto/blackbox/testdata/jsextensions-propertylist/propertylist.qbs @@ -0,0 +1,129 @@ +import qbs +import qbs.Process +import qbs.PropertyList +import qbs.TextFile + +Product { + type: { + var plistobj = new PropertyList(); + if (!plistobj.isEmpty()) { + throw "newly created PropertyList was not empty!"; + } + + if (plistobj.format() !== undefined) { + throw "newly created PropertyList did not have an undefined format"; + } + + plistobj.clear(); + + if (!plistobj.isEmpty() || plistobj.format() !== undefined) { + throw "clear() somehow had an effect on an empty PropertyList"; + } + + plistobj.readFromString('{"key":["value"]}'); + if (plistobj.isEmpty() || plistobj.format() !== "json") { + throw "readFromString did not set format to JSON or object thinks it is empty"; + } + + plistobj.clear(); + + if (!plistobj.isEmpty() || plistobj.format() !== undefined) { + throw "clear() had no effect on a non-empty PropertyList"; + } + + var obj = { + "Array": ["ListItem1", "ListItem2", "ListItem3"], + "Integer": 1, + "Boolean": true, + "String": "otherString" + }; + + var infoplist = new TextFile("test.xml", TextFile.WriteOnly); + infoplist.write(JSON.stringify(obj)); + infoplist.close(); + + var process = new Process(); + process.exec("plutil", ["-convert", "xml1", "test.xml"]); + process.close(); + + var xmlfile = new TextFile("test.xml", TextFile.ReadOnly); + var propertyList = new PropertyList(); + propertyList.readFromString(xmlfile.readAll()); + xmlfile.close(); + + var jsontextfile = new TextFile("test.json", TextFile.WriteOnly); + jsontextfile.write(propertyList.toJSON()); + jsontextfile.close(); + + propertyList.writeToFile("test2.json", "json-compact"); + propertyList.writeToFile("test3.json", "json-pretty"); + + process = new Process(); + process.exec("plutil", ["-convert", "json", "test.xml"]); + process.close(); + + propertyList = new PropertyList(); + propertyList.readFromFile("test.xml"); + if (propertyList.format() !== "json") { // yes, JSON -- ignore the file extension + throw "expected property list format json but got " + propertyList.format(); + } + + if (propertyList.isEmpty()) { + throw "PropertyList was 'empty' after being loaded with data"; + } + + var opensteptextfile = new TextFile("test.openstep.plist", TextFile.WriteOnly); + opensteptextfile.write('{ rootObject = ( "val1", "val3", "val5", /* comment */ "val7", "val9", ); }'); + opensteptextfile.close(); + + propertyList = new PropertyList(); + propertyList.readFromFile("test.openstep.plist"); + if (propertyList.format() !== "openstep") { + throw "expected property list format openstep but got " + propertyList.format(); + } + + var jsonObj = JSON.parse(propertyList.toJSON()); + if (jsonObj["rootObject"].length != 5) { + throw "going from OpenStep to a JSON string to a JSON object somehow broke"; + } + + propertyList.clear(); + propertyList.readFromString('foobarz'); + jsonObj = JSON.parse(propertyList.toJSON()); + if (jsonObj["foo"] !== "barz") { + throw "the XML plist did not get parsed properly"; + } + + propertyList.writeToFile("woof.xml", "xml1"); + propertyList.readFromFile("woof.xml"); + if (propertyList.format() !== "xml1") { + throw "round trip writing and reading XML failed"; + } + + propertyList.writeToFile("woof.plist", "binary1"); + propertyList.readFromFile("woof.plist"); + if (propertyList.format() !== "binary1") { + throw "round trip writing and reading binary failed"; + } + + if (jsonObj["foo"] !== "barz") { + throw "the binary plist did not get parsed properly"; + } + + if (propertyList.toString("json") !== propertyList.toString("json-compact") || + propertyList.toJSON() !== propertyList.toJSON("compact")) { + throw "json and json-compact formats were not equivalent"; + } + + if (propertyList.toString("json") === propertyList.toString("json-pretty") || + propertyList.toJSON() === propertyList.toJSON("pretty")) { + throw "json and json-pretty formats were not different"; + } + + if (propertyList.toString("xml1") !== propertyList.toXMLString()) { + throw 'toString("xml1") and toXMLString() were not equivalent'; + } + + return "Pineapple Steve"; + } +} diff --git a/tests/auto/blackbox/testdata/jsextensions-temporarydir/jsextensions-temporarydir.qbs b/tests/auto/blackbox/testdata/jsextensions-temporarydir/jsextensions-temporarydir.qbs new file mode 100644 index 00000000..fb54a45e --- /dev/null +++ b/tests/auto/blackbox/testdata/jsextensions-temporarydir/jsextensions-temporarydir.qbs @@ -0,0 +1,28 @@ +import qbs +import qbs.File +import qbs.TemporaryDir + +Product { + targetName: { + var dir; + var dirPath; + try { + dir = new TemporaryDir(); + dirPath = dir.path(); + if (!dirPath) + throw "path is empty"; + + if (!dir.isValid()) + throw "dir is not valid"; + + if (!File.exists(dirPath)) + throw "dir does not exist"; + } finally { + if (!dir.remove()) + throw "could not remove"; + } + + if (File.exists(dirPath)) + throw "dir was not removed"; + } +} diff --git a/tests/auto/blackbox/testdata/jsextensions-textfile/textfile.qbs b/tests/auto/blackbox/testdata/jsextensions-textfile/textfile.qbs new file mode 100644 index 00000000..456a10f0 --- /dev/null +++ b/tests/auto/blackbox/testdata/jsextensions-textfile/textfile.qbs @@ -0,0 +1,36 @@ +import qbs +import qbs.TextFile + +Product { + type: ["dummy"] + Rule { + multiplex: true + Artifact { + filePath: "dummy.txt" + fileTags: ["dummy"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + var file1 = new TextFile("file1.txt", TextFile.WriteOnly); + file1.write("First line.\nSecond line.\nThird line."); + file1.close(); + file1 = new TextFile("file1.txt", TextFile.ReadWrite); + var file2 = new TextFile("file2.txt", TextFile.WriteOnly); + file2.writeLine(file1.atEof()); + while (true) { + var line = file1.readLine(); + if (!line || line.length == 0) + break; + file2.writeLine(line); + } + file1.truncate(); + file2.writeLine(file1.atEof()); + file1.close(); + file2.close(); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/ld/coreutils.cpp b/tests/auto/blackbox/testdata/ld/coreutils.cpp new file mode 100644 index 00000000..29acfc48 --- /dev/null +++ b/tests/auto/blackbox/testdata/ld/coreutils.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "coreutils.h" + +int foo() +{ + return 42; +} diff --git a/tests/auto/blackbox/testdata/ld/coreutils.h b/tests/auto/blackbox/testdata/ld/coreutils.h new file mode 100644 index 00000000..2768bea1 --- /dev/null +++ b/tests/auto/blackbox/testdata/ld/coreutils.h @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int foo(); diff --git a/tests/auto/blackbox/testdata/ld/ld.qbs b/tests/auto/blackbox/testdata/ld/ld.qbs new file mode 100644 index 00000000..d1960351 --- /dev/null +++ b/tests/auto/blackbox/testdata/ld/ld.qbs @@ -0,0 +1,26 @@ +import qbs + +Project { + Library { + Depends { name: "cpp" } + name: "coreutils" + targetName: "qbs can handle any file paths, even the crazy ones! ;)" + files: ["coreutils.cpp", "coreutils.h"] + bundle.isBundle: false + + Properties { + condition: qbs.targetOS.contains("darwin") + cpp.sonamePrefix: "@rpath" + } + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + CppApplication { + Depends { name: "coreutils" } + files: ["main.cpp"] + } +} diff --git a/tests/auto/blackbox/testdata/ld/main.cpp b/tests/auto/blackbox/testdata/ld/main.cpp new file mode 100644 index 00000000..fd6b72f3 --- /dev/null +++ b/tests/auto/blackbox/testdata/ld/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "coreutils.h" +#include + +int main(int argc, char *argv[]) +{ + printf("%d\n", foo()); + return 0; +} diff --git a/tests/auto/blackbox/testdata/lexyacc/one-grammar/lexer.l b/tests/auto/blackbox/testdata/lexyacc/one-grammar/lexer.l new file mode 100644 index 00000000..86f248a3 --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/one-grammar/lexer.l @@ -0,0 +1,20 @@ +%{ +#include +#include +void yyerror(const char *e) { std::cerr << e; } +extern "C" int yywrap() { return 1; } +%} + +ID [a-z]+ +AND "&&" +OR "||" +NOT "!" + +%% +[[:space:]]+ +{ID} yylval.s = yytext; return ID; +{AND} return AND; +{OR} return OR; +{NOT} return NOT; + +%% diff --git a/tests/auto/blackbox/testdata/lexyacc/one-grammar/one-grammar.qbs b/tests/auto/blackbox/testdata/lexyacc/one-grammar/one-grammar.qbs new file mode 100644 index 00000000..2cdf9f3f --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/one-grammar/one-grammar.qbs @@ -0,0 +1,15 @@ +import qbs + +CppApplication { + Depends { name: "lex_yacc" } + lex_yacc.outputTag: "cpp" + lex_yacc.yaccFlags: ["-l"] + cpp.includePaths: ["."] + cpp.cxxLanguageVersion: "c++11" + consoleApplication: true + files: [ + "lexer.l", + "parser.y", + "types.h", + ] +} diff --git a/tests/auto/blackbox/testdata/lexyacc/one-grammar/parser.y b/tests/auto/blackbox/testdata/lexyacc/one-grammar/parser.y new file mode 100644 index 00000000..e19c9a90 --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/one-grammar/parser.y @@ -0,0 +1,30 @@ +%{ +#include +%} + +%type expr +%left OR +%left AND +%nonassoc NOT +%token ID + +%% + +start: expr { root = $1; } +expr: expr AND expr { auto t = std::make_shared(); t->val = "AND"; t->children = { $1, $3 }; $$ = t; } + | expr OR expr { auto t = std::make_shared(); t->val = "OR"; t->children = { $1, $3 }; $$ = t; } + | NOT expr { auto t = std::make_shared(); t->val = "NOT"; t->children = { $2 }; $$ = t; } + | ID { auto t = std::make_shared(); t->val = $1; $$ = t; } + +%% + +TreePtr root; + +int main() +{ + yyparse(); + if (!root) + return 1; + root->print(); + std::cout << std::endl; +} diff --git a/tests/auto/blackbox/testdata/lexyacc/one-grammar/types.h b/tests/auto/blackbox/testdata/lexyacc/one-grammar/types.h new file mode 100644 index 00000000..12cafe89 --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/one-grammar/types.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +struct Tree; +using TreePtr = std::shared_ptr; +struct Tree { + std::string val; + std::vector children; + void print() const { + std::cout << val << ' '; + for (const TreePtr &t : children) + t->print(); + } +}; +struct YaccType { TreePtr t; std::string s; }; +#define YYSTYPE YaccType +extern TreePtr root; + +int yylex(); +void yyerror(const char *); diff --git a/tests/auto/blackbox/testdata/lexyacc/two-grammars/g1.l b/tests/auto/blackbox/testdata/lexyacc/two-grammars/g1.l new file mode 100644 index 00000000..a8e8bec4 --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/two-grammars/g1.l @@ -0,0 +1,10 @@ +%option noyywrap + +%{ +#include +void g1error(const char *e) { } +%} + +%% + +%% diff --git a/tests/auto/blackbox/testdata/lexyacc/two-grammars/g1.y b/tests/auto/blackbox/testdata/lexyacc/two-grammars/g1.y new file mode 100644 index 00000000..402bf351 --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/two-grammars/g1.y @@ -0,0 +1,7 @@ +%{ +int g1lex(); +%} + +%% + +expr: '1' diff --git a/tests/auto/blackbox/testdata/lexyacc/two-grammars/g2.l b/tests/auto/blackbox/testdata/lexyacc/two-grammars/g2.l new file mode 100644 index 00000000..918e60ad --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/two-grammars/g2.l @@ -0,0 +1,10 @@ +%option noyywrap + +%{ +#include +void g2error(const char *e) { } +%} + +%% + +%% diff --git a/tests/auto/blackbox/testdata/lexyacc/two-grammars/g2.y b/tests/auto/blackbox/testdata/lexyacc/two-grammars/g2.y new file mode 100644 index 00000000..402bf351 --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/two-grammars/g2.y @@ -0,0 +1,7 @@ +%{ +int g1lex(); +%} + +%% + +expr: '1' diff --git a/tests/auto/blackbox/testdata/lexyacc/two-grammars/main.c b/tests/auto/blackbox/testdata/lexyacc/two-grammars/main.c new file mode 100644 index 00000000..0889bb4f --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/two-grammars/main.c @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int g1parse(); +int g2parse(); + +int main() +{ + g1parse(); + g2parse(); + return 0; +} diff --git a/tests/auto/blackbox/testdata/lexyacc/two-grammars/two-grammars.qbs b/tests/auto/blackbox/testdata/lexyacc/two-grammars/two-grammars.qbs new file mode 100644 index 00000000..d70f733a --- /dev/null +++ b/tests/auto/blackbox/testdata/lexyacc/two-grammars/two-grammars.qbs @@ -0,0 +1,13 @@ +import qbs + +CppApplication { + Depends { name: "lex_yacc" } + consoleApplication: true + files: [ + "g1.l", + "g1.y", + "g2.l", + "g2.y", + "main.c", + ] +} diff --git a/tests/auto/blackbox/testdata/linkerMode/main.c b/tests/auto/blackbox/testdata/linkerMode/main.c new file mode 100644 index 00000000..4ef6de1d --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerMode/main.c @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + void *memory = malloc(8); + free(memory); + return 0; +} diff --git a/tests/auto/blackbox/testdata/linkerMode/main.cpp b/tests/auto/blackbox/testdata/linkerMode/main.cpp new file mode 100644 index 00000000..11062f6a --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerMode/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + std::string s = "Hello World"; + (void)s; + return 0; +} diff --git a/tests/auto/blackbox/testdata/linkerMode/main.m b/tests/auto/blackbox/testdata/linkerMode/main.m new file mode 100644 index 00000000..8db28e5b --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerMode/main.m @@ -0,0 +1,7 @@ +#include + +int main() +{ + sel_registerName("qbs"); + return 0; +} diff --git a/tests/auto/blackbox/testdata/linkerMode/main.mm b/tests/auto/blackbox/testdata/linkerMode/main.mm new file mode 100644 index 00000000..65ef0409 --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerMode/main.mm @@ -0,0 +1,10 @@ +#include +#include + +int main() +{ + std::string s = "Hello World"; + (void)s; + sel_registerName("qbs"); + return 0; +} diff --git a/tests/auto/blackbox/testdata/linkerMode/main.s b/tests/auto/blackbox/testdata/linkerMode/main.s new file mode 100644 index 00000000..c2c78b7d --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerMode/main.s @@ -0,0 +1,5 @@ +.globl _main +.globl _start + +_main: +_start: diff --git a/tests/auto/blackbox/testdata/linkerMode/project.qbs b/tests/auto/blackbox/testdata/linkerMode/project.qbs new file mode 100644 index 00000000..f1cfcb52 --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerMode/project.qbs @@ -0,0 +1,83 @@ +import qbs + +Project { + CppApplication { + consoleApplication: true + name: "LinkedProduct-Assembly" + files: ["main.s"] + + cpp.entryPoint: "_start" + cpp.dynamicLibraries: qbs.targetOS.contains("darwin") ? ["System"] : ["c"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + CppApplication { + consoleApplication: true + name: "LinkedProduct-C" + files: ["main.c"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + CppApplication { + condition: qbs.targetOS.contains("darwin") + + consoleApplication: true + name: "LinkedProduct-Objective-C" + files: ["main.m"] + + cpp.dynamicLibraries: ["ObjC"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + CppApplication { + consoleApplication: true + name: "LinkedProduct-C++" + files: ["main.cpp"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + CppApplication { + condition: qbs.targetOS.contains("darwin") + + consoleApplication: true + name: "LinkedProduct-Objective-C++" + files: ["main.mm"] + + cpp.dynamicLibraries: ["ObjC"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + CppApplication { + Depends { name: "LinkedProduct-C++StaticLibrary" } + + name: "LinkedProduct-BlankApp" + files: ["staticmain.c"] + } + + StaticLibrary { + Depends { name: "cpp" } + + name: "LinkedProduct-C++StaticLibrary" + files: ["staticlib.cpp"] + } +} diff --git a/tests/auto/blackbox/testdata/linkerMode/staticlib.cpp b/tests/auto/blackbox/testdata/linkerMode/staticlib.cpp new file mode 100644 index 00000000..ca791682 --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerMode/staticlib.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +extern "C" int cpp(); + +int cpp() +{ + std::cout << "Hello world" << std::endl; + return 0; +} diff --git a/tests/auto/blackbox/testdata/linkerMode/staticmain.c b/tests/auto/blackbox/testdata/linkerMode/staticmain.c new file mode 100644 index 00000000..bd41eec0 --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerMode/staticmain.c @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +extern int cpp(); + +int main() +{ + return cpp(); +} diff --git a/tests/auto/blackbox/testdata/linkerscripts/linkerscript1 b/tests/auto/blackbox/testdata/linkerscripts/linkerscript1 new file mode 100644 index 00000000..d0f2e9cf --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerscripts/linkerscript1 @@ -0,0 +1 @@ +TEST_SYMBOL1 = 0; diff --git a/tests/auto/blackbox/testdata/linkerscripts/linkerscript2 b/tests/auto/blackbox/testdata/linkerscripts/linkerscript2 new file mode 100644 index 00000000..62832ba0 --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerscripts/linkerscript2 @@ -0,0 +1 @@ +TEST_SYMBOL2 = 1; diff --git a/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs b/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs new file mode 100644 index 00000000..b7a95089 --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs @@ -0,0 +1,33 @@ +import qbs + +DynamicLibrary { + type: base.concat("custom") + Depends { name: "cpp" } + files: ["testlib.c"] + Group { + name: "linker scripts" + files: ["linkerscript1", "linkerscript2"] + fileTags: ["linkerscript"] + } + + Rule { + multiplex: true + Artifact { + filePath: "dummy.txt" + fileTags: ["custom"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + console.warn("---" + product.moduleProperty("cpp", "nmPath") + "---"); + } + return [cmd]; + } + } + + Group { + fileTagsFilter: ["dynamiclibrary"] + qbs.install: true + } +} diff --git a/tests/auto/blackbox/testdata/linkerscripts/testlib.c b/tests/auto/blackbox/testdata/linkerscripts/testlib.c new file mode 100644 index 00000000..cd4792b5 --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerscripts/testlib.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void dummy() {} diff --git a/tests/auto/blackbox/testdata/list-properties-with-outer/dummy.txt b/tests/auto/blackbox/testdata/list-properties-with-outer/dummy.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/list-properties-with-outer/modules/higher/higher.qbs b/tests/auto/blackbox/testdata/list-properties-with-outer/modules/higher/higher.qbs new file mode 100644 index 00000000..a8d1b186 --- /dev/null +++ b/tests/auto/blackbox/testdata/list-properties-with-outer/modules/higher/higher.qbs @@ -0,0 +1,6 @@ +import qbs + +Module { + Depends { name: "lower" } + lower.listProp: ["higher"] +} diff --git a/tests/auto/blackbox/testdata/list-properties-with-outer/modules/lower/lower.qbs b/tests/auto/blackbox/testdata/list-properties-with-outer/modules/lower/lower.qbs new file mode 100644 index 00000000..1a00d406 --- /dev/null +++ b/tests/auto/blackbox/testdata/list-properties-with-outer/modules/lower/lower.qbs @@ -0,0 +1,22 @@ +import qbs + +Module { + property stringList listProp + + Rule { + inputs: ["intype"] + Artifact { + filePath: "dummy.out" + fileTags: ["outtype"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + var listProp = input.moduleProperty("lower", "listProp"); + console.info("listProp: " + JSON.stringify(listProp)); + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/list-properties-with-outer/project.qbs b/tests/auto/blackbox/testdata/list-properties-with-outer/project.qbs new file mode 100644 index 00000000..3b0b3be4 --- /dev/null +++ b/tests/auto/blackbox/testdata/list-properties-with-outer/project.qbs @@ -0,0 +1,12 @@ +import qbs + +Product { + type: ["outtype"] + Depends { name: "higher" } + lower.listProp: ["product"] + Group { + files: ["dummy.txt"] + fileTags: ["intype"] + lower.listProp: outer.concat(["group"]) + } +} diff --git a/tests/auto/blackbox/testdata/list-property-order/dummy.txt b/tests/auto/blackbox/testdata/list-property-order/dummy.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/list-property-order/modules/higher1/higher1.qbs b/tests/auto/blackbox/testdata/list-property-order/modules/higher1/higher1.qbs new file mode 100644 index 00000000..c56dd33b --- /dev/null +++ b/tests/auto/blackbox/testdata/list-property-order/modules/higher1/higher1.qbs @@ -0,0 +1,6 @@ +import qbs + +Module { + Depends { name: "lower" } + lower.listProp: ["higher1"] +} diff --git a/tests/auto/blackbox/testdata/list-property-order/modules/higher2/higher2.qbs b/tests/auto/blackbox/testdata/list-property-order/modules/higher2/higher2.qbs new file mode 100644 index 00000000..75600ffa --- /dev/null +++ b/tests/auto/blackbox/testdata/list-property-order/modules/higher2/higher2.qbs @@ -0,0 +1,6 @@ +import qbs + +Module { + Depends { name: "lower" } + lower.listProp: ["higher2"] +} diff --git a/tests/auto/blackbox/testdata/list-property-order/modules/higher3/higher3.qbs b/tests/auto/blackbox/testdata/list-property-order/modules/higher3/higher3.qbs new file mode 100644 index 00000000..5372b92d --- /dev/null +++ b/tests/auto/blackbox/testdata/list-property-order/modules/higher3/higher3.qbs @@ -0,0 +1,6 @@ +import qbs + +Module { + Depends { name: "lower" } + lower.listProp: ["higher3"] +} diff --git a/tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs b/tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs new file mode 100644 index 00000000..95cdfd70 --- /dev/null +++ b/tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs @@ -0,0 +1,23 @@ +import qbs + +Module { + property stringList listProp + + Rule { + inputs: ["intype"] + Artifact { + filePath: "dummy.out" + fileTags: "outtype" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.sourceCode = function() { + console.info("listProp = " + + JSON.stringify(product.moduleProperty("lower", "listProp"))); + }; + var prop = product.moduleProperty("lowerlevel", "prop"); + cmd.silent = true; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/list-property-order/product.qbs b/tests/auto/blackbox/testdata/list-property-order/product.qbs new file mode 100644 index 00000000..ef5e5c82 --- /dev/null +++ b/tests/auto/blackbox/testdata/list-property-order/product.qbs @@ -0,0 +1,13 @@ +import qbs + +Product { + type: "outtype" + name: "toplevel" + Depends { name: "higher1" } + Depends { name: "higher2" } + Depends { name: "higher3" } + Group { + files: ["dummy.txt"] + fileTags: ["intype"] + } +} diff --git a/tests/auto/blackbox/testdata/loadablemodule/exported.cpp b/tests/auto/blackbox/testdata/loadablemodule/exported.cpp new file mode 100644 index 00000000..919bd350 --- /dev/null +++ b/tests/auto/blackbox/testdata/loadablemodule/exported.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "exported.h" + +extern "C" { + int foo() { + return 42; + } +} diff --git a/tests/auto/blackbox/testdata/loadablemodule/exported.h b/tests/auto/blackbox/testdata/loadablemodule/exported.h new file mode 100644 index 00000000..7745b5c9 --- /dev/null +++ b/tests/auto/blackbox/testdata/loadablemodule/exported.h @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +extern "C" { + Q_DECL_EXPORT int foo(); +} diff --git a/tests/auto/blackbox/testdata/loadablemodule/loadablemodule.qbs b/tests/auto/blackbox/testdata/loadablemodule/loadablemodule.qbs new file mode 100644 index 00000000..f5044268 --- /dev/null +++ b/tests/auto/blackbox/testdata/loadablemodule/loadablemodule.qbs @@ -0,0 +1,32 @@ +import qbs + +Project { + LoadableModule { + Depends { name: "cpp" } + Depends { name: "bundle" } + Depends { name: "Qt.core" } + bundle.isBundle: false + name: "CoolPlugIn" + files: ["exported.cpp", "exported.h"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } + + CppApplication { + Depends { name: "cpp" } + Depends { name: "CoolPlugIn" } + Depends { name: "bundle" } + Depends { name: "Qt.core" } + bundle.isBundle: false + name: "CoolApp" + files: ["main.cpp"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + } +} diff --git a/tests/auto/blackbox/testdata/loadablemodule/main.cpp b/tests/auto/blackbox/testdata/loadablemodule/main.cpp new file mode 100644 index 00000000..259d2bb7 --- /dev/null +++ b/tests/auto/blackbox/testdata/loadablemodule/main.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2015 Jake Petroules. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int main() { + QLibrary lib("CoolPlugIn"); + if (lib.load()) { + QFunctionPointer fptr = lib.resolve("foo"); + if (fptr) { + qDebug() << "foo =" << ((int (*)(void))fptr)(); + } + } + return 0; +} diff --git a/tests/auto/blackbox/testdata/lrelease/de.ts b/tests/auto/blackbox/testdata/lrelease/de.ts new file mode 100644 index 00000000..5beb013b --- /dev/null +++ b/tests/auto/blackbox/testdata/lrelease/de.ts @@ -0,0 +1,23 @@ + + + + + Application + + I am hunry + Ich bin Ungar + + + I am thirsty + Ich bin Donnerstag + + + Please press Control + Bitte drücken Sie den Kontrolleur + + + No keyboard detected. Please press F1 to continue + Sehr witzig + + + diff --git a/tests/auto/blackbox/testdata/lrelease/hu.ts b/tests/auto/blackbox/testdata/lrelease/hu.ts new file mode 100644 index 00000000..dc45eac4 --- /dev/null +++ b/tests/auto/blackbox/testdata/lrelease/hu.ts @@ -0,0 +1,15 @@ + + + + + Application + + My hovercraft is full of eels + A légpárnásom tele van angolnákkal + + + I will not buy this record; it is scratched + Nem fogok vásárolni ezt a rekordot; ez karcos + + + diff --git a/tests/auto/blackbox/testdata/lrelease/lrelease.qbs b/tests/auto/blackbox/testdata/lrelease/lrelease.qbs new file mode 100644 index 00000000..b4119402 --- /dev/null +++ b/tests/auto/blackbox/testdata/lrelease/lrelease.qbs @@ -0,0 +1,8 @@ +import qbs + +Product { + name: "lrelease-test" + type: ["ts"] + Depends { name: "Qt.core" } + files: ["de.ts", "hu.ts"] +} diff --git a/tests/auto/blackbox/testdata/missing-dependency/main.cpp b/tests/auto/blackbox/testdata/missing-dependency/main.cpp new file mode 100644 index 00000000..7751df91 --- /dev/null +++ b/tests/auto/blackbox/testdata/missing-dependency/main.cpp @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ +} diff --git a/tests/auto/blackbox/testdata/missing-dependency/missing-dependency.qbs b/tests/auto/blackbox/testdata/missing-dependency/missing-dependency.qbs new file mode 100644 index 00000000..a30ac2a7 --- /dev/null +++ b/tests/auto/blackbox/testdata/missing-dependency/missing-dependency.qbs @@ -0,0 +1,33 @@ +import qbs +import qbs.TextFile + +Project { + Product { + name: "theDep" + type: ["genheader"] + + Rule { + multiplex: true + Artifact { + filePath: project.buildDirectory + "/theHeader.h" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.close(); + } + return [cmd]; + } + } + } + CppApplication { + name: "theApp" + cpp.includePaths: [project.buildDirectory] + files: ["main.cpp"] + } +} + + diff --git a/tests/auto/blackbox/testdata/mixed-build-variants/main.cpp b/tests/auto/blackbox/testdata/mixed-build-variants/main.cpp new file mode 100644 index 00000000..141d7ebe --- /dev/null +++ b/tests/auto/blackbox/testdata/mixed-build-variants/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + qDebug("Tach."); +} diff --git a/tests/auto/blackbox/testdata/mixed-build-variants/mixed-build-variants.qbs b/tests/auto/blackbox/testdata/mixed-build-variants/mixed-build-variants.qbs new file mode 100644 index 00000000..2b208f3d --- /dev/null +++ b/tests/auto/blackbox/testdata/mixed-build-variants/mixed-build-variants.qbs @@ -0,0 +1,6 @@ +import qbs + +QtApplication { + Qt.core.qtBuildVariant: "release" + files: ["main.cpp"] +} diff --git a/tests/auto/blackbox/testdata/moc-flags/blubb.h b/tests/auto/blackbox/testdata/moc-flags/blubb.h new file mode 100644 index 00000000..54e2fdd7 --- /dev/null +++ b/tests/auto/blackbox/testdata/moc-flags/blubb.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class Blubb : public QObject +{ + Q_OBJECT +public: + Blubb() { } + void makeBlubb() { } +}; diff --git a/tests/auto/blackbox/testdata/moc-flags/main.cpp b/tests/auto/blackbox/testdata/moc-flags/main.cpp new file mode 100644 index 00000000..8e7c9e28 --- /dev/null +++ b/tests/auto/blackbox/testdata/moc-flags/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "blubb.h" + +int main() +{ + Blubb().makeBlubb(); +} diff --git a/tests/auto/blackbox/testdata/moc-flags/moc-flags.qbs b/tests/auto/blackbox/testdata/moc-flags/moc-flags.qbs new file mode 100644 index 00000000..d42c7ad8 --- /dev/null +++ b/tests/auto/blackbox/testdata/moc-flags/moc-flags.qbs @@ -0,0 +1,5 @@ +import qbs + +QtApplication { + files: ["main.cpp", "blubb.h"] +} diff --git a/tests/auto/blackbox/testdata/multiple-changes/dummy.txt b/tests/auto/blackbox/testdata/multiple-changes/dummy.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/multiple-changes/project.qbs b/tests/auto/blackbox/testdata/multiple-changes/project.qbs new file mode 100644 index 00000000..254e2b4d --- /dev/null +++ b/tests/auto/blackbox/testdata/multiple-changes/project.qbs @@ -0,0 +1,33 @@ +import qbs + +Project { + property bool prop: false + Product { + name: "test" + type: ["out-type"] + Group { + name: "Rule input" + files: ["dummy.txt"] + fileTags: ["in-type"] + } + Group { + name: "irrelevant" + files: ["*.blubb"] + } + Rule { + inputs: ["in-type"] + Artifact { + filePath: "dummy.out" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + console.info("prop: " + project.prop); + } + return [cmd]; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/nested-groups/file1.cpp b/tests/auto/blackbox/testdata/nested-groups/file1.cpp new file mode 100644 index 00000000..95255e4e --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/file1.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef REQUIRED_FOR_FILE1 +#error "missing define" +#endif + +#ifndef ALSO_REQUIRED_FOR_FILE1 +#error "missing define" +#endif + +void file1() {} diff --git a/tests/auto/blackbox/testdata/nested-groups/file1.h b/tests/auto/blackbox/testdata/nested-groups/file1.h new file mode 100644 index 00000000..d52b9f05 --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/file1.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void file1(); diff --git a/tests/auto/blackbox/testdata/nested-groups/file2.cpp b/tests/auto/blackbox/testdata/nested-groups/file2.cpp new file mode 100644 index 00000000..87333eab --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/file2.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef BREAKS_FILE2 +#error "unexpected define" +#endif + +#ifndef REQUIRED_FOR_FILE2 +#error "missing define" +#endif + +void file2() {} diff --git a/tests/auto/blackbox/testdata/nested-groups/file2.h b/tests/auto/blackbox/testdata/nested-groups/file2.h new file mode 100644 index 00000000..97e76d0e --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/file2.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void file2(); diff --git a/tests/auto/blackbox/testdata/nested-groups/file3.cpp b/tests/auto/blackbox/testdata/nested-groups/file3.cpp new file mode 100644 index 00000000..7c3a4b92 --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/file3.cpp @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef REQUIRED_FOR_FILE3 +#error "missing define" +#endif + +void file3() {} diff --git a/tests/auto/blackbox/testdata/nested-groups/file3.h b/tests/auto/blackbox/testdata/nested-groups/file3.h new file mode 100644 index 00000000..22fe31c1 --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/file3.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void file3(); diff --git a/tests/auto/blackbox/testdata/nested-groups/main.cpp b/tests/auto/blackbox/testdata/nested-groups/main.cpp new file mode 100644 index 00000000..c9c73b9e --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "file1.h" +#include "file2.h" +#include "file3.h" +#include "other.h" + +int main() +{ + file1(); + file2(); + file3(); + other(); +} diff --git a/tests/auto/blackbox/testdata/nested-groups/main2.cpp b/tests/auto/blackbox/testdata/nested-groups/main2.cpp new file mode 100644 index 00000000..e14f806b --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/main2.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { } diff --git a/tests/auto/blackbox/testdata/nested-groups/main3.cpp b/tests/auto/blackbox/testdata/nested-groups/main3.cpp new file mode 100644 index 00000000..e14f806b --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/main3.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { } diff --git a/tests/auto/blackbox/testdata/nested-groups/modules/themodule/themodule.qbs b/tests/auto/blackbox/testdata/nested-groups/modules/themodule/themodule.qbs new file mode 100644 index 00000000..ffca11cd --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/modules/themodule/themodule.qbs @@ -0,0 +1,11 @@ +import qbs + +Module { + Group { + cpp.defines: ["REQUIRED_FOR_FILE3"] + Group { + prefix: product.sourceDirectory + '/' + files: ["file3.cpp", "file3.h"] + } + } +} diff --git a/tests/auto/blackbox/testdata/nested-groups/nested-groups.qbs b/tests/auto/blackbox/testdata/nested-groups/nested-groups.qbs new file mode 100644 index 00000000..7ff4cf28 --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/nested-groups.qbs @@ -0,0 +1,40 @@ +import qbs + +CppApplication { + consoleApplication: true + Depends { name: "themodule" } + files: ["main.cpp"] + Group { + cpp.defines: ["REQUIRED_FOR_FILE1", "BREAKS_FILE2"] + + fileTags: ["cpp"] + + // This group has no files, and that's okay. + + Group { + files: ["other.cpp", "other.h"] + Group { + cpp.defines: outer.concat(["ALSO_REQUIRED_FOR_FILE1"]) + files: ["file1.cpp", "file1.h"] + } + Group { + cpp.defines: ["REQUIRED_FOR_FILE2"] + files: ["file2.cpp", "file2.h"] + } + Group { + name: "disabled" + condition: false + Group { + name: "indirectly disabled" + condition: true + files: ["main2.cpp"] + } + } + Group { + name: "no tags" + fileTags: [] + files: ["main3.cpp"] + } + } + } +} diff --git a/tests/auto/blackbox/testdata/nested-groups/other.cpp b/tests/auto/blackbox/testdata/nested-groups/other.cpp new file mode 100644 index 00000000..c18f141e --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/other.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef REQUIRED_FOR_FILE1 +#error "missing define" +#endif + +#ifndef BREAKS_FILE2 +#error "missing define" +#endif + +void other() { } diff --git a/tests/auto/blackbox/testdata/nested-groups/other.h b/tests/auto/blackbox/testdata/nested-groups/other.h new file mode 100644 index 00000000..4c04b3ed --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-groups/other.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void other(); diff --git a/tests/auto/blackbox/testdata/nested-properties/dummy.txt b/tests/auto/blackbox/testdata/nested-properties/dummy.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/nested-properties/modules/higherlevel/higher-level.qbs b/tests/auto/blackbox/testdata/nested-properties/modules/higherlevel/higher-level.qbs new file mode 100644 index 00000000..a3c117ef --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-properties/modules/higherlevel/higher-level.qbs @@ -0,0 +1,6 @@ +import qbs + +Module { + Depends { name: "lowerlevel" } + lowerlevel.propDependency: "value in higherlevel" +} diff --git a/tests/auto/blackbox/testdata/nested-properties/modules/lowerlevel/lower-level.qbs b/tests/auto/blackbox/testdata/nested-properties/modules/lowerlevel/lower-level.qbs new file mode 100644 index 00000000..37356681 --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-properties/modules/lowerlevel/lower-level.qbs @@ -0,0 +1,22 @@ +import qbs + +Module { + property string propDependency: "value in lowerlevel module" + property string prop: propDependency + property string someOtherProp + + Rule { + inputs: ["dummy-input"] + Artifact { + filePath: "dummy.out" + fileTags: "mytype" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.sourceCode = function() { }; + var prop = product.moduleProperty("lowerlevel", "prop"); + cmd.description = "lowerlevel.prop is '" + prop + "'."; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/nested-properties/product.qbs b/tests/auto/blackbox/testdata/nested-properties/product.qbs new file mode 100644 index 00000000..916cee15 --- /dev/null +++ b/tests/auto/blackbox/testdata/nested-properties/product.qbs @@ -0,0 +1,22 @@ +import qbs + +Project { + Product { + name: "dep" + Export { + Depends { name: "lowerlevel" } + lowerlevel.someOtherProp: "blubb" + } + } + Product { + type: "mytype" + name: "toplevel" + Depends { name: "higherlevel" } + Depends { name: "lowerlevel" } + Depends { name: "dep" } + Group { + files: ["dummy.txt"] + fileTags: ["dummy-input"] + } + } +} diff --git a/tests/auto/blackbox/testdata/new-output-artifact/input.txt b/tests/auto/blackbox/testdata/new-output-artifact/input.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/new-output-artifact/new-output-artifact.qbs b/tests/auto/blackbox/testdata/new-output-artifact/new-output-artifact.qbs new file mode 100644 index 00000000..10e0e430 --- /dev/null +++ b/tests/auto/blackbox/testdata/new-output-artifact/new-output-artifact.qbs @@ -0,0 +1,37 @@ +import qbs +import qbs.TextFile + +Product { + name: "theProduct" + type: ["output"] + property int artifactCount: 99 + Group { + files: ["input.txt"] + fileTags: ["input"] + } + Group { + fileTagsFilter: product.type + qbs.install: true + } + Rule { + inputs: ["input"] + outputFileTags: ["output"] + outputArtifacts: { + var list = []; + for (var i = 0; i < product.artifactCount; ++i) + list.push({ filePath: "output_" + i + ".out", fileTags: ["output"]}); + return list; + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + for (var i = 0; i < outputs["output"].length; ++i) { + var f = new TextFile(outputs["output"][i].filePath, TextFile.WriteOnly); + f.close(); + } + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/nodejs/hello.js b/tests/auto/blackbox/testdata/nodejs/hello.js new file mode 100644 index 00000000..43f1e2ff --- /dev/null +++ b/tests/auto/blackbox/testdata/nodejs/hello.js @@ -0,0 +1,3 @@ +if (console) { + console.log("hello world"); +} diff --git a/tests/auto/blackbox/testdata/nodejs/hello.qbs b/tests/auto/blackbox/testdata/nodejs/hello.qbs new file mode 100644 index 00000000..d5e92209 --- /dev/null +++ b/tests/auto/blackbox/testdata/nodejs/hello.qbs @@ -0,0 +1,6 @@ +import qbs + +NodeJSApplication { + nodejs.applicationFile: "hello.js" + name: "hello" +} diff --git a/tests/auto/blackbox/testdata/non-broken-files-in-broken-product/broken.cpp b/tests/auto/blackbox/testdata/non-broken-files-in-broken-product/broken.cpp new file mode 100644 index 00000000..57f89ed1 --- /dev/null +++ b/tests/auto/blackbox/testdata/non-broken-files-in-broken-product/broken.cpp @@ -0,0 +1 @@ +broken diff --git a/tests/auto/blackbox/testdata/non-broken-files-in-broken-product/fine.cpp b/tests/auto/blackbox/testdata/non-broken-files-in-broken-product/fine.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/non-broken-files-in-broken-product/fine.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/non-broken-files-in-broken-product/project.qbs b/tests/auto/blackbox/testdata/non-broken-files-in-broken-product/project.qbs new file mode 100644 index 00000000..1ec764df --- /dev/null +++ b/tests/auto/blackbox/testdata/non-broken-files-in-broken-product/project.qbs @@ -0,0 +1,6 @@ +import qbs + +CppApplication { + consoleApplication: true + files: ["fine.cpp", "broken.cpp"] +} diff --git a/tests/auto/blackbox/testdata/non-default-product/main.cpp b/tests/auto/blackbox/testdata/non-default-product/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/non-default-product/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/non-default-product/project.qbs b/tests/auto/blackbox/testdata/non-default-product/project.qbs new file mode 100644 index 00000000..49834497 --- /dev/null +++ b/tests/auto/blackbox/testdata/non-default-product/project.qbs @@ -0,0 +1,17 @@ +import qbs + +Project { + CppApplication { + name: "default app" + consoleApplication: true + files: "main.cpp" + } + + CppApplication { + name: "non-default app" + Depends { name: "default app" } + consoleApplication: true + builtByDefault: false + files: "main.cpp" + } +} diff --git a/tests/auto/blackbox/testdata/nsis/hello.bat b/tests/auto/blackbox/testdata/nsis/hello.bat new file mode 100644 index 00000000..3af583cd --- /dev/null +++ b/tests/auto/blackbox/testdata/nsis/hello.bat @@ -0,0 +1 @@ +echo Hello world! diff --git a/tests/auto/blackbox/testdata/nsis/hello.nsi b/tests/auto/blackbox/testdata/nsis/hello.nsi new file mode 100644 index 00000000..70b73056 --- /dev/null +++ b/tests/auto/blackbox/testdata/nsis/hello.nsi @@ -0,0 +1,19 @@ +SetCompressor zlib + +!ifdef Win64 + Name "Qbs Hello - ${qbs.architecture} (64-bit)" +!else + Name "Qbs Hello - ${qbs.architecture} (32-bit)" +!endif + +OutFile "you-should-not-see-a-file-with-this-name.exe" +InstallDir "$DESKTOP\Qbs Hello" +RequestExecutionLevel user + +Page directory +Page instfiles + +Section "" + SetOutPath "$INSTDIR" + File "${batchFile}" +SectionEnd diff --git a/tests/auto/blackbox/testdata/nsis/hello.qbs b/tests/auto/blackbox/testdata/nsis/hello.qbs new file mode 100644 index 00000000..b7f3da50 --- /dev/null +++ b/tests/auto/blackbox/testdata/nsis/hello.qbs @@ -0,0 +1,10 @@ +import qbs + +NSISSetup { + condition: qbs.targetOS.contains("windows") + name: "Qbs Hello" + targetName: "qbs-hello-" + qbs.architecture + files: ["hello.nsi", "hello.bat"] + nsis.defines: ["batchFile=hello.bat"] + nsis.compressor: "lzma-solid" +} diff --git a/tests/auto/blackbox/testdata/objc-arc/arc.m b/tests/auto/blackbox/testdata/objc-arc/arc.m new file mode 100644 index 00000000..6d8e22f0 --- /dev/null +++ b/tests/auto/blackbox/testdata/objc-arc/arc.m @@ -0,0 +1,3 @@ +#if !__has_feature(objc_arc) +#error Not using ARC but should be +#endif diff --git a/tests/auto/blackbox/testdata/objc-arc/arc.mm b/tests/auto/blackbox/testdata/objc-arc/arc.mm new file mode 100644 index 00000000..6d8e22f0 --- /dev/null +++ b/tests/auto/blackbox/testdata/objc-arc/arc.mm @@ -0,0 +1,3 @@ +#if !__has_feature(objc_arc) +#error Not using ARC but should be +#endif diff --git a/tests/auto/blackbox/testdata/objc-arc/main.m b/tests/auto/blackbox/testdata/objc-arc/main.m new file mode 100644 index 00000000..071c53c9 --- /dev/null +++ b/tests/auto/blackbox/testdata/objc-arc/main.m @@ -0,0 +1,4 @@ +#if __has_feature(objc_arc) +#error Using ARC but shouldn't be +#endif +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/objc-arc/mrc.m b/tests/auto/blackbox/testdata/objc-arc/mrc.m new file mode 100644 index 00000000..1d907b5b --- /dev/null +++ b/tests/auto/blackbox/testdata/objc-arc/mrc.m @@ -0,0 +1,3 @@ +#if __has_feature(objc_arc) +#error Using ARC but shouldn't be +#endif diff --git a/tests/auto/blackbox/testdata/objc-arc/mrc.mm b/tests/auto/blackbox/testdata/objc-arc/mrc.mm new file mode 100644 index 00000000..1d907b5b --- /dev/null +++ b/tests/auto/blackbox/testdata/objc-arc/mrc.mm @@ -0,0 +1,3 @@ +#if __has_feature(objc_arc) +#error Using ARC but shouldn't be +#endif diff --git a/tests/auto/blackbox/testdata/objc-arc/objc-arc.qbs b/tests/auto/blackbox/testdata/objc-arc/objc-arc.qbs new file mode 100644 index 00000000..672de3cb --- /dev/null +++ b/tests/auto/blackbox/testdata/objc-arc/objc-arc.qbs @@ -0,0 +1,21 @@ +import qbs + +Product { + Depends { name: "cpp" } + consoleApplication: true + type: ["application"] + condition: qbs.targetOS.contains("darwin") + + Group { + cpp.automaticReferenceCounting: true + files: ["arc.m", "arc.mm"] + } + + Group { + cpp.automaticReferenceCounting: false + files: ["mrc.m", "mrc.mm"] + } + + files: "main.m" + cpp.minimumMacosVersion: "10.7" +} diff --git a/tests/auto/blackbox/testdata/output-artifact-auto-tagging/broken.cpp.in b/tests/auto/blackbox/testdata/output-artifact-auto-tagging/broken.cpp.in new file mode 100644 index 00000000..10bec280 --- /dev/null +++ b/tests/auto/blackbox/testdata/output-artifact-auto-tagging/broken.cpp.in @@ -0,0 +1 @@ +void f() { blubb(); } diff --git a/tests/auto/blackbox/testdata/output-artifact-auto-tagging/main.cpp.in b/tests/auto/blackbox/testdata/output-artifact-auto-tagging/main.cpp.in new file mode 100644 index 00000000..237c8ce1 --- /dev/null +++ b/tests/auto/blackbox/testdata/output-artifact-auto-tagging/main.cpp.in @@ -0,0 +1 @@ +int main() {} diff --git a/tests/auto/blackbox/testdata/output-artifact-auto-tagging/output-artifact-auto-tagging.qbs b/tests/auto/blackbox/testdata/output-artifact-auto-tagging/output-artifact-auto-tagging.qbs new file mode 100644 index 00000000..49a732e2 --- /dev/null +++ b/tests/auto/blackbox/testdata/output-artifact-auto-tagging/output-artifact-auto-tagging.qbs @@ -0,0 +1,27 @@ +import qbs +import qbs.File + +CppApplication { + consoleApplication: true + Group { + files: ["broken.cpp.in", "main.cpp.in"] + fileTags: ["cpp.in"] + } + Rule { + multiplex: true + inputs: ["cpp.in"] + outputFileTags: ["cpp"] + outputArtifacts: [{ filePath: "main.cpp" }, { filePath: "broken.nomatch" }] + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "creating main.cpp"; + cmd.sourceCode = function() { + File.copy(product.sourceDirectory + "/main.cpp.in", + product.buildDirectory + "/main.cpp"); + File.copy(product.sourceDirectory + "/broken.cpp.in", + product.buildDirectory + "/broken.nomatch"); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/overrideProjectProperties/helper_lib.qbs b/tests/auto/blackbox/testdata/overrideProjectProperties/helper_lib.qbs new file mode 100644 index 00000000..16c68064 --- /dev/null +++ b/tests/auto/blackbox/testdata/overrideProjectProperties/helper_lib.qbs @@ -0,0 +1,8 @@ +import qbs + +DynamicLibrary { + name: "helperLib" + files: "helperlib.cpp" + Depends { name: "cpp" } + bundle.isBundle: false +} diff --git a/tests/auto/blackbox/testdata/overrideProjectProperties/helperlib.cpp b/tests/auto/blackbox/testdata/overrideProjectProperties/helperlib.cpp new file mode 100644 index 00000000..7d2b64c3 --- /dev/null +++ b/tests/auto/blackbox/testdata/overrideProjectProperties/helperlib.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined(_WIN32) +# define EXPORT __declspec(dllexport) +#else +# define EXPORT +#endif + +void EXPORT helperFunc() {} + diff --git a/tests/auto/blackbox/testdata/overrideProjectProperties/main.cpp b/tests/auto/blackbox/testdata/overrideProjectProperties/main.cpp new file mode 100644 index 00000000..ed95605b --- /dev/null +++ b/tests/auto/blackbox/testdata/overrideProjectProperties/main.cpp @@ -0,0 +1,32 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() +{ + return 0; +} diff --git a/tests/auto/blackbox/testdata/overrideProjectProperties/main2.cpp b/tests/auto/blackbox/testdata/overrideProjectProperties/main2.cpp new file mode 100644 index 00000000..291135ae --- /dev/null +++ b/tests/auto/blackbox/testdata/overrideProjectProperties/main2.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined(_WIN32) +# define IMPORT __declspec(dllimport) +#else +# define IMPORT +#endif + +void IMPORT helperFunc(); + +int main() +{ + helperFunc(); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/overrideProjectProperties/project.qbs b/tests/auto/blackbox/testdata/overrideProjectProperties/project.qbs new file mode 100644 index 00000000..6e8ef62a --- /dev/null +++ b/tests/auto/blackbox/testdata/overrideProjectProperties/project.qbs @@ -0,0 +1,35 @@ +import qbs 1.0 + +Project { + property string nameSuffix: "" + property bool someBool + property int someInt + property stringList someStringList + Product { + consoleApplication: true + type: "application" + property string mainFile: "" + name: "MyApp" + nameSuffix + Depends { name: "cpp" } + files: { + // Check types of the project's custom properties here. + // Provoke a build error if the expected types do not match. + var wrongFile = "doesnotexist.cpp"; + if (typeof project.someBool != "boolean") { + console.info("someBool has a wrong type: " + typeof project.someBool); + return wrongFile; + } + if (typeof project.someInt != "number") { + console.info("someInt has a wrong type: " + typeof project.someInt); + return wrongFile; + } + if (typeof project.someStringList != "object") { + console.info("someStringList has a wrong type: " + typeof project.someStringList); + return wrongFile; + } + + // Return the mainFile property that is set on the command line. + return [mainFile]; + } + } +} diff --git a/tests/auto/blackbox/testdata/overrideProjectProperties/project_using_helper_lib.qbs b/tests/auto/blackbox/testdata/overrideProjectProperties/project_using_helper_lib.qbs new file mode 100644 index 00000000..c61716e6 --- /dev/null +++ b/tests/auto/blackbox/testdata/overrideProjectProperties/project_using_helper_lib.qbs @@ -0,0 +1,14 @@ +import qbs 1.0 + +Project { + property bool linkSuccessfully: false + references: linkSuccessfully ? ["helper_lib.qbs"] : [] + CppApplication { + consoleApplication: true + Depends { + condition: project.linkSuccessfully + name: "helperLib" + } + files: "main2.cpp" + } +} diff --git a/tests/auto/blackbox/testdata/pch-change-tracking/header1.h b/tests/auto/blackbox/testdata/pch-change-tracking/header1.h new file mode 100644 index 00000000..2837dbdb --- /dev/null +++ b/tests/auto/blackbox/testdata/pch-change-tracking/header1.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include diff --git a/tests/auto/blackbox/testdata/pch-change-tracking/header2.h b/tests/auto/blackbox/testdata/pch-change-tracking/header2.h new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/blackbox/testdata/pch-change-tracking/header2.h @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/blackbox/testdata/pch-change-tracking/main.cpp b/tests/auto/blackbox/testdata/pch-change-tracking/main.cpp new file mode 100644 index 00000000..609293e6 --- /dev/null +++ b/tests/auto/blackbox/testdata/pch-change-tracking/main.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "header2.h" +#include "pch.h" + +int main() +{ + std::cout << "Tach." << std::endl; +} diff --git a/tests/auto/blackbox/testdata/pch-change-tracking/pch-change-tracking.qbs b/tests/auto/blackbox/testdata/pch-change-tracking/pch-change-tracking.qbs new file mode 100644 index 00000000..76fd41a8 --- /dev/null +++ b/tests/auto/blackbox/testdata/pch-change-tracking/pch-change-tracking.qbs @@ -0,0 +1,14 @@ +import qbs + +CppApplication { + cpp.useCxxPrecompiledHeader: true + files: [ + "header1.h", + "header2.h", + "main.cpp", + ] + Group { + files: ["pch.h"] + fileTags: ["cpp_pch_src"] + } +} diff --git a/tests/auto/blackbox/testdata/pch-change-tracking/pch.h b/tests/auto/blackbox/testdata/pch-change-tracking/pch.h new file mode 100644 index 00000000..bdceaa8a --- /dev/null +++ b/tests/auto/blackbox/testdata/pch-change-tracking/pch.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "header1.h" diff --git a/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/modules/themodule/themodule.qbs b/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/modules/themodule/themodule.qbs new file mode 100644 index 00000000..fbacf846 --- /dev/null +++ b/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/modules/themodule/themodule.qbs @@ -0,0 +1,28 @@ +import qbs +import qbs.Probes + +Module { + Probes.PkgConfigProbe { + id: theProbe + name: "dummy" + } + + property stringList libs: theProbe.libs + + Rule { + multiplex: true + Artifact { + filePath: "dummy.out" + fileTags: ["theType"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + console.info(product.name + " libs: " + + JSON.stringify(product.moduleProperty("themodule", "libs"))); + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/pkg-config.qbs b/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/pkg-config.qbs new file mode 100644 index 00000000..0eac3cca --- /dev/null +++ b/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/pkg-config.qbs @@ -0,0 +1,29 @@ +import qbs + +Project { + property string packageBaseName + + Product { + name: "theProduct1" + type: ["theType"] + + Depends { name: "themodule" } + qbs.sysroot: path + "/sysroot1" + } + + Product { + name: "theProduct2" + type: ["theType"] + + Depends { name: "themodule" } + qbs.sysroot: path + "/sysroot2" + } + + Product { + name: "theProduct3" + type: ["theType"] + + Depends { name: "themodule" } + qbs.sysroot: path + "/sysroot1" + } +} diff --git a/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/sysroot1/usr/share/pkgconfig/dummy.pc b/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/sysroot1/usr/share/pkgconfig/dummy.pc new file mode 100644 index 00000000..9e89e44b --- /dev/null +++ b/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/sysroot1/usr/share/pkgconfig/dummy.pc @@ -0,0 +1,7 @@ +Name: dummy1 +Description: dummy1 package +Version: 0.0.1 + +Requires: +Libs: -L/usr/dummy -ldummy1 +Cflags: diff --git a/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/sysroot2/usr/share/pkgconfig/dummy.pc b/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/sysroot2/usr/share/pkgconfig/dummy.pc new file mode 100644 index 00000000..9e89e44b --- /dev/null +++ b/tests/auto/blackbox/testdata/pkg-config-probe-sysroot/sysroot2/usr/share/pkgconfig/dummy.pc @@ -0,0 +1,7 @@ +Name: dummy1 +Description: dummy1 package +Version: 0.0.1 + +Requires: +Libs: -L/usr/dummy -ldummy1 +Cflags: diff --git a/tests/auto/blackbox/testdata/pkg-config-probe/dummy1/dummy1.pc b/tests/auto/blackbox/testdata/pkg-config-probe/dummy1/dummy1.pc new file mode 100644 index 00000000..94263ec0 --- /dev/null +++ b/tests/auto/blackbox/testdata/pkg-config-probe/dummy1/dummy1.pc @@ -0,0 +1,7 @@ +Name: dummy1 +Description: dummy1 package +Version: 0.0.1 + +Requires: +Libs: -Ldummydir1 -ldummy1 +Cflags: diff --git a/tests/auto/blackbox/testdata/pkg-config-probe/dummy2/dummy2.pc b/tests/auto/blackbox/testdata/pkg-config-probe/dummy2/dummy2.pc new file mode 100644 index 00000000..c6e5631d --- /dev/null +++ b/tests/auto/blackbox/testdata/pkg-config-probe/dummy2/dummy2.pc @@ -0,0 +1,7 @@ +Name: dummy2 +Description: dummy2 package +Version: 0.0.2 + +Requires: +Libs: -Ldummydir2 -ldummy2 +Cflags: diff --git a/tests/auto/blackbox/testdata/pkg-config-probe/modules/themodule/themodule.qbs b/tests/auto/blackbox/testdata/pkg-config-probe/modules/themodule/themodule.qbs new file mode 100644 index 00000000..ec6ff7d6 --- /dev/null +++ b/tests/auto/blackbox/testdata/pkg-config-probe/modules/themodule/themodule.qbs @@ -0,0 +1,42 @@ +import qbs +import qbs.Probes + +Module { + property string packageName + property string libDir + + Probes.PkgConfigProbe { + id: theProbe + name: packageName + libDirs: [libDir] + } + + property bool probeSuccess: theProbe.found + property stringList libs: theProbe.libs + property stringList cFlags: theProbe.cflags + property string packageVersion: theProbe.modversion + + Rule { + multiplex: true + Artifact { + filePath: "dummy.out" + fileTags: ["theType"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + console.info(product.name + " found: " + + product.moduleProperty("themodule", "probeSuccess")); + console.info(product.name + " libs: " + + JSON.stringify(product.moduleProperty("themodule", "libs"))); + console.info(product.name + " cflags: " + + JSON.stringify(product.moduleProperty("themodule", "cFlags"))); + console.info(product.name + " version: " + + product.moduleProperty("themodule", "packageVersion")); + } + return [cmd]; + } + } + +} diff --git a/tests/auto/blackbox/testdata/pkg-config-probe/pkg-config.qbs b/tests/auto/blackbox/testdata/pkg-config-probe/pkg-config.qbs new file mode 100644 index 00000000..db831fbc --- /dev/null +++ b/tests/auto/blackbox/testdata/pkg-config-probe/pkg-config.qbs @@ -0,0 +1,23 @@ +import qbs + +Project { + property string packageBaseName + + Product { + name: "theProduct1" + type: ["theType"] + + Depends { name: "themodule" } + themodule.packageName: project.packageBaseName + "1" + themodule.libDir: path + "/dummy1" + } + + Product { + name: "theProduct2" + type: ["theType"] + + Depends { name: "themodule" } + themodule.packageName: project.packageBaseName + "2" + themodule.libDir: path + "/dummy2" + } +} diff --git a/tests/auto/blackbox/testdata/plugin-meta-data/app.cpp b/tests/auto/blackbox/testdata/plugin-meta-data/app.cpp new file mode 100644 index 00000000..187e57e2 --- /dev/null +++ b/tests/auto/blackbox/testdata/plugin-meta-data/app.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QPluginLoader loader(QLatin1String("thePlugin")); + const QJsonValue v = loader.metaData().value(QLatin1String("theKey")); + if (!v.isArray()) { + qDebug() << "value is" << v; + return 1; + } + const QJsonArray a = v.toArray(); + if (a.count() != 1 || a.first() != QLatin1String("theValue")) { + qDebug() << "value is" << v; + return 1; + } + return 0; +} diff --git a/tests/auto/blackbox/testdata/plugin-meta-data/plugin-meta-data.qbs b/tests/auto/blackbox/testdata/plugin-meta-data/plugin-meta-data.qbs new file mode 100644 index 00000000..81069bea --- /dev/null +++ b/tests/auto/blackbox/testdata/plugin-meta-data/plugin-meta-data.qbs @@ -0,0 +1,39 @@ +import qbs + +Project { + QtApplication { + consoleApplication: true + + Depends { name: "thePlugin" } + + cpp.cxxLanguageVersion: "c++11" + cpp.rpaths: qbs.targetOS.contains("darwin") ? ["@loader_path"] : ["$ORIGIN"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + + files: ["app.cpp"] + } + + DynamicLibrary { + name: "thePlugin" + + Depends { name: "cpp" } + Depends { name: "Qt.core" } + + bundle.isBundle: false + cpp.defines: ["QT_PLUGIN"] + cpp.cxxLanguageVersion: "c++11" + cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined + Qt.core.pluginMetaData: ["theKey=theValue"] + + Group { + fileTagsFilter: product.type + qbs.install: true + } + + files: ["theplugin.cpp"] + } +} diff --git a/tests/auto/blackbox/testdata/plugin-meta-data/theplugin.cpp b/tests/auto/blackbox/testdata/plugin-meta-data/theplugin.cpp new file mode 100644 index 00000000..75a0e893 --- /dev/null +++ b/tests/auto/blackbox/testdata/plugin-meta-data/theplugin.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +class ThePlugin : public QObject +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qbs.ThePlugin") +}; + +#include diff --git a/tests/auto/blackbox/testdata/probe-change-tracking/probe-change-tracking.qbs b/tests/auto/blackbox/testdata/probe-change-tracking/probe-change-tracking.qbs new file mode 100644 index 00000000..fbc796e8 --- /dev/null +++ b/tests/auto/blackbox/testdata/probe-change-tracking/probe-change-tracking.qbs @@ -0,0 +1,13 @@ +import qbs + +Product { + name: "theProduct" + property bool runProbe + + Probe { + condition: product.runProbe + configure: { + console.info("running probe"); + } + } +} diff --git a/tests/auto/blackbox/testdata/probe-in-exported-module/dependee.qbs b/tests/auto/blackbox/testdata/probe-in-exported-module/dependee.qbs new file mode 100644 index 00000000..121ec6c3 --- /dev/null +++ b/tests/auto/blackbox/testdata/probe-in-exported-module/dependee.qbs @@ -0,0 +1,16 @@ +import qbs + +Product { + name: "dependee" + Depends { name: "myothermodule" } + Depends { name: "dependency" } + type: ["out", "dep-out"] + Group { + files: "test.in" + fileTags: ["dep-in"] + } + Group { + files: "test2.in" + fileTags: ["in"] + } +} diff --git a/tests/auto/blackbox/testdata/probe-in-exported-module/dependency.qbs b/tests/auto/blackbox/testdata/probe-in-exported-module/dependency.qbs new file mode 100644 index 00000000..1413777d --- /dev/null +++ b/tests/auto/blackbox/testdata/probe-in-exported-module/dependency.qbs @@ -0,0 +1,8 @@ +import qbs + +Product { + name: "dependency" + Export { + Depends { name: "mymodule" } + } +} diff --git a/tests/auto/blackbox/testdata/probe-in-exported-module/modules/depmodule/depmodule.qbs b/tests/auto/blackbox/testdata/probe-in-exported-module/modules/depmodule/depmodule.qbs new file mode 100644 index 00000000..f2d7e8dc --- /dev/null +++ b/tests/auto/blackbox/testdata/probe-in-exported-module/modules/depmodule/depmodule.qbs @@ -0,0 +1,23 @@ +import qbs + +Module { + property string prop + property stringList listProp: [] + + Rule { + inputs: ["dep-in"] + Artifact { + filePath: "dummy.txt" + fileTags: ["dep-out"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating dep-out artifact"; + cmd.sourceCode = function() { + console.info("prop: " + product.moduleProperty("depmodule", "prop")); + console.info("listProp: " + product.moduleProperty("depmodule", "listProp")); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/probe-in-exported-module/modules/mymodule/mymodule.qbs b/tests/auto/blackbox/testdata/probe-in-exported-module/modules/mymodule/mymodule.qbs new file mode 100644 index 00000000..e68899ae --- /dev/null +++ b/tests/auto/blackbox/testdata/probe-in-exported-module/modules/mymodule/mymodule.qbs @@ -0,0 +1,28 @@ +import qbs + +Module { + Depends { name: "depmodule" } + Probe { + id: theProbe + configure: { found = true; } + } + property bool found: theProbe.found + depmodule.prop: found ? "yes" : "no" + depmodule.listProp: theProbe.found ? ["my"] : [] + + Rule { + inputs: ["in"] + Artifact { + filePath: "dummy2.txt" + fileTags: ["out"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating out artifact"; + cmd.sourceCode = function() { + console.info("found: " + product.moduleProperty("mymodule", "found")); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/probe-in-exported-module/modules/myothermodule/myothermodule.qbs b/tests/auto/blackbox/testdata/probe-in-exported-module/modules/myothermodule/myothermodule.qbs new file mode 100644 index 00000000..d3009f53 --- /dev/null +++ b/tests/auto/blackbox/testdata/probe-in-exported-module/modules/myothermodule/myothermodule.qbs @@ -0,0 +1,6 @@ +import qbs + +Module { + Depends { name: "depmodule" } + depmodule.listProp: ["myother"] +} diff --git a/tests/auto/blackbox/testdata/probe-in-exported-module/probe-in-exported-module.qbs b/tests/auto/blackbox/testdata/probe-in-exported-module/probe-in-exported-module.qbs new file mode 100644 index 00000000..4e0f13f6 --- /dev/null +++ b/tests/auto/blackbox/testdata/probe-in-exported-module/probe-in-exported-module.qbs @@ -0,0 +1,5 @@ +import qbs + +Project { + references: [ "dependee.qbs", "dependency.qbs" ] +} diff --git a/tests/auto/blackbox/testdata/probe-in-exported-module/test.in b/tests/auto/blackbox/testdata/probe-in-exported-module/test.in new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/probe-in-exported-module/test2.in b/tests/auto/blackbox/testdata/probe-in-exported-module/test2.in new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/probeProperties/bin/tool b/tests/auto/blackbox/testdata/probeProperties/bin/tool new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/probeProperties/main.c b/tests/auto/blackbox/testdata/probeProperties/main.c new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/probeProperties/main.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs b/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs new file mode 100644 index 00000000..36842e40 --- /dev/null +++ b/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs @@ -0,0 +1,30 @@ +import qbs +import qbs.Probes + +CppApplication { + Probes.PathProbe { + id: probe1 + names: ["bin/tool"] + platformPaths: [product.sourceDirectory] + } + + Probes.PathProbe { + id: probe2 + names: ["tool"] + platformPaths: [product.sourceDirectory + "/bin"] + } + + targetName: { + console.info("probe1.fileName=" + probe1.fileName); + console.info("probe1.path=" + probe1.path); + console.info("probe1.filePath=" + probe1.filePath); + + console.info("probe2.fileName=" + probe2.fileName); + console.info("probe2.path=" + probe2.path); + console.info("probe2.filePath=" + probe2.filePath); + + return name; + } + + files: ["main.c"] +} diff --git a/tests/auto/blackbox/testdata/probes-and-array-properties/modules/mymodule/mymodule.qbs b/tests/auto/blackbox/testdata/probes-and-array-properties/modules/mymodule/mymodule.qbs new file mode 100644 index 00000000..29809216 --- /dev/null +++ b/tests/auto/blackbox/testdata/probes-and-array-properties/modules/mymodule/mymodule.qbs @@ -0,0 +1,32 @@ +import qbs + +Module { + Probe { + id: propProbe + property stringList prop: [] + configure: { + prop = []; + prop.push("probe"); + found = true; + } + } + + property stringList prop: propProbe.found ? propProbe.prop : ["other"] + + Rule { + multiplex: true + alwaysRun: true + Artifact { + filePath: "dummy" + fileTags: ["the-output"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating dummy"; + cmd.sourceCode = function() { + console.info("prop: " + JSON.stringify(product.moduleProperty("mymodule", "prop"))); + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/probes-and-array-properties/probes-and-array-properties.qbs b/tests/auto/blackbox/testdata/probes-and-array-properties/probes-and-array-properties.qbs new file mode 100644 index 00000000..c7d88ec1 --- /dev/null +++ b/tests/auto/blackbox/testdata/probes-and-array-properties/probes-and-array-properties.qbs @@ -0,0 +1,8 @@ +import qbs + +Product { + name: "theProduct" + type: ["the-output"] + Depends { name: "mymodule" } +// mymodule.prop: ["product"] +} diff --git a/tests/auto/blackbox/testdata/probes-in-nested-modules/modules/inner/inner.qbs b/tests/auto/blackbox/testdata/probes-in-nested-modules/modules/inner/inner.qbs new file mode 100644 index 00000000..b6d39b02 --- /dev/null +++ b/tests/auto/blackbox/testdata/probes-in-nested-modules/modules/inner/inner.qbs @@ -0,0 +1,20 @@ +import qbs +import qbs.Probes + +Module { + property bool alt: false + + Probe { + id: foo + property string baz + property bool useAlt: alt + property string named: product.name + configure: { + console.info("running probe " + named); + baz = useAlt ? "hahaha" : "hello"; + found = true; + } + } + + property string something: foo.baz +} diff --git a/tests/auto/blackbox/testdata/probes-in-nested-modules/modules/outer/outer.qbs b/tests/auto/blackbox/testdata/probes-in-nested-modules/modules/outer/outer.qbs new file mode 100644 index 00000000..71b745f4 --- /dev/null +++ b/tests/auto/blackbox/testdata/probes-in-nested-modules/modules/outer/outer.qbs @@ -0,0 +1,19 @@ +import qbs + +Module { + Depends { name: "inner" } + + Probe { + id: foo2 + property string barz + property string named: product.name + configure: { + console.info("running second probe " + named); + barz = "goodbye"; + found = true; + } + } + + property string something: inner.something + property string somethingElse: foo2.barz +} diff --git a/tests/auto/blackbox/testdata/probes-in-nested-modules/probes-in-nested-modules.qbs b/tests/auto/blackbox/testdata/probes-in-nested-modules/probes-in-nested-modules.qbs new file mode 100644 index 00000000..334dfda2 --- /dev/null +++ b/tests/auto/blackbox/testdata/probes-in-nested-modules/probes-in-nested-modules.qbs @@ -0,0 +1,35 @@ +import qbs + +Project { + Product { + name: "a" + Depends { name: "outer" } + inner.alt: true + type: { + console.info("product " + name + ", inner.something = " + inner.something); + console.info("product " + name + ", outer.something = " + outer.something); + console.info("product " + name + ", outer.somethingElse = " + outer.somethingElse); + return ["foo"]; + } + } + + Product { + name: "b" + Depends { name: "inner" } + inner.alt: true + type: { + console.info("product " + name + ", inner.something = " + inner.something); + return ["foo"]; + } + } + + Product { + name: "c" + Depends { name: "inner" } + inner.alt: false + type: { + console.info("product " + name + ", inner.something = " + inner.something); + return ["foo"]; + } + } +} diff --git a/tests/auto/blackbox/testdata/product-dependencies-by-type/main.cpp b/tests/auto/blackbox/testdata/product-dependencies-by-type/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/product-dependencies-by-type/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/product-dependencies-by-type/project.qbs b/tests/auto/blackbox/testdata/product-dependencies-by-type/project.qbs new file mode 100644 index 00000000..ebfc45d3 --- /dev/null +++ b/tests/auto/blackbox/testdata/product-dependencies-by-type/project.qbs @@ -0,0 +1,66 @@ +import qbs +import qbs.TextFile + +Project { + CppApplication { + consoleApplication: true + name: "no-match" + files: "main.cpp" + } + + Project { + CppApplication { + consoleApplication: true + name: "app1" + files: "main.cpp" + } + CppApplication { + consoleApplication: true + name: "app2" + files: "main.cpp" + } + CppApplication { + consoleApplication: true + name: "app3" + files: "main.cpp" + } + + DynamicLibrary { + Depends { name: "cpp" } + name: "lib-product" + files: "main.cpp" + bundle.isBundle: false + } + + CppApplication { + type: base.concat(["app-list"]) + consoleApplication: true + name: "app list" + Depends { + productTypes: ["application"] + limitToSubProject: true + } + files: ["main.cpp"] + + Rule { + multiplex: true + inputsFromDependencies: "application" + Artifact { + filePath: "app-list.txt" + fileTags: "app-list" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Collecting apps"; + cmd.sourceCode = function() { + var file = new TextFile(output.filePath, TextFile.WriteOnly); + for (var i = 0; i < inputs["application"].length; ++i) + file.writeLine(inputs["application"][i].filePath); + file.close(); + }; + return cmd; + } + } + } + } +} diff --git a/tests/auto/blackbox/testdata/productproperties/app.qbs b/tests/auto/blackbox/testdata/productproperties/app.qbs new file mode 100644 index 00000000..405d08ad --- /dev/null +++ b/tests/auto/blackbox/testdata/productproperties/app.qbs @@ -0,0 +1,12 @@ +import qbs 1.0 + +Product { + consoleApplication: true + type: "application" + name: "blubb_user" + + files: "main.cpp" + + Depends { name: "blubb_header" } + Depends { name: "cpp" } +} diff --git a/tests/auto/blackbox/testdata/productproperties/blubb_header.h.in b/tests/auto/blackbox/testdata/productproperties/blubb_header.h.in new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/productproperties/header.qbs b/tests/auto/blackbox/testdata/productproperties/header.qbs new file mode 100644 index 00000000..5c3e2db6 --- /dev/null +++ b/tests/auto/blackbox/testdata/productproperties/header.qbs @@ -0,0 +1,35 @@ +import qbs 1.0 +import qbs.TextFile + +Product { + name: "blubb_header" + type: "hpp" + files: "blubb_header.h.in" + property string blubbProp: project.blubbProp + + Rule { + multiplex: true + Artifact { + filePath: "blubb_header.h" + fileTags: "hpp" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating blubb_header.h"; + cmd.highlight = "codegen"; + cmd.blubbProp = product.blubbProp; + cmd.sourceCode = function() { + file = new TextFile(output.filePath, TextFile.WriteOnly); + file.truncate(); + file.write("#define BLUBB_PROP " + blubbProp); + file.close(); + } + return cmd; + } + } + + Export { + Depends { name: "cpp" } + cpp.includePaths: product.buildDirectory + } +} diff --git a/tests/auto/blackbox/testdata/productproperties/main.cpp b/tests/auto/blackbox/testdata/productproperties/main.cpp new file mode 100644 index 00000000..cb991ec9 --- /dev/null +++ b/tests/auto/blackbox/testdata/productproperties/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "blubb_header.h" + +int main() +{ +#if BLUBB_PROP != 5 + blubb(); +#endif +} diff --git a/tests/auto/blackbox/testdata/productproperties/project.qbs b/tests/auto/blackbox/testdata/productproperties/project.qbs new file mode 100644 index 00000000..75c6224b --- /dev/null +++ b/tests/auto/blackbox/testdata/productproperties/project.qbs @@ -0,0 +1,6 @@ +import qbs 1.0 + +Project { + property string blubbProp: "5" + references: ["header.qbs", "app.qbs"] +} diff --git a/tests/auto/blackbox/testdata/project_filepath_check/main.cpp b/tests/auto/blackbox/testdata/project_filepath_check/main.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/project_filepath_check/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/project_filepath_check/project1.qbs b/tests/auto/blackbox/testdata/project_filepath_check/project1.qbs new file mode 100644 index 00000000..5fc79d43 --- /dev/null +++ b/tests/auto/blackbox/testdata/project_filepath_check/project1.qbs @@ -0,0 +1,5 @@ +import qbs 1.0 + +CppApplication { + files: "main.cpp" +} diff --git a/tests/auto/blackbox/testdata/project_filepath_check/project2.qbs b/tests/auto/blackbox/testdata/project_filepath_check/project2.qbs new file mode 100644 index 00000000..5fc79d43 --- /dev/null +++ b/tests/auto/blackbox/testdata/project_filepath_check/project2.qbs @@ -0,0 +1,5 @@ +import qbs 1.0 + +CppApplication { + files: "main.cpp" +} diff --git a/tests/auto/blackbox/testdata/proper quoting/main.cpp b/tests/auto/blackbox/testdata/proper quoting/main.cpp new file mode 100644 index 00000000..22cafeaa --- /dev/null +++ b/tests/auto/blackbox/testdata/proper quoting/main.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int bla(); + +int main() +{ + printf(DEFINE"\n"); + printf(DEFINEWITHSPACE"\n"); + printf(DEFINEWITHTAB"\n"); + printf(DEFINEWITHBACKSLASH"\n"); + + return bla(); +} diff --git a/tests/auto/blackbox/testdata/proper quoting/my static lib helper.cpp b/tests/auto/blackbox/testdata/proper quoting/my static lib helper.cpp new file mode 100644 index 00000000..96ccb1b4 --- /dev/null +++ b/tests/auto/blackbox/testdata/proper quoting/my static lib helper.cpp @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int helper_function() +{ + return 156; +} + diff --git a/tests/auto/blackbox/testdata/proper quoting/my static lib.cpp b/tests/auto/blackbox/testdata/proper quoting/my static lib.cpp new file mode 100644 index 00000000..e7490e80 --- /dev/null +++ b/tests/auto/blackbox/testdata/proper quoting/my static lib.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int bla() +{ + int n = getSomeNumber(); + printf("Hello World! The magic number is %d.", n); + return n; +} diff --git a/tests/auto/blackbox/testdata/proper quoting/proper quoting.qbs b/tests/auto/blackbox/testdata/proper quoting/proper quoting.qbs new file mode 100644 index 00000000..184ee13e --- /dev/null +++ b/tests/auto/blackbox/testdata/proper quoting/proper quoting.qbs @@ -0,0 +1,40 @@ +import qbs 1.0 + +Project { + Product { + type: "application" + consoleApplication: true + name: "Hello World" + files : [ "main.cpp" ] + Depends { name: "cpp" } + Depends { name: "my static lib" } + cpp.defines: [ + 'DEFINE="whitespaceless"', + 'DEFINEWITHSPACE="contains space"', + 'DEFINEWITHTAB="contains\ttab"', + 'DEFINEWITHBACKSLASH="backslash\\\\"', + ] + } + + Product { + type: "staticlibrary" + name : "my static lib" + files : [ "my static lib.cpp" ] + Depends { name: "cpp" } + Depends { name: "helper lib" } + } + + Product { + type: "staticlibrary" + name : "helper lib" + files : [ + "some helper/some helper.h", + "some helper/some helper.cpp" + ] + Depends { name: "cpp" } + Export { + Depends { name: "cpp" } + cpp.includePaths: [product.sourceDirectory + '/some helper'] + } + } +} diff --git a/tests/auto/blackbox/testdata/proper quoting/some helper/some helper.cpp b/tests/auto/blackbox/testdata/proper quoting/some helper/some helper.cpp new file mode 100644 index 00000000..981a07e1 --- /dev/null +++ b/tests/auto/blackbox/testdata/proper quoting/some helper/some helper.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "some helper.h" + +int getSomeNumber() +{ + return 156; +} + diff --git a/tests/auto/blackbox/testdata/proper quoting/some helper/some helper.h b/tests/auto/blackbox/testdata/proper quoting/some helper/some helper.h new file mode 100644 index 00000000..e0c0b2fa --- /dev/null +++ b/tests/auto/blackbox/testdata/proper quoting/some helper/some helper.h @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef HELPER_H +#define HELPER_H + +extern int getSomeNumber(); + +#endif + diff --git a/tests/auto/blackbox/testdata/properties-in-export-items/main1.cpp b/tests/auto/blackbox/testdata/properties-in-export-items/main1.cpp new file mode 100644 index 00000000..c1c3786a --- /dev/null +++ b/tests/auto/blackbox/testdata/properties-in-export-items/main1.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef P1 +int main() { } +#endif diff --git a/tests/auto/blackbox/testdata/properties-in-export-items/main2.cpp b/tests/auto/blackbox/testdata/properties-in-export-items/main2.cpp new file mode 100644 index 00000000..e5a0cd4d --- /dev/null +++ b/tests/auto/blackbox/testdata/properties-in-export-items/main2.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifdef P2 +int main() { } +#endif diff --git a/tests/auto/blackbox/testdata/properties-in-export-items/properties-in-export-items.qbs b/tests/auto/blackbox/testdata/properties-in-export-items/properties-in-export-items.qbs new file mode 100644 index 00000000..a3baf07a --- /dev/null +++ b/tests/auto/blackbox/testdata/properties-in-export-items/properties-in-export-items.qbs @@ -0,0 +1,31 @@ +import qbs + +Project { + minimumQbsVersion: "1.6" + + Product { + name: "dep" + Export { + property string theDefine: "" + Depends { name: "cpp" } + cpp.defines: [theDefine] + } + } + + Application { + name: "p1" + consoleApplication: true + Depends { name: "dep" } + dep.theDefine: "P1" + files: ["main1.cpp"] + } + Application { + name: "p2" + consoleApplication: true + Depends { name: "dep" } + Group { + dep.theDefine: "P2" + files: ["main2.cpp"] + } + } +} diff --git a/tests/auto/blackbox/testdata/property-precedence/dep.qbs b/tests/auto/blackbox/testdata/property-precedence/dep.qbs new file mode 100644 index 00000000..c464b7d2 --- /dev/null +++ b/tests/auto/blackbox/testdata/property-precedence/dep.qbs @@ -0,0 +1,11 @@ +import qbs + +Product { + name: "dep" + Export { + Depends { name: "leaf" } + Depends { name: "nonleaf" } + // leaf.scalarProp: "export" + // leaf.listProp: ["export"] + } +} diff --git a/tests/auto/blackbox/testdata/property-precedence/dummy.txt b/tests/auto/blackbox/testdata/property-precedence/dummy.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/property-precedence/modules/leaf/leaf.qbs b/tests/auto/blackbox/testdata/property-precedence/modules/leaf/leaf.qbs new file mode 100644 index 00000000..7e2833dd --- /dev/null +++ b/tests/auto/blackbox/testdata/property-precedence/modules/leaf/leaf.qbs @@ -0,0 +1,25 @@ +import qbs + +Module { + property string scalarProp: "leaf" + property stringList listProp: ["leaf"] + + Rule { + inputs: ["rule-input"] + Artifact { + filePath: "dummy" + fileTags: ["rule-output"] + alwaysUpdated: false + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + console.info("scalar prop: " + product.moduleProperty("leaf", "scalarProp")); + console.info("list prop: " + + JSON.stringify(product.moduleProperty("leaf", "listProp"))); + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/property-precedence/modules/nonleaf/nonleaf.qbs b/tests/auto/blackbox/testdata/property-precedence/modules/nonleaf/nonleaf.qbs new file mode 100644 index 00000000..d67b261c --- /dev/null +++ b/tests/auto/blackbox/testdata/property-precedence/modules/nonleaf/nonleaf.qbs @@ -0,0 +1,8 @@ +import qbs + +Module { + Depends { name: "leaf" } + + // leaf.scalarProp: "nonleaf" + // leaf.listProp: ["nonleaf"] +} diff --git a/tests/auto/blackbox/testdata/property-precedence/project.qbs b/tests/auto/blackbox/testdata/property-precedence/project.qbs new file mode 100644 index 00000000..5246c8a7 --- /dev/null +++ b/tests/auto/blackbox/testdata/property-precedence/project.qbs @@ -0,0 +1,18 @@ +import qbs + +Project { + references: ["dep.qbs"] + Product { + name: "toplevel" + type: ["rule-output"] + Depends { name: "leaf" } + Depends { name: "nonleaf" } + Depends { name: "dep" } + Group { + files: ["dummy.txt"] + fileTags: ["rule-input"] + } + // leaf.scalarProp: "product" + // leaf.listProp: ["product"] + } +} diff --git a/tests/auto/blackbox/testdata/propertyChanges/lib.cpp b/tests/auto/blackbox/testdata/propertyChanges/lib.cpp new file mode 100644 index 00000000..7e43bdcc --- /dev/null +++ b/tests/auto/blackbox/testdata/propertyChanges/lib.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +Q_DECL_EXPORT void f() {} diff --git a/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs b/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs new file mode 100644 index 00000000..4bdd5c7f --- /dev/null +++ b/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs @@ -0,0 +1,31 @@ +import qbs +import qbs.File + +Module { + FileTagger { + patterns: ["*.in"] + fileTags: "test-input" + } + + property string testProperty + + Rule { + inputs: ['test-input'] + Artifact { + fileTags: "test-output" + filePath: input.fileName + ".out" + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.highlight = "codegen"; + cmd.description = "Making output from input"; + cmd.sourceCode = function() { + // console.info('Change in source code'); + console.info(input.moduleProperty("TestModule", "testProperty")); + File.copy(input.filePath, output.filePath); + } + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata/propertyChanges/project.qbs b/tests/auto/blackbox/testdata/propertyChanges/project.qbs new file mode 100644 index 00000000..a2b42a62 --- /dev/null +++ b/tests/auto/blackbox/testdata/propertyChanges/project.qbs @@ -0,0 +1,91 @@ +import qbs 1.0 +import qbs.Environment +import qbs.File +import qbs.TextFile + +Project { + property var projectDefines: ["blubb2"] + property string fileContentSuffix: "suffix 1" + property string testProperty: "default value" + CppApplication { + name: qbs.enableDebugCode ? "product 1.debug" : "product 1.release" + cpp.defines: ["blubb1"] + files: "source1.cpp" + } + CppApplication { + Depends { name: 'library' } + name: "product 2" + cpp.defines: project.projectDefines + files: "source2.cpp" + } + CppApplication { + name: "product 3" + cpp.defines: Environment.getEnv("QBS_BLACKBOX_DEFINE") + files: "source3.cpp" + } + DynamicLibrary { + name: "library" + Depends { name: "Qt.core" } + files: "lib.cpp" + bundle.isBundle: false + } + + Product { + name: "generated text file" + type: ["my_output"] + property string fileContentPrefix: "prefix 1" + + Rule { + multiplex: true + Artifact { filePath: "nothing"; fileTags: ["my_output"] } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { console.info(product.fileContentPrefix); } + return cmd; + } + } + + Rule { + multiplex: true + Artifact { filePath: "generated.txt"; fileTags: ["my_output"] } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating " + output.filePath; + cmd.highlight = "codegen"; + cmd.sourceCode = function() { + file = new TextFile(output.filePath, TextFile.WriteOnly); + file.truncate(); + file.write(product.fileContentPrefix + "contents 1" + + project.fileContentSuffix); + file.close(); + } + return cmd; + } + } + } + + Product { + Depends { name: "ruletest" } + type: ["test-output2"] + Rule { + inputsFromDependencies: ['test-output'] + Artifact { + fileTags: "test-output2" + filePath: input.fileName + ".out2" + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.highlight = "codegen"; + cmd.description = "Making output from other output"; + cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); } + return cmd; + } + } + } + + references: "ruletest.qbs" + + qbsSearchPaths: "." +} diff --git a/tests/auto/blackbox/testdata/propertyChanges/ruletest.qbs b/tests/auto/blackbox/testdata/propertyChanges/ruletest.qbs new file mode 100644 index 00000000..21d6f75e --- /dev/null +++ b/tests/auto/blackbox/testdata/propertyChanges/ruletest.qbs @@ -0,0 +1,11 @@ +import qbs + +Product { + name: "ruletest" + type: "test-output" + Depends { name: "TestModule" } + Group { + files: "test.in" + TestModule.testProperty: project.testProperty + } +} diff --git a/tests/auto/blackbox/testdata/propertyChanges/source1.cpp b/tests/auto/blackbox/testdata/propertyChanges/source1.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/propertyChanges/source1.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/propertyChanges/source2.cpp b/tests/auto/blackbox/testdata/propertyChanges/source2.cpp new file mode 100644 index 00000000..13c5f83a --- /dev/null +++ b/tests/auto/blackbox/testdata/propertyChanges/source2.cpp @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} + diff --git a/tests/auto/blackbox/testdata/propertyChanges/source3.cpp b/tests/auto/blackbox/testdata/propertyChanges/source3.cpp new file mode 100644 index 00000000..13c5f83a --- /dev/null +++ b/tests/auto/blackbox/testdata/propertyChanges/source3.cpp @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} + diff --git a/tests/auto/blackbox/testdata/propertyChanges/test.in b/tests/auto/blackbox/testdata/propertyChanges/test.in new file mode 100644 index 00000000..8633abf1 --- /dev/null +++ b/tests/auto/blackbox/testdata/propertyChanges/test.in @@ -0,0 +1 @@ +blubb diff --git a/tests/auto/blackbox/testdata/qbsVersion/qbs-version.qbs b/tests/auto/blackbox/testdata/qbsVersion/qbs-version.qbs new file mode 100644 index 00000000..b07ee494 --- /dev/null +++ b/tests/auto/blackbox/testdata/qbsVersion/qbs-version.qbs @@ -0,0 +1,22 @@ +import qbs + +Project { + property string qbsVersion + property int qbsVersionMajor + property int qbsVersionMinor + property int qbsVersionPatch + + Product { + name: { + if (qbsVersion !== qbs.version || + qbsVersionMajor !== qbs.versionMajor || + qbsVersionMinor !== qbs.versionMinor || + qbsVersionPatch !== qbs.versionPatch) + throw("expected " + + [qbsVersion, qbsVersionMajor, qbsVersionMinor, qbsVersionPatch].join(", ") + + ", got " + + [qbs.version, qbs.versionMajor, qbs.versionMinor, qbs.versionPatch].join(", ")); + return "foo"; + } + } +} diff --git a/tests/auto/blackbox/testdata/qml-debugging/main.cpp b/tests/auto/blackbox/testdata/qml-debugging/main.cpp new file mode 100644 index 00000000..535d6d63 --- /dev/null +++ b/tests/auto/blackbox/testdata/qml-debugging/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) +#include +#include +typedef QGuiApplication Application; +#define AH_SO_THIS_IS_QT5 +#else +#include +#include +#define AH_SO_THIS_IS_QT4 +typedef QApplication Application; +#endif + +int main(int argc, char *argv[]) +{ + Application app(argc, argv); +#ifdef AH_SO_THIS_IS_QT5 + QQmlApplicationEngine engine; + engine.load(QUrl("blubb")); +#else + QDeclarativeView view; + view.setSource(QUrl("blubb")); +#endif + + return app.exec(); +} diff --git a/tests/auto/blackbox/testdata/qml-debugging/project.qbs b/tests/auto/blackbox/testdata/qml-debugging/project.qbs new file mode 100644 index 00000000..3606b4aa --- /dev/null +++ b/tests/auto/blackbox/testdata/qml-debugging/project.qbs @@ -0,0 +1,9 @@ +import qbs + +QtApplication { + name: "debuggable-app" + consoleApplication: true + Depends { name: "Qt.quick" } + Qt.quick.qmlDebugging: true + files: "main.cpp" +} diff --git a/tests/auto/blackbox/testdata/qobject-in-mm/main.mm b/tests/auto/blackbox/testdata/qobject-in-mm/main.mm new file mode 100644 index 00000000..40c464b8 --- /dev/null +++ b/tests/auto/blackbox/testdata/qobject-in-mm/main.mm @@ -0,0 +1,13 @@ +#include + +class Foo : public QObject +{ +Q_OBJECT +}; + +int main() +{ + Foo foo; + return 0; +} +#include "main.moc" diff --git a/tests/auto/blackbox/testdata/qobject-in-mm/project.qbs b/tests/auto/blackbox/testdata/qobject-in-mm/project.qbs new file mode 100644 index 00000000..2f819d01 --- /dev/null +++ b/tests/auto/blackbox/testdata/qobject-in-mm/project.qbs @@ -0,0 +1,6 @@ +import qbs + +CppApplication { + Depends { name: "Qt.core" } + files: ["main.mm"] +} diff --git a/tests/auto/blackbox/testdata/qrc/bla.cpp b/tests/auto/blackbox/testdata/qrc/bla.cpp new file mode 100644 index 00000000..e04f873a --- /dev/null +++ b/tests/auto/blackbox/testdata/qrc/bla.cpp @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() +{ + return 3; +} + diff --git a/tests/auto/blackbox/testdata/qrc/bla.qrc b/tests/auto/blackbox/testdata/qrc/bla.qrc new file mode 100644 index 00000000..46c93847 --- /dev/null +++ b/tests/auto/blackbox/testdata/qrc/bla.qrc @@ -0,0 +1,5 @@ + + + stuff.txt + + diff --git a/tests/auto/blackbox/testdata/qrc/i.qbs b/tests/auto/blackbox/testdata/qrc/i.qbs new file mode 100644 index 00000000..67b836dd --- /dev/null +++ b/tests/auto/blackbox/testdata/qrc/i.qbs @@ -0,0 +1,20 @@ +import qbs 1.0 + +Project { + Product { + consoleApplication: true + type: "application" + name: "i" + + Depends { + name: "Qt.core" + } + + files: [ + "bla.cpp", + "bla.qrc", + "stuff.txt" + ] + } +} + diff --git a/tests/auto/blackbox/testdata/qrc/stuff.txt b/tests/auto/blackbox/testdata/qrc/stuff.txt new file mode 100644 index 00000000..78f0d32c --- /dev/null +++ b/tests/auto/blackbox/testdata/qrc/stuff.txt @@ -0,0 +1 @@ +a resource file diff --git a/tests/auto/blackbox/testdata/qtscxml/dummystatemachine.scxml b/tests/auto/blackbox/testdata/qtscxml/dummystatemachine.scxml new file mode 100644 index 00000000..6c751866 --- /dev/null +++ b/tests/auto/blackbox/testdata/qtscxml/dummystatemachine.scxml @@ -0,0 +1,3 @@ + + + diff --git a/tests/auto/blackbox/testdata/qtscxml/main.cpp b/tests/auto/blackbox/testdata/qtscxml/main.cpp new file mode 100644 index 00000000..c9a7d774 --- /dev/null +++ b/tests/auto/blackbox/testdata/qtscxml/main.cpp @@ -0,0 +1,15 @@ +#ifdef HAS_QTSCXML +#include +#endif + +#include + +int main() +{ +#ifdef HAS_QTSCXML + QbsTest::QbsStateMachine machine; + std::cout << "state machine name: " << qPrintable(machine.name()) << std::endl; +#else + std::cout << "QtScxml not present" << std::endl; +#endif +} diff --git a/tests/auto/blackbox/testdata/qtscxml/qtscxml.qbs b/tests/auto/blackbox/testdata/qtscxml/qtscxml.qbs new file mode 100644 index 00000000..af96b859 --- /dev/null +++ b/tests/auto/blackbox/testdata/qtscxml/qtscxml.qbs @@ -0,0 +1,52 @@ +import qbs +import qbs.FileInfo + +Project { + QtApplication { + name: "app" + Depends { name: "Qt.scxml"; required: false } + + Properties { + condition: Qt.scxml.present + cpp.defines: ["HAS_QTSCXML"] + } + + Qt.scxml.className: "QbsStateMachine" + Qt.scxml.namespace: "QbsTest" + + files: ["main.cpp"] + Group { + files: ["dummystatemachine.scxml"] + fileTags: ["qt.scxml.compilable"] + } + } + + Product { + name: "runner" + type: ["runner"] + Depends { name: "app" } + Rule { + inputsFromDependencies: ["application"] + Artifact { + filePath: "dummy" + fileTags: ["runner"] + } + prepare: { + var cmd = new Command(input.filePath); + cmd.description = "running " + input.filePath; + var pathVar; + var pathValue; + if (product.moduleProperty("qbs", "hostOS").contains("windows")) { + pathVar = "PATH"; + pathValue = FileInfo.toWindowsSeparators( + input.moduleProperty("Qt.core", "binPath")); + } else { + pathVar = "LD_LIBRARY_PATH"; + pathValue = input.moduleProperty("Qt.core", "libPath"); + } + cmd.environment = [pathVar + '=' + pathValue]; + return [cmd]; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/rad-after-incomplete-build/dummy.txt b/tests/auto/blackbox/testdata/rad-after-incomplete-build/dummy.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/rad-after-incomplete-build/project_with_rule.qbs b/tests/auto/blackbox/testdata/rad-after-incomplete-build/project_with_rule.qbs new file mode 100644 index 00000000..42ec452d --- /dev/null +++ b/tests/auto/blackbox/testdata/rad-after-incomplete-build/project_with_rule.qbs @@ -0,0 +1,26 @@ +import qbs +import qbs.TextFile + +Product { + type: "custom" + Group { + files: "dummy.txt" + fileTags: "input" + } + Rule { + inputs: "input" + Artifact { + fileTags: "custom" + filePath: "oldfile" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "creating file"; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.close(); + } + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata/recursive_renaming/dir/subdir/blubb.txt b/tests/auto/blackbox/testdata/recursive_renaming/dir/subdir/blubb.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/recursive_renaming/dir/wasser.txt b/tests/auto/blackbox/testdata/recursive_renaming/dir/wasser.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/recursive_renaming/recursive_renaming.qbs b/tests/auto/blackbox/testdata/recursive_renaming/recursive_renaming.qbs new file mode 100644 index 00000000..5c2c4ec4 --- /dev/null +++ b/tests/auto/blackbox/testdata/recursive_renaming/recursive_renaming.qbs @@ -0,0 +1,9 @@ +import qbs 1.0 + +Product { + Group { + qbs.install: true + qbs.installSourceBase: "." + files: ["dir/**"] + } +} diff --git a/tests/auto/blackbox/testdata/recursive_wildcards/dir/file1.txt b/tests/auto/blackbox/testdata/recursive_wildcards/dir/file1.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/recursive_wildcards/dir/subdir/file2.txt b/tests/auto/blackbox/testdata/recursive_wildcards/dir/subdir/file2.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/recursive_wildcards/recursive_wildcards.qbs b/tests/auto/blackbox/testdata/recursive_wildcards/recursive_wildcards.qbs new file mode 100644 index 00000000..a1970e1c --- /dev/null +++ b/tests/auto/blackbox/testdata/recursive_wildcards/recursive_wildcards.qbs @@ -0,0 +1,7 @@ +Product { + Group { + files: "dir/**" + qbs.install: true + qbs.installDir: "dir" + } +} diff --git a/tests/auto/blackbox/testdata/referenceErrorInExport/main.c b/tests/auto/blackbox/testdata/referenceErrorInExport/main.c new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/referenceErrorInExport/main.c @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/referenceErrorInExport/project.qbs b/tests/auto/blackbox/testdata/referenceErrorInExport/project.qbs new file mode 100644 index 00000000..91069f62 --- /dev/null +++ b/tests/auto/blackbox/testdata/referenceErrorInExport/project.qbs @@ -0,0 +1,20 @@ +import qbs + +Project { + CppApplication { + Depends { name: "other" } + files: ["main.c"] + cpp.includePaths: ["."] + } + + DynamicLibrary { + name: "other" + files: ["main.c"] + + property stringList includePaths: [] + Export { + Depends { name: "cpp" } + cpp.includePaths: includePaths + } + } +} diff --git a/tests/auto/blackbox/testdata/renameDependency/after/lib2.cpp b/tests/auto/blackbox/testdata/renameDependency/after/lib2.cpp new file mode 100644 index 00000000..7ecd6a10 --- /dev/null +++ b/tests/auto/blackbox/testdata/renameDependency/after/lib2.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lib2.h" +#include + +void print_two_numbers(int a, int b, int c) +{ + std::cout << "a=" << a << ", b=" << b << ", c=" << c << std::endl; +} diff --git a/tests/auto/blackbox/testdata/renameDependency/after/lib2.h b/tests/auto/blackbox/testdata/renameDependency/after/lib2.h new file mode 100644 index 00000000..fdd6bb87 --- /dev/null +++ b/tests/auto/blackbox/testdata/renameDependency/after/lib2.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void print_two_numbers(int a, int b, int c); diff --git a/tests/auto/blackbox/testdata/renameDependency/before/lib.cpp b/tests/auto/blackbox/testdata/renameDependency/before/lib.cpp new file mode 100644 index 00000000..6958518a --- /dev/null +++ b/tests/auto/blackbox/testdata/renameDependency/before/lib.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "lib.h" +#include + +void print_two_numbers(int a, int b/*, int c*/) +{ + std::cout << "a=" << a << ", b=" << b /*<< ", c=" << c */ << std::endl; +} diff --git a/tests/auto/blackbox/testdata/renameDependency/before/lib.h b/tests/auto/blackbox/testdata/renameDependency/before/lib.h new file mode 100644 index 00000000..17a0b170 --- /dev/null +++ b/tests/auto/blackbox/testdata/renameDependency/before/lib.h @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void print_two_numbers(int a, int b/*, int c*/); diff --git a/tests/auto/blackbox/testdata/renameDependency/before/main.cpp b/tests/auto/blackbox/testdata/renameDependency/before/main.cpp new file mode 100644 index 00000000..7f1bbe16 --- /dev/null +++ b/tests/auto/blackbox/testdata/renameDependency/before/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "lib.h" + +int main() +{ + print_two_numbers(2, 3); + return 0; +} diff --git a/tests/auto/blackbox/testdata/renameDependency/before/renameDependency.qbs b/tests/auto/blackbox/testdata/renameDependency/before/renameDependency.qbs new file mode 100644 index 00000000..e6f3cf66 --- /dev/null +++ b/tests/auto/blackbox/testdata/renameDependency/before/renameDependency.qbs @@ -0,0 +1,5 @@ +import qbs + +CppApplication { + files: ["*.cpp", "*.h"] +} diff --git a/tests/auto/blackbox/testdata/reproducible-build/file1.cpp b/tests/auto/blackbox/testdata/reproducible-build/file1.cpp new file mode 100644 index 00000000..ce54c007 --- /dev/null +++ b/tests/auto/blackbox/testdata/reproducible-build/file1.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +static void f() { } + +void f1() { f(); } diff --git a/tests/auto/blackbox/testdata/reproducible-build/file2.cpp b/tests/auto/blackbox/testdata/reproducible-build/file2.cpp new file mode 100644 index 00000000..8c095814 --- /dev/null +++ b/tests/auto/blackbox/testdata/reproducible-build/file2.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +static void f() { } + +void f2() { f(); } diff --git a/tests/auto/blackbox/testdata/reproducible-build/main.cpp b/tests/auto/blackbox/testdata/reproducible-build/main.cpp new file mode 100644 index 00000000..6390016a --- /dev/null +++ b/tests/auto/blackbox/testdata/reproducible-build/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void f1(); +void f2(); + +int main() +{ + f1(); + f2(); +} diff --git a/tests/auto/blackbox/testdata/reproducible-build/reproducible-build.qbs b/tests/auto/blackbox/testdata/reproducible-build/reproducible-build.qbs new file mode 100644 index 00000000..ebf2ba63 --- /dev/null +++ b/tests/auto/blackbox/testdata/reproducible-build/reproducible-build.qbs @@ -0,0 +1,7 @@ +import qbs + +CppApplication { + name: "the product" + files: ["file1.cpp", "file2.cpp", "main.cpp"] + cpp.cxxFlags: ["-flto"] +} diff --git a/tests/auto/blackbox/testdata/response-files/cat-response-file.cpp b/tests/auto/blackbox/testdata/response-files/cat-response-file.cpp new file mode 100644 index 00000000..c9839681 --- /dev/null +++ b/tests/auto/blackbox/testdata/response-files/cat-response-file.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +using namespace std; + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + cerr << "cat-response-file: not enough arguments: " << argc - 1 << endl; + return 1; + } + if (strlen(argv[2]) < 2) { + cerr << "cat-response-file: second argument is too short: " << argv[2] << endl; + return 2; + } + if (argv[2][0] != '@') { + cerr << "cat-response-file: second argument does not start with @: " << argv[2] << endl; + return 3; + } + ifstream inf(argv[2] + 1); + if (!inf.is_open()) { + cerr << "cat-response-file: cannot open input file " << argv[2] + 1 << endl; + return 4; + } + ofstream ouf(argv[1]); + if (!ouf.is_open()) { + cerr << "cat-response-file: cannot open output file " << argv[1] << endl; + return 5; + } + string line; + while (getline(inf, line)) + ouf << line << endl; + inf.close(); + ouf.close(); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/response-files/response-files.qbs b/tests/auto/blackbox/testdata/response-files/response-files.qbs new file mode 100644 index 00000000..7913287e --- /dev/null +++ b/tests/auto/blackbox/testdata/response-files/response-files.qbs @@ -0,0 +1,37 @@ +import qbs +import qbs.FileInfo +import qbs.TextFile + +Project { + CppApplication { + name: "cat-response-file" + files: ["cat-response-file.cpp"] + cpp.enableExceptions: true + } + Product { + name: "response-file-text" + type: ["text"] + Depends { name: "cat-response-file" } + Group { + fileTagsFilter: ["text"] + qbs.install: true + } + Rule { + inputsFromDependencies: ["application"] + Artifact { + filePath: "response-file-content.txt" + fileTags: ["text"] + } + prepare: { + var filePath = inputs["application"][0].filePath; + var args = [output.filePath, "foo", "with space", "bar"]; + var cmd = new Command(filePath, args); + cmd.responseFileThreshold = 1; + cmd.responseFileArgumentIndex = 1; + cmd.responseFileUsagePrefix = '@'; + cmd.silent = true; + return cmd; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/rule-with-no-inputs/rule-with-no-inputs.qbs b/tests/auto/blackbox/testdata/rule-with-no-inputs/rule-with-no-inputs.qbs new file mode 100644 index 00000000..3792f977 --- /dev/null +++ b/tests/auto/blackbox/testdata/rule-with-no-inputs/rule-with-no-inputs.qbs @@ -0,0 +1,26 @@ +import qbs +import qbs.TextFile + +Product { + name: "theProduct" + type: ["output"] + version: "1" + Rule { + inputs: [] + multiplex: true + Artifact { + filePath: "output.out" + fileTags: ["output"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "creating output"; + cmd.sourceCode = function() { + console.info(product.version); + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.close(); + } + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/ruleConditions/foo.narf b/tests/auto/blackbox/testdata/ruleConditions/foo.narf new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/ruleConditions/main.cpp b/tests/auto/blackbox/testdata/ruleConditions/main.cpp new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/ruleConditions/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/ruleConditions/modules/narfzort/narfzort.qbs b/tests/auto/blackbox/testdata/ruleConditions/modules/narfzort/narfzort.qbs new file mode 100644 index 00000000..8e798957 --- /dev/null +++ b/tests/auto/blackbox/testdata/ruleConditions/modules/narfzort/narfzort.qbs @@ -0,0 +1,30 @@ +import qbs 1.0 +import qbs.FileInfo +import qbs.TextFile + +Module { + property bool buildZort: true + FileTagger { + patterns: "*.narf" + fileTags: ["narf"] + } + Rule { + condition: product.moduleProperty("narfzort", "buildZort"); + inputs: ["narf"] + outputFileTags: ["zort"] + outputArtifacts: [{ + filePath: product.name + "." + input.fileName + ".zort", + fileTags: ["zort"] + }] + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating " + FileInfo.fileName(output.filePath); + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.write("NARF! ZORT!"); + f.close(); + } + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata/ruleConditions/ruleConditions.qbs b/tests/auto/blackbox/testdata/ruleConditions/ruleConditions.qbs new file mode 100644 index 00000000..766bf3c5 --- /dev/null +++ b/tests/auto/blackbox/testdata/ruleConditions/ruleConditions.qbs @@ -0,0 +1,12 @@ +import qbs 1.0 +import "templates/zorduct.qbs" as Zorduct + +Project { + Zorduct { + narfzort.buildZort: false + name: "unzorted" + } + Zorduct { + name: "zorted" + } +} diff --git a/tests/auto/blackbox/testdata/ruleConditions/templates/zorduct.qbs b/tests/auto/blackbox/testdata/ruleConditions/templates/zorduct.qbs new file mode 100644 index 00000000..8f19a5a6 --- /dev/null +++ b/tests/auto/blackbox/testdata/ruleConditions/templates/zorduct.qbs @@ -0,0 +1,12 @@ +import qbs 1.0 + +Product { + type: ["application", "zort"] + consoleApplication: true + Depends { name: "cpp" } + Depends { name: "narfzort" } + files: [ + "main.cpp", + "foo.narf" + ] +} diff --git a/tests/auto/blackbox/testdata/ruleCycle/happy.grass b/tests/auto/blackbox/testdata/ruleCycle/happy.grass new file mode 100644 index 00000000..8cf7f02e --- /dev/null +++ b/tests/auto/blackbox/testdata/ruleCycle/happy.grass @@ -0,0 +1 @@ +happy! happy! joy! joy! diff --git a/tests/auto/blackbox/testdata/ruleCycle/ruleCycle.qbs b/tests/auto/blackbox/testdata/ruleCycle/ruleCycle.qbs new file mode 100644 index 00000000..6898afea --- /dev/null +++ b/tests/auto/blackbox/testdata/ruleCycle/ruleCycle.qbs @@ -0,0 +1,47 @@ +import qbs 1.0 + +Project { + Product { + name: "the cycle of life" + type: "cow" + Group { + files: ["happy.grass"] + fileTags: ["grass"] + } + Rule { + inputs: ["grass"] + outputFileTags: ["cow"] + outputArtifacts: [{ + filePath: input.completeBaseName + ".cow", + fileTags: ["cow"] + }] + prepare: { console.info("The cow feeds on grass."); } + } + Rule { + inputs: ["cow"] + Artifact { + filePath: input.completeBaseName + ".cow_pat" + fileTags: ["cow_pat"] + } + prepare: { console.info("The cow pat falls out of the cow."); } + } + Rule { + inputs: ["cow_pat"] + Artifact { + filePath: input.completeBaseName + ".fertilizer" + fileTags: ["fertilizer"] + } + prepare: { console.info("The cow pat is used as fertilizer."); } + } + Rule { + inputs: ["fertilizer"] + outputFileTags: ["grass"] + outputArtifacts: [{ + filePath: input.completeBaseName + ".grass", + fileTags: ["grass"] + }] + prepare: { console.info("The fertilizer lets the grass grow."); } + } + } +} + diff --git a/tests/auto/blackbox/testdata/separate-debug-info/foo.cpp b/tests/auto/blackbox/testdata/separate-debug-info/foo.cpp new file mode 100644 index 00000000..daa1fd70 --- /dev/null +++ b/tests/auto/blackbox/testdata/separate-debug-info/foo.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#if defined(_WIN32) || defined(WIN32) +# define EXPORT __declspec(dllexport) +#else +# define EXPORT +#endif + +EXPORT int getAnswer() { return 42; } diff --git a/tests/auto/blackbox/testdata/separate-debug-info/main.cpp b/tests/auto/blackbox/testdata/separate-debug-info/main.cpp new file mode 100644 index 00000000..210c8274 --- /dev/null +++ b/tests/auto/blackbox/testdata/separate-debug-info/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { return 0; } diff --git a/tests/auto/blackbox/testdata/separate-debug-info/project.qbs b/tests/auto/blackbox/testdata/separate-debug-info/project.qbs new file mode 100644 index 00000000..c0498df3 --- /dev/null +++ b/tests/auto/blackbox/testdata/separate-debug-info/project.qbs @@ -0,0 +1,115 @@ +import qbs + +Project { + CppApplication { + name: "app1" + type: ["application"] + files: ["main.cpp"] + cpp.separateDebugInformation: true + } + DynamicLibrary { + Depends { name: "cpp" } + name: "foo1" + type: ["dynamiclibrary"] + files: ["foo.cpp"] + cpp.separateDebugInformation: true + } + LoadableModule { + Depends { name: "cpp" } + name: "bar1" + files: ["foo.cpp"] + cpp.separateDebugInformation: true + } + + CppApplication { + name: "app2" + type: ["application"] + files: ["main.cpp"] + cpp.separateDebugInformation: false + } + DynamicLibrary { + Depends { name: "cpp" } + name: "foo2" + type: ["dynamiclibrary"] + files: ["foo.cpp"] + cpp.separateDebugInformation: false + } + LoadableModule { + Depends { name: "cpp" } + name: "bar2" + files: ["foo.cpp"] + cpp.separateDebugInformation: false + } + + CppApplication { + name: "app3" + type: ["application"] + files: ["main.cpp"] + cpp.separateDebugInformation: true + cpp.dsymutilFlags: ["--flat"] + } + DynamicLibrary { + Depends { name: "cpp" } + name: "foo3" + type: ["dynamiclibrary"] + files: ["foo.cpp"] + cpp.separateDebugInformation: true + cpp.dsymutilFlags: ["--flat"] + } + LoadableModule { + Depends { name: "cpp" } + name: "bar3" + files: ["foo.cpp"] + cpp.separateDebugInformation: true + cpp.dsymutilFlags: ["--flat"] + } + + CppApplication { + name: "app4" + type: ["application"] + files: ["main.cpp"] + bundle.isBundle: false + cpp.separateDebugInformation: true + } + DynamicLibrary { + Depends { name: "cpp" } + name: "foo4" + type: ["dynamiclibrary"] + files: ["foo.cpp"] + bundle.isBundle: false + cpp.separateDebugInformation: true + } + LoadableModule { + Depends { name: "cpp" } + name: "bar4" + files: ["foo.cpp"] + bundle.isBundle: false + cpp.separateDebugInformation: true + } + + CppApplication { + name: "app5" + type: ["application"] + files: ["main.cpp"] + bundle.isBundle: false + cpp.separateDebugInformation: true + cpp.dsymutilFlags: ["--flat"] + } + DynamicLibrary { + Depends { name: "cpp" } + name: "foo5" + type: ["dynamiclibrary"] + files: ["foo.cpp"] + bundle.isBundle: false + cpp.separateDebugInformation: true + cpp.dsymutilFlags: ["--flat"] + } + LoadableModule { + Depends { name: "cpp" } + name: "bar5" + files: ["foo.cpp"] + bundle.isBundle: false + cpp.separateDebugInformation: true + cpp.dsymutilFlags: ["--flat"] + } +} diff --git a/tests/auto/blackbox/testdata/soversion/lib.cpp b/tests/auto/blackbox/testdata/soversion/lib.cpp new file mode 100644 index 00000000..8101b05d --- /dev/null +++ b/tests/auto/blackbox/testdata/soversion/lib.cpp @@ -0,0 +1 @@ +void f() { } diff --git a/tests/auto/blackbox/testdata/soversion/soversion.qbs b/tests/auto/blackbox/testdata/soversion/soversion.qbs new file mode 100644 index 00000000..d024c25b --- /dev/null +++ b/tests/auto/blackbox/testdata/soversion/soversion.qbs @@ -0,0 +1,9 @@ +import qbs + +DynamicLibrary { + name: "mylib" + property bool useVersion + version: useVersion ? "1.2.3" : undefined + Depends { name: "cpp" } + files: ["lib.cpp"] +} diff --git a/tests/auto/blackbox/testdata/subprofile-change-tracking/main1.cpp b/tests/auto/blackbox/testdata/subprofile-change-tracking/main1.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/subprofile-change-tracking/main1.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/subprofile-change-tracking/main2.cpp b/tests/auto/blackbox/testdata/subprofile-change-tracking/main2.cpp new file mode 100644 index 00000000..612a484a --- /dev/null +++ b/tests/auto/blackbox/testdata/subprofile-change-tracking/main2.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() {} diff --git a/tests/auto/blackbox/testdata/subprofile-change-tracking/subprofile-change-tracking.qbs b/tests/auto/blackbox/testdata/subprofile-change-tracking/subprofile-change-tracking.qbs new file mode 100644 index 00000000..4805c527 --- /dev/null +++ b/tests/auto/blackbox/testdata/subprofile-change-tracking/subprofile-change-tracking.qbs @@ -0,0 +1,9 @@ +import qbs + +Project { + CppApplication { files: ["main1.cpp"] } + CppApplication { + profiles: ["qbs-autotests-subprofile"] + files: ["main2.cpp"] + } +} diff --git a/tests/auto/blackbox/testdata/successive-changes/input.in b/tests/auto/blackbox/testdata/successive-changes/input.in new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs b/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs new file mode 100644 index 00000000..f42ca409 --- /dev/null +++ b/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs @@ -0,0 +1,30 @@ +import qbs +import qbs.TextFile + +Project { + property string version: "1" + Product { + name: "theProduct" + type: ["output"] + Group { + files: ["input.in"] + fileTags: ["input"] + } + Rule { + inputs: ["input"] + Artifact { + filePath: "output.out" + fileTags: ["output"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating output"; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.write(project.version); + } + return [cmd]; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/suspicious-calls/copy-command.qbs b/tests/auto/blackbox/testdata/suspicious-calls/copy-command.qbs new file mode 100644 index 00000000..e01435fc --- /dev/null +++ b/tests/auto/blackbox/testdata/suspicious-calls/copy-command.qbs @@ -0,0 +1,23 @@ +import qbs +import qbs.File + +Product { + type: ["out"] + Group { + files: ["test.txt"] + fileTags: ["in"] + } + Rule { + inputs: ["in"] + Artifact { + filePath: "dummy.txt" + fileTags: ["out"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/suspicious-calls/copy-eval.qbs b/tests/auto/blackbox/testdata/suspicious-calls/copy-eval.qbs new file mode 100644 index 00000000..93bfca81 --- /dev/null +++ b/tests/auto/blackbox/testdata/suspicious-calls/copy-eval.qbs @@ -0,0 +1,10 @@ +import qbs +import qbs.File + +Product { + name: { + File.copy(sourceDirectory + "/copy-eval.qbs", + sourceDirectory + "/copy-eval2.qbs"); + return "blubb" + } +} diff --git a/tests/auto/blackbox/testdata/suspicious-calls/copy-prepare.qbs b/tests/auto/blackbox/testdata/suspicious-calls/copy-prepare.qbs new file mode 100644 index 00000000..b6098e8d --- /dev/null +++ b/tests/auto/blackbox/testdata/suspicious-calls/copy-prepare.qbs @@ -0,0 +1,24 @@ +import qbs +import qbs.File + +Product { + type: ["out"] + Group { + files: ["test.txt"] + fileTags: ["in"] + } + Rule { + inputs: ["in"] + Artifact { + filePath: "dummy.txt" + fileTags: ["out"] + } + prepare: { + File.copy(input.filePath, output.filePath); + var cmd = new JavaScriptCommand(); + cmd.description = "no-op"; + cmd.sourceCode = function() { }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/suspicious-calls/copy-probe.qbs b/tests/auto/blackbox/testdata/suspicious-calls/copy-probe.qbs new file mode 100644 index 00000000..dce27b19 --- /dev/null +++ b/tests/auto/blackbox/testdata/suspicious-calls/copy-probe.qbs @@ -0,0 +1,13 @@ +import qbs +import qbs.File + +Product { + Probe { + property string baseDir: project.sourceDirectory + + configure: { + File.copy(baseDir + "/copy-probe.qbs", + baseDir + "/copy-probe2.qbs"); + } + } +} diff --git a/tests/auto/blackbox/testdata/suspicious-calls/direntries-command.qbs b/tests/auto/blackbox/testdata/suspicious-calls/direntries-command.qbs new file mode 100644 index 00000000..d15351c2 --- /dev/null +++ b/tests/auto/blackbox/testdata/suspicious-calls/direntries-command.qbs @@ -0,0 +1,25 @@ +import qbs +import qbs.File + +Product { + type: ["out"] + Group { + files: ["test.txt"] + fileTags: ["in"] + } + Rule { + inputs: ["in"] + Artifact { + filePath: "dummy.txt" + fileTags: ["out"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + var dummy = File.directoryEntries(product.sourceDirectory, File.Files); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/suspicious-calls/direntries-eval.qbs b/tests/auto/blackbox/testdata/suspicious-calls/direntries-eval.qbs new file mode 100644 index 00000000..4cd0d634 --- /dev/null +++ b/tests/auto/blackbox/testdata/suspicious-calls/direntries-eval.qbs @@ -0,0 +1,6 @@ +import qbs +import qbs.File + +Product { + name: File.directoryEntries(sourceDirectory, File.Files)[0] +} diff --git a/tests/auto/blackbox/testdata/suspicious-calls/direntries-prepare.qbs b/tests/auto/blackbox/testdata/suspicious-calls/direntries-prepare.qbs new file mode 100644 index 00000000..6f732011 --- /dev/null +++ b/tests/auto/blackbox/testdata/suspicious-calls/direntries-prepare.qbs @@ -0,0 +1,24 @@ +import qbs +import qbs.File + +Product { + type: ["out"] + Group { + files: ["test.txt"] + fileTags: ["in"] + } + Rule { + inputs: ["in"] + Artifact { + filePath: "dummy.txt" + fileTags: ["out"] + } + prepare: { + var dummy = File.directoryEntries(product.sourceDirectory, File.Files); + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/suspicious-calls/direntries-probe.qbs b/tests/auto/blackbox/testdata/suspicious-calls/direntries-probe.qbs new file mode 100644 index 00000000..dc91adc8 --- /dev/null +++ b/tests/auto/blackbox/testdata/suspicious-calls/direntries-probe.qbs @@ -0,0 +1,14 @@ +import qbs +import qbs.File + +Product { + Probe { + property string baseDir: project.sourceDirectory + property stringList subDirs + + configure: { + subDirs = File.directoryEntries(baseDir, File.AllDirs); + found = true; + } + } +} diff --git a/tests/auto/blackbox/testdata/suspicious-calls/test.txt b/tests/auto/blackbox/testdata/suspicious-calls/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/symlink-removal/symlink-removal.qbs b/tests/auto/blackbox/testdata/symlink-removal/symlink-removal.qbs new file mode 100644 index 00000000..fc47c450 --- /dev/null +++ b/tests/auto/blackbox/testdata/symlink-removal/symlink-removal.qbs @@ -0,0 +1,21 @@ +import qbs +import qbs.File + +Product { + type: "removal" + Rule { + multiplex: true + Artifact { + filePath: "dummy" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + File.remove(product.sourceDirectory + "/dir1"); + }; + return [cmd]; + } + } +} diff --git a/tests/auto/blackbox/testdata/system-run-paths/lib.cpp b/tests/auto/blackbox/testdata/system-run-paths/lib.cpp new file mode 100644 index 00000000..bd3fa911 --- /dev/null +++ b/tests/auto/blackbox/testdata/system-run-paths/lib.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void func() { } diff --git a/tests/auto/blackbox/testdata/system-run-paths/main.cpp b/tests/auto/blackbox/testdata/system-run-paths/main.cpp new file mode 100644 index 00000000..183c82f6 --- /dev/null +++ b/tests/auto/blackbox/testdata/system-run-paths/main.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void func(); + +int main() +{ + func(); +} diff --git a/tests/auto/blackbox/testdata/system-run-paths/system-run-paths.qbs b/tests/auto/blackbox/testdata/system-run-paths/system-run-paths.qbs new file mode 100644 index 00000000..81778d6f --- /dev/null +++ b/tests/auto/blackbox/testdata/system-run-paths/system-run-paths.qbs @@ -0,0 +1,24 @@ +import qbs + +Project { + property bool setRunPaths + Product { + name: "theLib" + type: ["dynamiclibrary"] + Depends { name: "cpp" } + Group { + fileTagsFilter: product.type + qbs.install: true + qbs.installDir: "lib" + } + files: ["lib.cpp"] + } + + CppApplication { + name: "app" + Depends { name: "theLib" } + files: ["main.cpp"] + cpp.rpaths: qbs.installRoot + "/lib" + cpp.systemRunPaths: project.setRunPaths ? [qbs.installRoot + "/lib"] : [] + } +} diff --git a/tests/auto/blackbox/testdata/toplevel-searchpath/qbs-resources/imports/MyProduct.qbs b/tests/auto/blackbox/testdata/toplevel-searchpath/qbs-resources/imports/MyProduct.qbs new file mode 100644 index 00000000..6d16a3c5 --- /dev/null +++ b/tests/auto/blackbox/testdata/toplevel-searchpath/qbs-resources/imports/MyProduct.qbs @@ -0,0 +1,3 @@ +import qbs + +Product { } diff --git a/tests/auto/blackbox/testdata/toplevel-searchpath/toplevel-searchpath.qbs b/tests/auto/blackbox/testdata/toplevel-searchpath/toplevel-searchpath.qbs new file mode 100644 index 00000000..2dd54a81 --- /dev/null +++ b/tests/auto/blackbox/testdata/toplevel-searchpath/toplevel-searchpath.qbs @@ -0,0 +1,3 @@ +import qbs + +MyProduct { } diff --git a/tests/auto/blackbox/testdata/trackAddFile/after/main.cpp b/tests/auto/blackbox/testdata/trackAddFile/after/main.cpp new file mode 100644 index 00000000..0e474b22 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddFile/after/main.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "narf.h" +#include "zort.h" +#include + +int main(int argc, char **argv) +{ + printf("Hello World!\n"); + Narf narf; + narf.shout(); + Zort zort; + zort.shout(); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/trackAddFile/after/project.qbs b/tests/auto/blackbox/testdata/trackAddFile/after/project.qbs new file mode 100644 index 00000000..469aff09 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddFile/after/project.qbs @@ -0,0 +1,16 @@ +import qbs 1.0 + +Project { + Product { + name: 'someapp' + type: 'application' + consoleApplication: true + Depends { name: 'cpp' } + files: [ + "main.cpp", + "narf.h", "narf.cpp", + "zort.h", "zort.cpp" + ] + } +} + diff --git a/tests/auto/blackbox/testdata/trackAddFile/after/zort.cpp b/tests/auto/blackbox/testdata/trackAddFile/after/zort.cpp new file mode 100644 index 00000000..1915ea86 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddFile/after/zort.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "zort.h" +#include + +void Zort::shout() +{ + printf("ZORT!\n"); +} + diff --git a/tests/auto/blackbox/testdata/trackAddFile/after/zort.h b/tests/auto/blackbox/testdata/trackAddFile/after/zort.h new file mode 100644 index 00000000..9f7acbf2 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddFile/after/zort.h @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef ZORT_H +#define ZORT_H + +class Zort +{ +public: + void shout(); +}; + +#endif + diff --git a/tests/auto/blackbox/testdata/trackAddFile/before/main.cpp b/tests/auto/blackbox/testdata/trackAddFile/before/main.cpp new file mode 100644 index 00000000..f989e1d9 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddFile/before/main.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "narf.h" +#include + +int main(int argc, char **argv) +{ + printf("Hello World!\n"); + Narf narf; + narf.shout(); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/trackAddFile/before/narf.cpp b/tests/auto/blackbox/testdata/trackAddFile/before/narf.cpp new file mode 100644 index 00000000..280d5f3b --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddFile/before/narf.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "narf.h" +#include + +void Narf::shout() +{ + printf("NARF!\n"); +} + diff --git a/tests/auto/blackbox/testdata/trackAddFile/before/narf.h b/tests/auto/blackbox/testdata/trackAddFile/before/narf.h new file mode 100644 index 00000000..bd50f6e3 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddFile/before/narf.h @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef NARF_H +#define NARF_H + +class Narf +{ +public: + void shout(); +}; + +#endif + diff --git a/tests/auto/blackbox/testdata/trackAddFile/before/project.qbs b/tests/auto/blackbox/testdata/trackAddFile/before/project.qbs new file mode 100644 index 00000000..fc647cdb --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddFile/before/project.qbs @@ -0,0 +1,12 @@ +import qbs 1.0 + +Project { + Product { + name: 'someapp' + type: 'application' + consoleApplication: true + Depends { name: 'cpp' } + files: [ "main.cpp", "narf.h", "narf.cpp" ] + } +} + diff --git a/tests/auto/blackbox/testdata/trackAddMocInclude/after/main.cpp b/tests/auto/blackbox/testdata/trackAddMocInclude/after/main.cpp new file mode 100644 index 00000000..566024ac --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddMocInclude/after/main.cpp @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class MyObject : public QObject +{ + Q_OBJECT +public: + MyObject(QObject *parent = 0) + : QObject(parent) + { + } +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + MyObject *obj = new MyObject(&app); + return app.exec(); +} + +#include + diff --git a/tests/auto/blackbox/testdata/trackAddMocInclude/before/main.cpp b/tests/auto/blackbox/testdata/trackAddMocInclude/before/main.cpp new file mode 100644 index 00000000..2a44ceec --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddMocInclude/before/main.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class MyObject : public QObject +{ + Q_OBJECT +public: + MyObject(QObject *parent = 0) + : QObject(parent) + { + } +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + MyObject *obj = new MyObject(&app); + return app.exec(); +} + diff --git a/tests/auto/blackbox/testdata/trackAddMocInclude/before/test.qbs b/tests/auto/blackbox/testdata/trackAddMocInclude/before/test.qbs new file mode 100644 index 00000000..1e1d7968 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackAddMocInclude/before/test.qbs @@ -0,0 +1,7 @@ +import qbs 1.0 + +Application { + Depends { name: "Qt.core" } + files: ["main.cpp"] +} + diff --git a/tests/auto/blackbox/testdata/trackExternalProductChanges/environmentChange.cpp b/tests/auto/blackbox/testdata/trackExternalProductChanges/environmentChange.cpp new file mode 100644 index 00000000..9ea74035 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackExternalProductChanges/environmentChange.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void environmentChange() { } diff --git a/tests/auto/blackbox/testdata/trackExternalProductChanges/fileList.js b/tests/auto/blackbox/testdata/trackExternalProductChanges/fileList.js new file mode 100644 index 00000000..301e4d27 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackExternalProductChanges/fileList.js @@ -0,0 +1,6 @@ +var File = loadExtension("qbs.File"); + +function fileList() { return []; } + +function filesFromFs(path) { return File.exists(path + "/fileExists.cpp") ? ["fileExists.cpp"] : []; } + diff --git a/tests/auto/blackbox/testdata/trackExternalProductChanges/hidden/hiddenheaderqbs.h b/tests/auto/blackbox/testdata/trackExternalProductChanges/hidden/hiddenheaderqbs.h new file mode 100644 index 00000000..50841a22 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackExternalProductChanges/hidden/hiddenheaderqbs.h @@ -0,0 +1,27 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ diff --git a/tests/auto/blackbox/testdata/trackExternalProductChanges/including.cpp b/tests/auto/blackbox/testdata/trackExternalProductChanges/including.cpp new file mode 100644 index 00000000..7feac10d --- /dev/null +++ b/tests/auto/blackbox/testdata/trackExternalProductChanges/including.cpp @@ -0,0 +1,31 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +void f() {} diff --git a/tests/auto/blackbox/testdata/trackExternalProductChanges/jsFileChange.cpp b/tests/auto/blackbox/testdata/trackExternalProductChanges/jsFileChange.cpp new file mode 100644 index 00000000..93fbe3ac --- /dev/null +++ b/tests/auto/blackbox/testdata/trackExternalProductChanges/jsFileChange.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void jsFileChange() { } diff --git a/tests/auto/blackbox/testdata/trackExternalProductChanges/main.cpp b/tests/auto/blackbox/testdata/trackExternalProductChanges/main.cpp new file mode 100644 index 00000000..e14f806b --- /dev/null +++ b/tests/auto/blackbox/testdata/trackExternalProductChanges/main.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main() { } diff --git a/tests/auto/blackbox/testdata/trackExternalProductChanges/project.qbs b/tests/auto/blackbox/testdata/trackExternalProductChanges/project.qbs new file mode 100644 index 00000000..232c8ea4 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackExternalProductChanges/project.qbs @@ -0,0 +1,15 @@ +import qbs +import qbs.Environment +import "fileList.js" as FileList + +CppApplication { + property stringList filesFromEnv: Environment.getEnv("QBS_TEST_PULL_IN_FILE_VIA_ENV") + ? ["environmentChange.cpp"] : [] + files: ["main.cpp"].concat(FileList.fileList()).concat(filesFromEnv).concat(FileList.filesFromFs(path)) + + Group { + condition: Environment.getEnv("INCLUDE_PATH_TEST") + name: "file that needs help from the environment to find a header" + files: "including.cpp" + } +} diff --git a/tests/auto/blackbox/testdata/trackFileTags/after/main.cpp b/tests/auto/blackbox/testdata/trackFileTags/after/main.cpp new file mode 100644 index 00000000..1d2c8ebb --- /dev/null +++ b/tests/auto/blackbox/testdata/trackFileTags/after/main.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +int foo(); + +int main(int argc, char **argv) +{ + printf("there's %d foo here\n", foo()); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/trackFileTags/after/project.qbs b/tests/auto/blackbox/testdata/trackFileTags/after/project.qbs new file mode 100644 index 00000000..c0e5c471 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackFileTags/after/project.qbs @@ -0,0 +1,53 @@ +import qbs 1.0 +import qbs.TextFile + +Project { + Product { + name: 'someapp' + type: 'application' + consoleApplication: true + Depends { name: 'cpp' } + Group { + files: [ "main.cpp" ] + fileTags: [ "foosource", "cpp" ] + } + } + + Rule { + inputs: ["foosource"] + Artifact { + filePath: input.baseName + ".foo" + fileTags: ["foo"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.sourceCode = "var file = new TextFile(output.filePath, TextFile.WriteOnly);"; + cmd.sourceCode += "file.truncate();" + cmd.sourceCode += "file.write(\"There's nothing to see here!\");" + cmd.sourceCode += "file.close();" + cmd.description = "generating something"; + return cmd; + } + } + + Rule { + inputs: ["foo"] + Artifact { + filePath: input.baseName + "_foo.cpp" + fileTags: ["cpp"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.sourceCode = "var file = new TextFile(output.filePath, TextFile.WriteOnly);"; + cmd.sourceCode += "file.truncate();"; + cmd.sourceCode += "file.write(\"// There's nothing to see here!\\n\");"; + cmd.sourceCode += "file.write(\"int foo() { return 15; }\\n\");"; + cmd.sourceCode += "file.close();"; + cmd.description = "generating something"; + return cmd; + } + } +} + diff --git a/tests/auto/blackbox/testdata/trackFileTags/before/main.cpp b/tests/auto/blackbox/testdata/trackFileTags/before/main.cpp new file mode 100644 index 00000000..3016f8bb --- /dev/null +++ b/tests/auto/blackbox/testdata/trackFileTags/before/main.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +int main(int argc, char **argv) +{ + printf("there's no foo here\n"); + return 0; +} + diff --git a/tests/auto/blackbox/testdata/trackFileTags/before/project.qbs b/tests/auto/blackbox/testdata/trackFileTags/before/project.qbs new file mode 100644 index 00000000..7ac895c5 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackFileTags/before/project.qbs @@ -0,0 +1,53 @@ +import qbs 1.0 +import qbs.TextFile + +Project { + Product { + name: 'someapp' + type: 'application' + consoleApplication: true + Depends { name: 'cpp' } + Group { + files: [ "main.cpp" ] + fileTags: [ "cpp" ] + } + } + + Rule { + inputs: ["foosource"] + Artifact { + filePath: input.baseName + ".foo" + fileTags: ["foo"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.sourceCode = "var file = new TextFile(output.filePath, TextFile.WriteOnly);"; + cmd.sourceCode += "file.truncate();" + cmd.sourceCode += "file.write(\"There's nothing to see here!\");" + cmd.sourceCode += "file.close();" + cmd.description = "generating something"; + return cmd; + } + } + + Rule { + inputs: ["foo"] + Artifact { + filePath: input.baseName + "_foo.cpp" + fileTags: ["cpp"] + } + + prepare: { + var cmd = new JavaScriptCommand(); + cmd.sourceCode = "var file = new TextFile(output.filePath, TextFile.WriteOnly);"; + cmd.sourceCode += "file.truncate();"; + cmd.sourceCode += "file.write(\"// There's nothing to see here!\\n\");"; + cmd.sourceCode += "file.write(\"int foo() { return 15; }\\n\");"; + cmd.sourceCode += "file.close();"; + cmd.description = "generating something"; + return cmd; + } + } +} + diff --git a/tests/auto/blackbox/testdata/trackProducts/after/product3.qbs b/tests/auto/blackbox/testdata/trackProducts/after/product3.qbs new file mode 100644 index 00000000..4dac755c --- /dev/null +++ b/tests/auto/blackbox/testdata/trackProducts/after/product3.qbs @@ -0,0 +1,8 @@ +import qbs 1.0 + +Product { + Depends { name: "cpp" } + type: "application" + consoleApplication: true + files: ["zoo.cpp"] +} diff --git a/tests/auto/blackbox/testdata/trackProducts/after/trackProducts.qbs b/tests/auto/blackbox/testdata/trackProducts/after/trackProducts.qbs new file mode 100644 index 00000000..343405c0 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackProducts/after/trackProducts.qbs @@ -0,0 +1,7 @@ +import qbs 1.0 + +Project +{ + name: "trackProducts" + references: ["product1.qbs", "product2.qbs", "product3.qbs"] +} diff --git a/tests/auto/blackbox/testdata/trackProducts/after/zoo.cpp b/tests/auto/blackbox/testdata/trackProducts/after/zoo.cpp new file mode 100644 index 00000000..b65fb3f9 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackProducts/after/zoo.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + printf("zoo\n"); +} diff --git a/tests/auto/blackbox/testdata/trackProducts/before/bar.cpp b/tests/auto/blackbox/testdata/trackProducts/before/bar.cpp new file mode 100644 index 00000000..7880c866 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackProducts/before/bar.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + printf("bar\n"); +} diff --git a/tests/auto/blackbox/testdata/trackProducts/before/foo.cpp b/tests/auto/blackbox/testdata/trackProducts/before/foo.cpp new file mode 100644 index 00000000..865fb129 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackProducts/before/foo.cpp @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + printf("foo\n"); +} diff --git a/tests/auto/blackbox/testdata/trackProducts/before/product1.qbs b/tests/auto/blackbox/testdata/trackProducts/before/product1.qbs new file mode 100644 index 00000000..60e8b092 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackProducts/before/product1.qbs @@ -0,0 +1,8 @@ +import qbs 1.0 + +Product { + Depends { name: "cpp" } + type: "application" + consoleApplication: true + files: ["foo.cpp"] +} diff --git a/tests/auto/blackbox/testdata/trackProducts/before/product2.qbs b/tests/auto/blackbox/testdata/trackProducts/before/product2.qbs new file mode 100644 index 00000000..5490b884 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackProducts/before/product2.qbs @@ -0,0 +1,8 @@ +import qbs 1.0 + +Product { + Depends { name: "cpp" } + type: "application" + consoleApplication: true + files: ["bar.cpp"] +} diff --git a/tests/auto/blackbox/testdata/trackProducts/before/trackProducts.qbs b/tests/auto/blackbox/testdata/trackProducts/before/trackProducts.qbs new file mode 100644 index 00000000..6d5555c6 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackProducts/before/trackProducts.qbs @@ -0,0 +1,7 @@ +import qbs 1.0 + +Project +{ + name: "trackProducts" + references: ["product1.qbs", "product2.qbs"] +} diff --git a/tests/auto/blackbox/testdata/trackQObjChange/bla.cpp b/tests/auto/blackbox/testdata/trackQObjChange/bla.cpp new file mode 100644 index 00000000..74c8bdac --- /dev/null +++ b/tests/auto/blackbox/testdata/trackQObjChange/bla.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "bla.h" + +int main() +{ + MyObject obj; + obj.setObjectName("I am the object!"); +} diff --git a/tests/auto/blackbox/testdata/trackQObjChange/bla_noqobject.h b/tests/auto/blackbox/testdata/trackQObjChange/bla_noqobject.h new file mode 100644 index 00000000..ca45b65b --- /dev/null +++ b/tests/auto/blackbox/testdata/trackQObjChange/bla_noqobject.h @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class MyObject : public QObject +{ +}; + diff --git a/tests/auto/blackbox/testdata/trackQObjChange/bla_qobject.h b/tests/auto/blackbox/testdata/trackQObjChange/bla_qobject.h new file mode 100644 index 00000000..840465e8 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackQObjChange/bla_qobject.h @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +class MyObject : public QObject +{ + Q_OBJECT +}; + diff --git a/tests/auto/blackbox/testdata/trackQObjChange/i.qbs b/tests/auto/blackbox/testdata/trackQObjChange/i.qbs new file mode 100644 index 00000000..c18fab08 --- /dev/null +++ b/tests/auto/blackbox/testdata/trackQObjChange/i.qbs @@ -0,0 +1,19 @@ +import qbs 1.0 + +Project { + Product { + type: "application" + consoleApplication: true + name: "i" + + Depends { + name: "Qt.core" + } + + files: [ + "bla.cpp", + "bla.h" + ] + } +} + diff --git a/tests/auto/blackbox/testdata/transitive-optional-dependencies/modules/a/a.qbs b/tests/auto/blackbox/testdata/transitive-optional-dependencies/modules/a/a.qbs new file mode 100644 index 00000000..adec14e4 --- /dev/null +++ b/tests/auto/blackbox/testdata/transitive-optional-dependencies/modules/a/a.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + Depends { name: "b"; required: false } +} diff --git a/tests/auto/blackbox/testdata/transitive-optional-dependencies/modules/b/b.qbs b/tests/auto/blackbox/testdata/transitive-optional-dependencies/modules/b/b.qbs new file mode 100644 index 00000000..fb38b600 --- /dev/null +++ b/tests/auto/blackbox/testdata/transitive-optional-dependencies/modules/b/b.qbs @@ -0,0 +1,5 @@ +import qbs + +Module { + condition: false +} diff --git a/tests/auto/blackbox/testdata/transitive-optional-dependencies/transitive-optional-dependencies.qbs b/tests/auto/blackbox/testdata/transitive-optional-dependencies/transitive-optional-dependencies.qbs new file mode 100644 index 00000000..08860b05 --- /dev/null +++ b/tests/auto/blackbox/testdata/transitive-optional-dependencies/transitive-optional-dependencies.qbs @@ -0,0 +1,5 @@ +import qbs + +Product { + Depends { name: "a" } +} diff --git a/tests/auto/blackbox/testdata/typescript/animals.ts b/tests/auto/blackbox/testdata/typescript/animals.ts new file mode 100644 index 00000000..a33ae5c1 --- /dev/null +++ b/tests/auto/blackbox/testdata/typescript/animals.ts @@ -0,0 +1,21 @@ +export interface Mammal { + speak(): string; +} + +export class Cat implements Mammal { + public speak() { + return "Meow"; // a cat says meow + } +} + +export class Dog implements Mammal { + public speak() { + return "Woof"; // a dog says woof + } +} + +export class Human implements Mammal { + public speak() { + return "Hello"; + } +} diff --git a/tests/auto/blackbox/testdata/typescript/extra.js b/tests/auto/blackbox/testdata/typescript/extra.js new file mode 100644 index 00000000..5500e468 --- /dev/null +++ b/tests/auto/blackbox/testdata/typescript/extra.js @@ -0,0 +1,3 @@ +if (console) { + console.log("This doesn't do anything useful!"); +} diff --git a/tests/auto/blackbox/testdata/typescript/foo.ts b/tests/auto/blackbox/testdata/typescript/foo.ts new file mode 100644 index 00000000..3554d317 --- /dev/null +++ b/tests/auto/blackbox/testdata/typescript/foo.ts @@ -0,0 +1,5 @@ +export class Greeter { + public getGreeting(): string { + return "guten Tag!"; + } +} diff --git a/tests/auto/blackbox/testdata/typescript/foo2.ts b/tests/auto/blackbox/testdata/typescript/foo2.ts new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/auto/blackbox/testdata/typescript/foo2.ts @@ -0,0 +1 @@ + diff --git a/tests/auto/blackbox/testdata/typescript/hello.ts b/tests/auto/blackbox/testdata/typescript/hello.ts new file mode 100644 index 00000000..940a3ff0 --- /dev/null +++ b/tests/auto/blackbox/testdata/typescript/hello.ts @@ -0,0 +1 @@ +console.log("Hello world!"); diff --git a/tests/auto/blackbox/testdata/typescript/main.ts b/tests/auto/blackbox/testdata/typescript/main.ts new file mode 100644 index 00000000..e2f7ed32 --- /dev/null +++ b/tests/auto/blackbox/testdata/typescript/main.ts @@ -0,0 +1,22 @@ +import Animals = require("./animals"); +import Foo = require("./foo"); +import Extra = require("./woosh/extra"); + +function main() { + var mammals: Animals.Mammal[] = []; + mammals.push(new Animals.Human()); + mammals.push(new Animals.Dog()); + mammals.push(new Animals.Cat()); + + // Make everyone speak + for (var i = 0; i < mammals.length; ++i) { + console.log(mammals[i].speak()); + } + + (new Extra.Boom()); + + var greeting: string = (new Foo.Greeter()).getGreeting(); + console.log(greeting); +} + +main(); diff --git a/tests/auto/blackbox/testdata/typescript/typescript.qbs b/tests/auto/blackbox/testdata/typescript/typescript.qbs new file mode 100644 index 00000000..b6604830 --- /dev/null +++ b/tests/auto/blackbox/testdata/typescript/typescript.qbs @@ -0,0 +1,56 @@ +import qbs + +Project { + NodeJSApplication { + Depends { name: "typescript" } + Depends { name: "lib" } + + typescript.warningLevel: "pedantic" + typescript.generateDeclarations: true + typescript.moduleLoader: "commonjs" + nodejs.applicationFile: "main.ts" + + name: "animals" + + files: [ + "animals.ts", + "extra.js", + "woosh/extra.ts" + ] + } + + Product { + Depends { name: "typescript" } + + typescript.generateDeclarations: true + typescript.moduleLoader: "commonjs" + + name: "lib" + + files: [ + "foo.ts" + ] + } + + Product { + Depends { name: "typescript" } + + typescript.generateDeclarations: true + + name: "lib2" + + files: [ + "foo2.ts" + ] + } + + NodeJSApplication { + Depends { name: "typescript" } + Depends { name: "lib2" } + + typescript.singleFile: true + nodejs.applicationFile: "hello.ts" + + name: "single" + } +} diff --git a/tests/auto/blackbox/testdata/typescript/woosh/extra.ts b/tests/auto/blackbox/testdata/typescript/woosh/extra.ts new file mode 100644 index 00000000..7dbef6a7 --- /dev/null +++ b/tests/auto/blackbox/testdata/typescript/woosh/extra.ts @@ -0,0 +1,2 @@ +export class Boom { +} diff --git a/tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/custom1.in b/tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/custom1.in new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/custom2.in b/tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/custom2.in new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/project.qbs b/tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/project.qbs new file mode 100644 index 00000000..7d408762 --- /dev/null +++ b/tests/auto/blackbox/testdata/usings-as-sole-inputs-non-multiplexed/project.qbs @@ -0,0 +1,62 @@ +import qbs +import qbs.FileInfo +import qbs.TextFile + +Project { + Product { + name: "p1" + type: "custom" + Group { + files: "custom1.in" + fileTags: "custom.in" + } + } + Product { + name: "p2" + type: "custom" + Group { + files: "custom2.in" + fileTags: "custom.in" + } + } + + Rule { + inputs: "custom.in" + Artifact { + filePath: FileInfo.baseName(input.filePath) + ".out" + fileTags: "custom" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating " + output.fileName; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.close(); + } + return cmd; + } + } + + Product { + name: "p3" + type: "custom-plus" + Depends { name: "p1" } + Depends { name: "p2" } + Rule { + inputsFromDependencies: "custom" + Artifact { + filePath: FileInfo.fileName(input.filePath) + ".plus" + fileTags: "custom-plus" + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "generating " + output.fileName; + cmd.sourceCode = function() { + var f = new TextFile(output.filePath, TextFile.WriteOnly); + f.close(); + } + return cmd; + } + } + } +} diff --git a/tests/auto/blackbox/testdata/versioncheck/modules/higher/higher.qbs b/tests/auto/blackbox/testdata/versioncheck/modules/higher/higher.qbs new file mode 100644 index 00000000..4a9c85ce --- /dev/null +++ b/tests/auto/blackbox/testdata/versioncheck/modules/higher/higher.qbs @@ -0,0 +1,10 @@ +import qbs + +Module { + Depends { + name: "lower" + required: false + versionAtLeast: "1.0" + versionBelow: "10.0" + } +} diff --git a/tests/auto/blackbox/testdata/versioncheck/modules/lower/lower.qbs b/tests/auto/blackbox/testdata/versioncheck/modules/lower/lower.qbs new file mode 100644 index 00000000..9322b53b --- /dev/null +++ b/tests/auto/blackbox/testdata/versioncheck/modules/lower/lower.qbs @@ -0,0 +1,3 @@ +import qbs + +Module { } diff --git a/tests/auto/blackbox/testdata/versioncheck/versioncheck.qbs b/tests/auto/blackbox/testdata/versioncheck/versioncheck.qbs new file mode 100644 index 00000000..8214428f --- /dev/null +++ b/tests/auto/blackbox/testdata/versioncheck/versioncheck.qbs @@ -0,0 +1,12 @@ +import qbs + +Product { + property string requestedMinVersion + property string requestedMaxVersion + Depends { name: "higher" } + Depends { + name: "lower" + versionAtLeast: requestedMinVersion + versionBelow: requestedMaxVersion + } +} diff --git a/tests/auto/blackbox/testdata/versionscript/testlib.c b/tests/auto/blackbox/testdata/versionscript/testlib.c new file mode 100644 index 00000000..87c59b27 --- /dev/null +++ b/tests/auto/blackbox/testdata/versionscript/testlib.c @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +void dummyLocal() {} +void dummyGlobal() {} diff --git a/tests/auto/blackbox/testdata/versionscript/versionscript b/tests/auto/blackbox/testdata/versionscript/versionscript new file mode 100644 index 00000000..ac9c05b4 --- /dev/null +++ b/tests/auto/blackbox/testdata/versionscript/versionscript @@ -0,0 +1 @@ +{ global: dummyGlobal; local: dummyLocal; }; diff --git a/tests/auto/blackbox/testdata/versionscript/versionscript.qbs b/tests/auto/blackbox/testdata/versionscript/versionscript.qbs new file mode 100644 index 00000000..c322de7d --- /dev/null +++ b/tests/auto/blackbox/testdata/versionscript/versionscript.qbs @@ -0,0 +1,33 @@ +import qbs + +DynamicLibrary { + type: base.concat("custom") + Depends { name: "cpp" } + files: ["testlib.c"] + Group { + name: "version script" + files: ["versionscript"] + fileTags: ["versionscript"] + } + + Rule { + multiplex: true + Artifact { + filePath: "dummy.txt" + fileTags: ["custom"] + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.silent = true; + cmd.sourceCode = function() { + console.warn("---" + product.moduleProperty("cpp", "nmPath") + "---"); + } + return [cmd]; + } + } + + Group { + fileTagsFilter: ["dynamiclibrary"] + qbs.install: true + } +} diff --git a/tests/auto/blackbox/testdata/wildcard_renaming/pioniere.txt b/tests/auto/blackbox/testdata/wildcard_renaming/pioniere.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/wildcard_renaming/wildcard_renaming.qbs b/tests/auto/blackbox/testdata/wildcard_renaming/wildcard_renaming.qbs new file mode 100644 index 00000000..4cb2c071 --- /dev/null +++ b/tests/auto/blackbox/testdata/wildcard_renaming/wildcard_renaming.qbs @@ -0,0 +1,8 @@ +import qbs 1.0 + +Product { + Group { + qbs.install: true + files: "*" + } +} diff --git a/tests/auto/blackbox/testdata/wildcards-and-rules/input1.inp b/tests/auto/blackbox/testdata/wildcards-and-rules/input1.inp new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/blackbox/testdata/wildcards-and-rules/project.qbs b/tests/auto/blackbox/testdata/wildcards-and-rules/project.qbs new file mode 100644 index 00000000..88fadd3a --- /dev/null +++ b/tests/auto/blackbox/testdata/wildcards-and-rules/project.qbs @@ -0,0 +1,37 @@ +import qbs +import qbs.TextFile + +Product { + name: "wildcards-and-rules" + type: "mytype" + files: ["*.inp", "*.dep"] + FileTagger { + patterns: "*.inp" + fileTags: ["inp"] + } + FileTagger { + patterns: "*.dep" + fileTags: ["dep"] + } + Rule { + multiplex: true + inputs: ["inp"] + explicitlyDependsOn: ["dep"] + Artifact { + filePath: "test.mytype" + fileTags: product.type + } + prepare: { + var cmd = new JavaScriptCommand(); + cmd.description = "Creating output artifact"; + cmd.highlight = "codegen"; + cmd.sourceCode = function() { + var file = new TextFile(output.filePath, TextFile.WriteOnly); + for (var i = 0; i < inputs.inp.length; ++i) + file.writeLine(inputs.inp[i].fileName); + file.close(); + } + return cmd; + } + } +} diff --git a/tests/auto/blackbox/testdata/wix/ExampleScript.bat b/tests/auto/blackbox/testdata/wix/ExampleScript.bat new file mode 100644 index 00000000..3af583cd --- /dev/null +++ b/tests/auto/blackbox/testdata/wix/ExampleScript.bat @@ -0,0 +1 @@ +echo Hello world! diff --git a/tests/auto/blackbox/testdata/wix/QbsBootstrapper.wxs b/tests/auto/blackbox/testdata/wix/QbsBootstrapper.wxs new file mode 100644 index 00000000..272f6af5 --- /dev/null +++ b/tests/auto/blackbox/testdata/wix/QbsBootstrapper.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tests/auto/blackbox/testdata/wix/QbsSetup.wxs b/tests/auto/blackbox/testdata/wix/QbsSetup.wxs new file mode 100644 index 00000000..8f97ff66 --- /dev/null +++ b/tests/auto/blackbox/testdata/wix/QbsSetup.wxs @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/auto/blackbox/testdata/wix/Qt.wxs b/tests/auto/blackbox/testdata/wix/Qt.wxs new file mode 100644 index 00000000..fbd992c4 --- /dev/null +++ b/tests/auto/blackbox/testdata/wix/Qt.wxs @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/auto/blackbox/testdata/wix/WiXInstallers.qbs b/tests/auto/blackbox/testdata/wix/WiXInstallers.qbs new file mode 100644 index 00000000..3e7facd7 --- /dev/null +++ b/tests/auto/blackbox/testdata/wix/WiXInstallers.qbs @@ -0,0 +1,34 @@ +import qbs +import qbs.FileInfo + +Project { + WindowsInstallerPackage { + name: "QbsSetup" + targetName: "qbs-" + qbs.architecture + files: ["QbsSetup.wxs", "ExampleScript.bat"] + wix.defines: ["scriptName=ExampleScript.bat"] + wix.extensions: ["WixBalExtension", "WixUIExtension"] + + Export { + Depends { name: "wix" } + wix.defines: base.concat(["msiName=" + + FileInfo.joinPaths(product.buildDirectory, + product.targetName + wix.windowsInstallerSuffix)]) + } + } + + WindowsSetupPackage { + Depends { name: "QbsSetup" } + condition: qbs.hostOS.contains("windows") // currently does not work in Wine with WiX 3.9 + name: "QbsBootstrapper" + targetName: "qbs-setup-" + qbs.architecture + files: ["QbsBootstrapper.wxs"] + } + + WindowsInstallerPackage { + name: "RegressionBuster9000" + files: ["QbsSetup.wxs", "Qt.wxs", "de.wxl"] + wix.defines: ["scriptName=ExampleScript.bat"] + wix.cultures: [] + } +} diff --git a/tests/auto/blackbox/testdata/wix/de.wxl b/tests/auto/blackbox/testdata/wix/de.wxl new file mode 100644 index 00000000..75394cfd --- /dev/null +++ b/tests/auto/blackbox/testdata/wix/de.wxl @@ -0,0 +1 @@ + diff --git a/tests/auto/blackbox/testdata/xcode/xcode-project.qbs b/tests/auto/blackbox/testdata/xcode/xcode-project.qbs new file mode 100644 index 00000000..0c2b0b93 --- /dev/null +++ b/tests/auto/blackbox/testdata/xcode/xcode-project.qbs @@ -0,0 +1,37 @@ +import qbs + +Project { + property stringList sdks: [] + + Product { + Depends { name: "xcode" } + + consoleApplication: { + console.info("Developer directory: " + xcode.developerPath); + console.info("SDK: " + xcode.sdk); + console.info("Target devices: " + xcode.targetDevices.join(", ")); + console.info("SDK name: " + xcode.sdkName); + console.info("SDK version: " + xcode.sdkVersion); + console.info("Latest SDK name: " + xcode.latestSdkName); + console.info("Latest SDK version: " + xcode.latestSdkVersion); + console.info("Available SDK names: " + xcode.availableSdkNames.join(", ")); + console.info("Available SDK versions: " + xcode.availableSdkVersions.join(", ")); + console.info("Actual SDK list: " + project.sdks.join(", ")); + + var msg = "Unexpected SDK list [" + xcode.availableSdkVersions.join(", ") + "]"; + var testArraysEqual = function(a, b) { + if (!a || !b || a.length !== b.length) { + throw msg; + } + + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) { + throw msg; + } + } + } + + testArraysEqual(project.sdks, xcode.availableSdkVersions); + } + } +} diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp new file mode 100644 index 00000000..6577ebbc --- /dev/null +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -0,0 +1,4437 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_blackbox.h" + +#include "../shared.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define WAIT_FOR_NEW_TIMESTAMP() waitForNewTimestamp(testDataDir) + +using qbs::Internal::HostOsInfo; +using qbs::Profile; +using qbs::Settings; + +class MacosTarHealer { +public: + MacosTarHealer() { + if (HostOsInfo::hostOs() == HostOsInfo::HostOsMacos) { + // work around absurd tar behavior on macOS + qputenv("COPY_EXTENDED_ATTRIBUTES_DISABLE", "true"); + qputenv("COPYFILE_DISABLE", "true"); + } + } + + ~MacosTarHealer() { + if (HostOsInfo::hostOs() == HostOsInfo::HostOsMacos) { + qunsetenv("COPY_EXTENDED_ATTRIBUTES_DISABLE"); + qunsetenv("COPYFILE_DISABLE"); + } + } +}; + +QMap TestBlackbox::findCli(int *status) +{ + QTemporaryDir temp; + QDir::setCurrent(testDataDir + "/find"); + QbsRunParameters params = QStringList() << "-f" << "find-cli.qbs"; + params.buildDirectory = temp.path(); + const int res = runQbs(params); + if (status) + *status = res; + QFile file(temp.path() + "/" + relativeProductBuildDir("find-cli") + "/cli.json"); + if (!file.open(QIODevice::ReadOnly)) + return QMap { }; + const auto tools = QJsonDocument::fromJson(file.readAll()).toVariant().toMap(); + return QMap { + {"path", QDir::fromNativeSeparators(tools["path"].toString())}, + }; +} + +QMap TestBlackbox::findNodejs(int *status) +{ + QTemporaryDir temp; + QDir::setCurrent(testDataDir + "/find"); + QbsRunParameters params = QStringList() << "-f" << "find-nodejs.qbs"; + params.buildDirectory = temp.path(); + const int res = runQbs(params); + if (status) + *status = res; + QFile file(temp.path() + "/" + relativeProductBuildDir("find-nodejs") + "/nodejs.json"); + if (!file.open(QIODevice::ReadOnly)) + return QMap { }; + const auto tools = QJsonDocument::fromJson(file.readAll()).toVariant().toMap(); + return QMap { + {"node", QDir::fromNativeSeparators(tools["node"].toString())} + }; +} + +QMap TestBlackbox::findTypeScript(int *status) +{ + QTemporaryDir temp; + QDir::setCurrent(testDataDir + "/find"); + QbsRunParameters params = QStringList() << "-f" << "find-typescript.qbs"; + params.buildDirectory = temp.path(); + const int res = runQbs(params); + if (status) + *status = res; + QFile file(temp.path() + "/" + relativeProductBuildDir("find-typescript") + "/typescript.json"); + if (!file.open(QIODevice::ReadOnly)) + return QMap { }; + const auto tools = QJsonDocument::fromJson(file.readAll()).toVariant().toMap(); + return QMap { + {"tsc", QDir::fromNativeSeparators(tools["tsc"].toString())} + }; +} + +QString TestBlackbox::findArchiver(const QString &fileName, int *status) +{ + if (fileName == "jar") + return findJdkTools(status)[fileName]; + + QString binary = findExecutable(QStringList(fileName)); + if (binary.isEmpty()) { + Settings s((QString())); + Profile p(profileName(), &s); + binary = findExecutable(p.value("archiver.command").toStringList()); + } + return binary; +} + +static bool isXcodeProfile(const QString &profileName) +{ + qbs::Settings settings((QString())); + qbs::Profile profile(profileName, &settings); + return profile.value("qbs.toolchain").toStringList().contains("xcode"); +} + +void TestBlackbox::sevenZip() +{ + QDir::setCurrent(testDataDir + "/archiver"); + QString binary = findArchiver("7z"); + if (binary.isEmpty()) + QSKIP("7zip not found"); + QCOMPARE(runQbs(QbsRunParameters(QStringList() << "archiver.type:7zip")), 0); + const QString outputFile = relativeProductBuildDir("archivable") + "/archivable.7z"; + QVERIFY2(regularFileExists(outputFile), qPrintable(outputFile)); + QProcess listContents; + listContents.start(binary, QStringList() << "l" << outputFile); + QVERIFY2(listContents.waitForStarted(), qPrintable(listContents.errorString())); + QVERIFY2(listContents.waitForFinished(), qPrintable(listContents.errorString())); + QVERIFY2(listContents.exitCode() == 0, listContents.readAllStandardError().constData()); + const QByteArray output = listContents.readAllStandardOutput(); + QVERIFY2(output.contains("2 files"), output.constData()); + QVERIFY2(output.contains("test.txt"), output.constData()); + QVERIFY2(output.contains("archivable.qbs"), output.constData()); +} + +void TestBlackbox::suspiciousCalls() +{ + const QString projectDir = testDataDir + "/suspicious-calls"; + QDir::setCurrent(projectDir); + rmDirR(relativeBuildDir()); + QFETCH(QString, projectFile); + QbsRunParameters params(QStringList() << "-f" << projectFile); + QCOMPARE(runQbs(params), 0); + QFETCH(QByteArray, expectedWarning); + QVERIFY2(m_qbsStderr.contains(expectedWarning), m_qbsStderr.constData()); +} + +void TestBlackbox::suspiciousCalls_data() +{ + QTest::addColumn("projectFile"); + QTest::addColumn("expectedWarning"); + QTest::newRow("File.copy() in Probe") << "copy-probe.qbs" << QByteArray(); + QTest::newRow("File.copy() during evaluation") << "copy-eval.qbs" << QByteArray("File.copy()"); + QTest::newRow("File.copy() in prepare script") + << "copy-prepare.qbs" << QByteArray("File.copy()"); + QTest::newRow("File.copy() in command") << "copy-command.qbs" << QByteArray(); + QTest::newRow("File.directoryEntries() in Probe") << "direntries-probe.qbs" << QByteArray(); + QTest::newRow("File.directoryEntries() during evaluation") + << "direntries-eval.qbs" << QByteArray("File.directoryEntries()"); + QTest::newRow("File.directoryEntries() in prepare script") + << "direntries-prepare.qbs" << QByteArray(); + QTest::newRow("File.directoryEntries() in command") << "direntries-command.qbs" << QByteArray(); +} + +void TestBlackbox::tar() +{ + if (HostOsInfo::hostOs() == HostOsInfo::HostOsWindows) + QSKIP("Beware of the msys tar"); + MacosTarHealer tarHealer; + QDir::setCurrent(testDataDir + "/archiver"); + QString binary = findArchiver("tar"); + if (binary.isEmpty()) + QSKIP("tar not found"); + QCOMPARE(runQbs(QbsRunParameters(QStringList() << "archiver.type:tar")), 0); + const QString outputFile = relativeProductBuildDir("archivable") + "/archivable.tar.gz"; + QVERIFY2(regularFileExists(outputFile), qPrintable(outputFile)); + QProcess listContents; + listContents.start(binary, QStringList() << "tf" << outputFile); + QVERIFY2(listContents.waitForStarted(), qPrintable(listContents.errorString())); + QVERIFY2(listContents.waitForFinished(), qPrintable(listContents.errorString())); + QVERIFY2(listContents.exitCode() == 0, listContents.readAllStandardError().constData()); + QFile listFile("list.txt"); + QVERIFY2(listFile.open(QIODevice::ReadOnly), qPrintable(listFile.errorString())); + QCOMPARE(listContents.readAllStandardOutput(), listFile.readAll()); +} + +static QStringList sortedFileList(const QByteArray &ba) +{ + auto list = QString::fromUtf8(ba).split(QRegExp("[\r\n]"), QString::SkipEmptyParts); + std::sort(list.begin(), list.end()); + return list; +} + +void TestBlackbox::zip() +{ + QFETCH(QString, binaryName); + int status = 0; + const QString binary = findArchiver(binaryName, &status); + QCOMPARE(status, 0); + if (binary.isEmpty()) + QSKIP("zip tool not found"); + + QDir::setCurrent(testDataDir + "/archiver"); + QbsRunParameters params(QStringList() + << "archiver.type:zip" << "archiver.command:" + binary); + QCOMPARE(runQbs(params), 0); + const QString outputFile = relativeProductBuildDir("archivable") + "/archivable.zip"; + QVERIFY2(regularFileExists(outputFile), qPrintable(outputFile)); + QProcess listContents; + if (binaryName == "zip") { + // zipinfo is part of Info-Zip + listContents.start("zipinfo", QStringList() << "-1" << outputFile); + } else { + listContents.start(binary, QStringList() << "tf" << outputFile); + } + QVERIFY2(listContents.waitForStarted(), qPrintable(listContents.errorString())); + QVERIFY2(listContents.waitForFinished(), qPrintable(listContents.errorString())); + QVERIFY2(listContents.exitCode() == 0, listContents.readAllStandardError().constData()); + QFile listFile("list.txt"); + QVERIFY2(listFile.open(QIODevice::ReadOnly), qPrintable(listFile.errorString())); + QCOMPARE(sortedFileList(listContents.readAllStandardOutput()), + sortedFileList(listFile.readAll())); + + // Make sure the module is still loaded when the java/jar fallback is not available + params.arguments << "java.jdkPath:/blubb"; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::zip_data() +{ + QTest::addColumn("binaryName"); + QTest::newRow("zip") << "zip"; + QTest::newRow("jar") << "jar"; +} + +void TestBlackbox::zipInvalid() +{ + QDir::setCurrent(testDataDir + "/archiver"); + QbsRunParameters params(QStringList() << "archiver.type:zip" + << "archiver.command:/bin/something"); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("unrecognized archive tool: 'something'"), m_qbsStderr.constData()); +} + +TestBlackbox::TestBlackbox() : TestBlackboxBase (SRCDIR "/testdata", "blackbox") +{ +} + +void TestBlackbox::alwaysRun() +{ + QFETCH(QString, projectFile); + + QDir::setCurrent(testDataDir + "/always-run"); + rmDirR(relativeBuildDir()); + QbsRunParameters params("build", QStringList() << "-f" << projectFile); + if (projectFile.contains("transformer")) { + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("removed"), m_qbsStderr.constData()); + return; + } + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("yo")); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("yo")); + WAIT_FOR_NEW_TIMESTAMP(); + QFile f(projectFile); + QVERIFY2(f.open(QIODevice::ReadWrite), qPrintable(f.errorString())); + QByteArray content = f.readAll(); + content.replace("alwaysRun: false", "alwaysRun: true"); + f.resize(0); + f.write(content); + f.close(); + + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("yo")); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("yo")); +} + +void TestBlackbox::alwaysRun_data() +{ + QTest::addColumn("projectFile"); + QTest::newRow("Transformer") << "transformer.qbs"; + QTest::newRow("Rule") << "rule.qbs"; +} + +void TestBlackbox::buildDirectories() +{ + const QString projectDir + = QDir::cleanPath(testDataDir + QLatin1String("/build-directories")); + const QString projectBuildDir = projectDir + '/' + relativeBuildDir(); + QDir::setCurrent(projectDir); + QCOMPARE(runQbs(), 0); + const QStringList outputLines + = QString::fromLocal8Bit(m_qbsStdout.trimmed()).split('\n', QString::SkipEmptyParts); + QVERIFY2(outputLines.contains(projectDir + '/' + relativeProductBuildDir("p1")), + m_qbsStdout.constData()); + QVERIFY2(outputLines.contains(projectDir + '/' + relativeProductBuildDir("p2")), + m_qbsStdout.constData()); + QVERIFY2(outputLines.contains(projectBuildDir), m_qbsStdout.constData()); + QVERIFY2(outputLines.contains(projectDir), m_qbsStdout.constData()); +} + +class QFileInfo2 : public QFileInfo { +public: + QFileInfo2(const QString &path) : QFileInfo(path) { } + bool isRegularFile() const { return isFile() && !isSymLink(); } + bool isRegularDir() const { return isDir() && !isSymLink(); } + bool isFileSymLink() const { return isFile() && isSymLink(); } + bool isDirSymLink() const { return isDir() && isSymLink(); } +}; + +void TestBlackbox::bundleStructure() +{ + if (!HostOsInfo::isMacosHost()) + QSKIP("only applies on macOS"); + + QFETCH(QString, productName); + QFETCH(QString, productTypeIdentifier); + QFETCH(bool, isShallow); + + QDir::setCurrent(testDataDir + "/bundle-structure"); + QbsRunParameters params; + params.arguments << "project.buildableProducts:" + productName; + if (isShallow) { + // Coerce shallow bundles - don't set bundle.isShallow directly because we want to test the + // automatic detection + params.arguments + << "qbs.targetOS:ios,darwin,bsd,unix" + << "qbs.architecture:arm64"; + } + + if (productName == "ABadApple" || productName == "ABadThirdParty") + params.expectFailure = true; + + rmDirR(relativeBuildDir()); + const int status = runQbs(params); + if (status != 0) { + QVERIFY2(m_qbsStderr.contains("Bundle product type " + + productTypeIdentifier.toLatin1() + + " is not supported."), + m_qbsStderr.constData()); + return; + } + + QCOMPARE(status, 0); + + if (!isShallow) { + if (productName == "A") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/MacOS").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/MacOS/A").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/PkgInfo").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents/Resources/resource.txt").isRegularFile()); + } + + if (productName == "B") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/B").isFileSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Headers").isDirSymLink()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Modules").isDirSymLink()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/B.framework/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/PrivateHeaders").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Resources").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/B").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Headers/dummy.h").isRegularFile()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Modules").isRegularDir()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Modules/module.modulemap").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/A/Resources/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Versions/Current").isDirSymLink()); + } + + if (productName == "C") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/C").isFileSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Headers").isDirSymLink()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Modules").isDirSymLink()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/C.framework/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/PrivateHeaders").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Resources").isDirSymLink()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/C").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Headers/dummy.h").isRegularFile()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Modules").isRegularDir()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Modules/module.modulemap").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/A/Resources/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Versions/Current").isDirSymLink()); + } + + if (productName == "D") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/MacOS").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/MacOS/D").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Contents/Resources/resource.txt").isRegularFile()); + } + + if (productName == "E") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/MacOS").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/MacOS/E").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/E.appex/Contents/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Contents/Resources/resource.txt").isRegularFile()); + } + + if (productName == "F") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/MacOS").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/MacOS/F").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/Resources").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Contents/Resources/resource.txt").isRegularFile()); + } + + if (productName == "G") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/G").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/G/ContentInfo.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/G/Contents/resource.txt").isRegularFile()); + } + } else { + if (productName == "A") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/A").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Info.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/PkgInfo").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/resource.txt").isRegularFile()); + } + + if (productName == "B") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/B").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Info.plist").isRegularFile()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Modules").isRegularDir()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/Modules/module.modulemap").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/B.framework/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/B.framework/resource.txt").isRegularFile()); + } + + if (productName == "C") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/C").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Info.plist").isRegularFile()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Modules").isRegularDir()); + //QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/Modules/module.modulemap").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/C.framework/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/C.framework/resource.txt").isRegularFile()); + } + + if (productName == "D") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/D").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/Info.plist").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/D.bundle/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/D.bundle/resource.txt").isRegularFile()); + } + + if (productName == "E") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/E").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/Info.plist").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/E.appex/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/E.appex/resource.txt").isRegularFile()); + } + + if (productName == "F") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/F").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Headers").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Headers/dummy.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/Info.plist").isRegularFile()); + QVERIFY(!QFileInfo2(defaultInstallRoot + "/F.xpc/PkgInfo").exists()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/PrivateHeaders").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/PrivateHeaders/dummy_p.h").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/F.xpc/resource.txt").isRegularFile()); + } + + if (productName == "G") { + QVERIFY(QFileInfo2(defaultInstallRoot + "/G").isRegularDir()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/G/ContentInfo.plist").isRegularFile()); + QVERIFY(QFileInfo2(defaultInstallRoot + "/G/Contents/resource.txt").isRegularFile()); + } + } +} + +void TestBlackbox::bundleStructure_data() +{ + QTest::addColumn("productName"); + QTest::addColumn("productTypeIdentifier"); + QTest::addColumn("isShallow"); + + const auto addRows = [](bool isShallow) { + const QString s = (isShallow ? " shallow" : ""); + QTest::newRow(("A" + s).toLatin1()) << "A" << "com.apple.product-type.application" << isShallow; + QTest::newRow(("ABadApple" + s).toLatin1()) << "ABadApple" << "com.apple.product-type.will.never.exist.ever.guaranteed" << isShallow; + QTest::newRow(("ABadThirdParty" + s).toLatin1()) << "ABadThirdParty" << "org.special.third.party.non.existent.product.type" << isShallow; + QTest::newRow(("B" + s).toLatin1()) << "B" << "com.apple.product-type.framework" << isShallow; + QTest::newRow(("C" + s).toLatin1()) << "C" << "com.apple.product-type.framework.static" << isShallow; + QTest::newRow(("D" + s).toLatin1()) << "D" << "com.apple.product-type.bundle" << isShallow; + QTest::newRow(("E" + s).toLatin1()) << "E" << "com.apple.product-type.app-extension" << isShallow; + QTest::newRow(("F" + s).toLatin1()) << "F" << "com.apple.product-type.xpc-service" << isShallow; + QTest::newRow(("G" + s).toLatin1()) << "G" << "com.apple.product-type.in-app-purchase-content" << isShallow; + }; + + addRows(true); + addRows(false); +} + +void TestBlackbox::changedFiles_data() +{ + QTest::addColumn("useChangedFilesForInitialBuild"); + QTest::newRow("initial build with changed files") << true; + QTest::newRow("initial build without changed files") << false; +} + +void TestBlackbox::changedFiles() +{ + QFETCH(bool, useChangedFilesForInitialBuild); + + QDir::setCurrent(testDataDir + "/changed-files"); + rmDirR(relativeBuildDir()); + const QString changedFile = QDir::cleanPath(QDir::currentPath() + "/file1.cpp"); + QbsRunParameters params1; + if (useChangedFilesForInitialBuild) + params1 = QbsRunParameters(QStringList("--changed-files") << changedFile); + + // Initial run: Build all files, even though only one of them was marked as changed + // (if --changed-files was used). + QCOMPARE(runQbs(params1), 0); + QCOMPARE(m_qbsStdout.count("compiling"), 3); + QCOMPARE(m_qbsStdout.count("creating"), 3); + + WAIT_FOR_NEW_TIMESTAMP(); + touch(QDir::currentPath() + "/main.cpp"); + + // Now only the file marked as changed must be compiled, even though it hasn't really + // changed and another one has. + QbsRunParameters params2(QStringList("--changed-files") << changedFile); + QCOMPARE(runQbs(params2), 0); + QCOMPARE(m_qbsStdout.count("compiling"), 1); + QCOMPARE(m_qbsStdout.count("creating"), 1); + QVERIFY2(m_qbsStdout.contains("file1.cpp"), m_qbsStdout.constData()); +} + +void TestBlackbox::changeInDisabledProduct() +{ + QDir::setCurrent(testDataDir + "/change-in-disabled-product"); + QCOMPARE(runQbs(), 0); + WAIT_FOR_NEW_TIMESTAMP(); + QFile projectFile("project.qbs"); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + QByteArray content = projectFile.readAll(); + content.replace("// 'test2.txt'", "'test2.txt'"); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::changeInImportedFile() +{ + QDir::setCurrent(testDataDir + "/change-in-imported-file"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("old output"), m_qbsStdout.constData()); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile jsFile("prepare.js"); + QVERIFY2(jsFile.open(QIODevice::ReadWrite), qPrintable(jsFile.errorString())); + QByteArray content = jsFile.readAll(); + content.replace("old output", "new output"); + jsFile.resize(0); + jsFile.write(content); + jsFile.close(); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("new output"), m_qbsStdout.constData()); + + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY2(jsFile.open(QIODevice::ReadWrite), qPrintable(jsFile.errorString())); + jsFile.resize(0); + jsFile.write(content); + jsFile.close(); + QCOMPARE(runQbs(), 0); + QVERIFY2(!m_qbsStdout.contains("output"), m_qbsStdout.constData()); +} + +static QJsonObject findByName(const QJsonArray &objects, const QString &name) +{ + for (const QJsonValue v : objects) { + if (!v.isObject()) + continue; + QJsonObject obj = v.toObject(); + const QString objName = obj.value(QLatin1String("name")).toString(); + if (objName == name) + return obj; + } + return QJsonObject(); +} + +void TestBlackbox::dependenciesProperty() +{ + QDir::setCurrent(testDataDir + QLatin1String("/dependenciesProperty")); + QCOMPARE(runQbs(), 0); + QFile depsFile(relativeProductBuildDir("product1") + QLatin1String("/product1.deps")); + QVERIFY(depsFile.open(QFile::ReadOnly)); + + QJsonParseError jsonerror; + QJsonDocument jsondoc = QJsonDocument::fromJson(depsFile.readAll(), &jsonerror); + if (jsonerror.error != QJsonParseError::NoError) { + qDebug() << jsonerror.errorString(); + QFAIL("JSON parsing failed."); + } + QVERIFY(jsondoc.isArray()); + QJsonArray dependencies = jsondoc.array(); + QCOMPARE(dependencies.size(), 2); + QJsonObject product2 = findByName(dependencies, QStringLiteral("product2")); + QJsonArray product2_type = product2.value(QLatin1String("type")).toArray(); + QCOMPARE(product2_type.size(), 1); + QCOMPARE(product2_type.first().toString(), QLatin1String("application")); + QCOMPARE(product2.value(QLatin1String("narf")).toString(), QLatin1String("zort")); + QJsonArray product2_deps = product2.value(QLatin1String("dependencies")).toArray(); + QVERIFY(!product2_deps.isEmpty()); + QJsonObject product2_qbs = findByName(product2_deps, QStringLiteral("qbs")); + QVERIFY(!product2_qbs.isEmpty()); + QJsonObject product2_cpp = findByName(product2_deps, QStringLiteral("cpp")); + QJsonArray product2_cpp_defines = product2_cpp.value(QLatin1String("defines")).toArray(); + QCOMPARE(product2_cpp_defines.size(), 1); + QCOMPARE(product2_cpp_defines.first().toString(), QLatin1String("SMURF")); +} + +void TestBlackbox::dependencyProfileMismatch() +{ + QDir::setCurrent(testDataDir + "/dependency-profile-mismatch"); + qbs::Settings s((QString())); + qbs::Internal::TemporaryProfile depProfile("qbs_autotests_profileMismatch", &s); + depProfile.p.setValue("qbs.architecture", "x86"); // Profiles must not be empty... + s.sync(); + QbsRunParameters params(QStringList() << ("project.mainProfile:" + profileName()) + << ("project.depProfile:" + depProfile.p.name())); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains(profileName().toLocal8Bit() + "', which does not exist"), + m_qbsStderr.constData()); +} + +void TestBlackbox::deploymentTarget() +{ + if (!HostOsInfo::isMacosHost()) + QSKIP("only applies on macOS"); + + QFETCH(QString, os); + QFETCH(QString, arch); + QFETCH(QString, cflags); + QFETCH(QString, lflags); + + QDir::setCurrent(testDataDir + "/deploymentTarget"); + + QbsRunParameters params; + params.arguments = QStringList() + << "--command-echo-mode" + << "command-line" + << "qbs.targetOS:" + os + << "qbs.architecture:" + arch; + + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains(cflags.toLatin1()), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains(lflags.toLatin1()), m_qbsStdout.constData()); +} + +void TestBlackbox::deploymentTarget_data() +{ + QTest::addColumn("os"); + QTest::addColumn("arch"); + QTest::addColumn("cflags"); + QTest::addColumn("lflags"); + QTest::newRow("macos") << "macos,darwin,bsd,unix" << "x86_64" + << "-triple x86_64-apple-macosx10.4" + << "-macosx_version_min 10.4"; + QTest::newRow("ios") << "ios,darwin,bsd,unix" << "arm64" + << "-triple arm64-apple-ios5.0" + << "-iphoneos_version_min 5.0"; + QTest::newRow("ios-sim") << "ios-simulator,ios,darwin,bsd,unix" << "x86_64" + << "-triple x86_64-apple-ios5.0" + << "-ios_simulator_version_min 5.0"; +} + +void TestBlackbox::deprecatedProperty() +{ + QDir::setCurrent(testDataDir + "/deprecated-property"); + QCOMPARE(runQbs(QStringList("-q")), 0); + QVERIFY2(m_qbsStderr.contains("deprecated-property.qbs:6:24 The property 'oldProp' is " + "deprecated and will be removed in Qbs 99.9.0."), m_qbsStderr.constData()); + QVERIFY2(m_qbsStderr.contains("deprecated-property.qbs:7:28 The property 'veryOldProp' can no " + "longer be used. It was removed in Qbs 1.3.0."), m_qbsStderr.constData()); + QVERIFY2(m_qbsStderr.count("Use newProp instead.") == 2, m_qbsStderr.constData()); + QVERIFY2(m_qbsStderr.count("is deprecated") == 1, m_qbsStderr.constData()); + QVERIFY2(m_qbsStderr.count("was removed") == 1, m_qbsStderr.constData()); +} + +void TestBlackbox::symlinkRemoval() +{ + if (HostOsInfo::isWindowsHost()) + QSKIP("No symlink support on Windows."); + QDir::setCurrent(testDataDir + "/symlink-removal"); + QVERIFY(QDir::current().mkdir("dir1")); + QVERIFY(QDir::current().mkdir("dir2")); + QVERIFY(QFile::link("dir2", "dir1/broken-link")); + QVERIFY(QFile::link(QFileInfo("dir2").absoluteFilePath(), "dir1/valid-link-to-dir")); + QVERIFY(QFile::link(QFileInfo("symlink-removal.qbs").absoluteFilePath(), + "dir1/valid-link-to-file")); + QCOMPARE(runQbs(), 0); + QVERIFY(!QFile::exists("dir1")); + QVERIFY(QFile::exists("dir2")); + QVERIFY(QFile::exists("symlink-removal.qbs")); +} + +void TestBlackbox::usingsAsSoleInputsNonMultiplexed() +{ + QDir::setCurrent(testDataDir + QLatin1String("/usings-as-sole-inputs-non-multiplexed")); + QCOMPARE(runQbs(), 0); + const QString p3BuildDir = relativeProductBuildDir("p3"); + QVERIFY(regularFileExists(p3BuildDir + "/custom1.out.plus")); + QVERIFY(regularFileExists(p3BuildDir + "/custom2.out.plus")); +} + +void TestBlackbox::versionCheck() +{ + QDir::setCurrent(testDataDir + "/versioncheck"); + QFETCH(QString, requestedMinVersion); + QFETCH(QString, requestedMaxVersion); + QFETCH(QString, actualVersion); + QFETCH(QString, errorMessage); + QbsRunParameters params; + params.expectFailure = !errorMessage.isEmpty(); + params.arguments << "-n" << ("versioncheck.requestedMinVersion:'" + requestedMinVersion + "'") + << ("versioncheck.requestedMaxVersion:'" + requestedMaxVersion + "'") + << ("lower.version:'" + actualVersion + "'"); + QCOMPARE(runQbs(params) == 0, errorMessage.isEmpty()); + if (params.expectFailure) + QVERIFY2(QString(m_qbsStderr).contains(errorMessage), m_qbsStderr.constData()); +} + +void TestBlackbox::versionCheck_data() +{ + QTest::addColumn("requestedMinVersion"); + QTest::addColumn("requestedMaxVersion"); + QTest::addColumn("actualVersion"); + QTest::addColumn("errorMessage"); + + QTest::newRow("ok1") << "1.0" << "1.1" << "1.0" << QString(); + QTest::newRow("ok2") << "1.0" << "2.0.1" << "2.0" << QString(); + QTest::newRow("ok3") << "1.0" << "2.5" << "1.5" << QString(); + QTest::newRow("ok3") << "1.0" << "2.0" << "1.99" << QString(); + QTest::newRow("bad1") << "2.0" << "2.1" << "1.5" << "needs to be at least"; + QTest::newRow("bad2") << "2.0" << "3.0" << "1.5" << "needs to be at least"; + QTest::newRow("bad3") << "2.0" << "3.0" << "3.5" << "needs to be lower than"; + QTest::newRow("bad4") << "2.0" << "3.0" << "3.0" << "needs to be lower than"; + + // "bad" because the "higer" module has stronger requirements. + QTest::newRow("bad5") << "0.1" << "0.9" << "0.5" << "Impossible version constraint"; +} + +void TestBlackbox::versionScript() +{ + Settings settings((QString())); + Profile buildProfile(profileName(), &settings); + QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList(); + if (!toolchain.contains("gcc") || targetOs() != HostOsInfo::HostOsLinux) + QSKIP("version script test only applies to Linux"); + QDir::setCurrent(testDataDir + "/versionscript"); + QCOMPARE(runQbs(QbsRunParameters(QStringList("-q") + << ("qbs.installRoot:" + QDir::currentPath()))), 0); + const QString output = QString::fromLocal8Bit(m_qbsStderr); + QRegExp pattern(".*---(.*)---.*"); + QVERIFY2(pattern.exactMatch(output), qPrintable(output)); + QCOMPARE(pattern.captureCount(), 1); + const QString nmPath = pattern.capturedTexts().at(1); + if (!QFile::exists(nmPath)) + QSKIP("Cannot check for symbol presence: No nm found."); + QProcess nm; + nm.start(nmPath, QStringList(QDir::currentPath() + "/libversionscript.so")); + QVERIFY(nm.waitForStarted()); + QVERIFY(nm.waitForFinished()); + const QByteArray allSymbols = nm.readAllStandardOutput(); + QCOMPARE(nm.exitCode(), 0); + QVERIFY2(allSymbols.contains("dummyLocal"), allSymbols.constData()); + QVERIFY2(allSymbols.contains("dummyGlobal"), allSymbols.constData()); + nm.start(nmPath, QStringList() << "-g" << QDir::currentPath() + "/libversionscript.so"); + QVERIFY(nm.waitForStarted()); + QVERIFY(nm.waitForFinished()); + const QByteArray globalSymbols = nm.readAllStandardOutput(); + QCOMPARE(nm.exitCode(), 0); + QVERIFY2(!globalSymbols.contains("dummyLocal"), allSymbols.constData()); + QVERIFY2(globalSymbols.contains("dummyGlobal"), allSymbols.constData()); +} + +static bool symlinkExists(const QString &linkFilePath) +{ + return QFileInfo(linkFilePath).isSymLink(); +} + +void TestBlackbox::clean() +{ + const QString appObjectFilePath = relativeProductBuildDir("app") + "/.obj/" + inputDirHash(".") + + objectFileName("/main.cpp", profileName()); + const QString appExeFilePath = relativeExecutableFilePath("app"); + const QString depObjectFilePath = relativeProductBuildDir("dep") + "/.obj/" + inputDirHash(".") + + objectFileName("/dep.cpp", profileName()); + const QString depLibBase = relativeProductBuildDir("dep") + + '/' + QBS_HOST_DYNAMICLIB_PREFIX + "dep"; + QString depLibFilePath; + QStringList symlinks; + if (qbs::Internal::HostOsInfo::isMacosHost()) { + depLibFilePath = depLibBase + ".1.1.0" + QBS_HOST_DYNAMICLIB_SUFFIX; + symlinks << depLibBase + ".1.1" + QBS_HOST_DYNAMICLIB_SUFFIX + << depLibBase + ".1" + QBS_HOST_DYNAMICLIB_SUFFIX + << depLibBase + QBS_HOST_DYNAMICLIB_SUFFIX; + } else if (qbs::Internal::HostOsInfo::isAnyUnixHost()) { + depLibFilePath = depLibBase + QBS_HOST_DYNAMICLIB_SUFFIX + ".1.1.0"; + symlinks << depLibBase + QBS_HOST_DYNAMICLIB_SUFFIX + ".1.1" + << depLibBase + QBS_HOST_DYNAMICLIB_SUFFIX + ".1" + << depLibBase + QBS_HOST_DYNAMICLIB_SUFFIX; + } else { + depLibFilePath = depLibBase + QBS_HOST_DYNAMICLIB_SUFFIX; + } + + QDir::setCurrent(testDataDir + "/clean"); + + // Default behavior: Remove all. + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(appObjectFilePath)); + QVERIFY(regularFileExists(appExeFilePath)); + QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"))), 0); + QVERIFY(!QFile(appObjectFilePath).exists()); + QVERIFY(!QFile(appExeFilePath).exists()); + QVERIFY(!QFile(depObjectFilePath).exists()); + QVERIFY(!QFile(depLibFilePath).exists()); + foreach (const QString &symLink, symlinks) + QVERIFY2(!symlinkExists(symLink), qPrintable(symLink)); + + // Remove all, with a forced re-resolve in between. + // This checks that rescuable artifacts are also removed. + QCOMPARE(runQbs(QbsRunParameters(QStringList() << "cpp.optimization:none")), 0); + QVERIFY(regularFileExists(appObjectFilePath)); + QVERIFY(regularFileExists(appExeFilePath)); + QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList() << "cpp.optimization:fast")), 0); + QVERIFY(regularFileExists(appObjectFilePath)); + QVERIFY(regularFileExists(appExeFilePath)); + QCOMPARE(runQbs(QbsRunParameters("clean")), 0); + QVERIFY(!QFile(appObjectFilePath).exists()); + QVERIFY(!QFile(appExeFilePath).exists()); + QVERIFY(!QFile(depObjectFilePath).exists()); + QVERIFY(!QFile(depLibFilePath).exists()); + foreach (const QString &symLink, symlinks) + QVERIFY2(!symlinkExists(symLink), qPrintable(symLink)); + + // Dry run. + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(appObjectFilePath)); + QVERIFY(regularFileExists(appExeFilePath)); + QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"), QStringList("-n"))), 0); + QVERIFY(regularFileExists(appObjectFilePath)); + QVERIFY(regularFileExists(appExeFilePath)); + QVERIFY(regularFileExists(depObjectFilePath)); + QVERIFY(regularFileExists(depLibFilePath)); + foreach (const QString &symLink, symlinks) + QVERIFY2(symlinkExists(symLink), qPrintable(symLink)); + + // Product-wise, dependency only. + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(appObjectFilePath)); + QVERIFY(regularFileExists(appExeFilePath)); + QVERIFY(regularFileExists(depObjectFilePath)); + QVERIFY(regularFileExists(depLibFilePath)); + QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"), QStringList("-p") << "dep")), 0); + QVERIFY(regularFileExists(appObjectFilePath)); + QVERIFY(regularFileExists(appExeFilePath)); + QVERIFY(!QFile(depObjectFilePath).exists()); + QVERIFY(!QFile(depLibFilePath).exists()); + foreach (const QString &symLink, symlinks) + QVERIFY2(!symlinkExists(symLink), qPrintable(symLink)); + + // Product-wise, dependent product only. + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(appObjectFilePath)); + QVERIFY(regularFileExists(appExeFilePath)); + QVERIFY(regularFileExists(depObjectFilePath)); + QVERIFY(regularFileExists(depLibFilePath)); + QCOMPARE(runQbs(QbsRunParameters(QLatin1String("clean"), QStringList("-p") << "app")), 0); + QVERIFY(!QFile(appObjectFilePath).exists()); + QVERIFY(!QFile(appExeFilePath).exists()); + QVERIFY(regularFileExists(depObjectFilePath)); + QVERIFY(regularFileExists(depLibFilePath)); + foreach (const QString &symLink, symlinks) + QVERIFY2(symlinkExists(symLink), qPrintable(symLink)); +} + +void TestBlackbox::concurrentExecutor() +{ + QDir::setCurrent(testDataDir + "/concurrent-executor"); + QCOMPARE(runQbs(QStringList() << "-j" << "2"), 0); + QVERIFY2(!m_qbsStderr.contains("ASSERT"), m_qbsStderr.constData()); +} + +void TestBlackbox::conditionalExport() +{ + QDir::setCurrent(testDataDir + "/conditional-export"); + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("missing define"), m_qbsStderr.constData()); + + params.expectFailure = false; + params.arguments << "project.enableExport:true"; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::conflictingArtifacts() +{ + QDir::setCurrent(testDataDir + "/conflicting-artifacts"); + QbsRunParameters params(QStringList() << "-n"); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("Conflicting artifacts"), m_qbsStderr.constData()); +} + +void TestBlackbox::dbusAdaptors() +{ + QDir::setCurrent(testDataDir + "/dbus-adaptors"); + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::dbusInterfaces() +{ + QDir::setCurrent(testDataDir + "/dbus-interfaces"); + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::renameDependency() +{ + QDir::setCurrent(testDataDir + "/renameDependency"); + if (QFile::exists("work")) + rmDirR("work"); + QDir().mkdir("work"); + ccp("before", "work"); + QDir::setCurrent(testDataDir + "/renameDependency/work"); + QCOMPARE(runQbs(), 0); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile::remove("lib.h"); + QFile::remove("lib.cpp"); + ccp("../after", "."); + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY(m_qbsStdout.contains("compiling main.cpp")); +} + +void TestBlackbox::separateDebugInfo() +{ + QDir::setCurrent(testDataDir + "/separate-debug-info"); + QCOMPARE(runQbs(), 0); + + Settings settings((QString())); + Profile buildProfile(profileName(), &settings); + QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList(); + QStringList targetOS = buildProfile.value("qbs.targetOS").toStringList(); + if (targetOS.contains("darwin") || (targetOS.isEmpty() && HostOsInfo::isMacosHost())) { + QVERIFY(directoryExists(relativeProductBuildDir("app1") + "/app1.app.dSYM")); + QVERIFY(regularFileExists(relativeProductBuildDir("app1") + + "/app1.app.dSYM/Contents/Info.plist")); + QVERIFY(regularFileExists(relativeProductBuildDir("app1") + + "/app1.app.dSYM/Contents/Resources/DWARF/app1")); + QCOMPARE(QDir(relativeProductBuildDir("app1") + + "/app1.app.dSYM/Contents/Resources/DWARF") + .entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries).size(), 1); + QVERIFY(!QFile::exists(relativeProductBuildDir("app2") + "/app2.app.dSYM")); + QVERIFY(!QFile::exists(relativeProductBuildDir("app3") + "/app3.app.dSYM")); + QVERIFY(regularFileExists(relativeProductBuildDir("app3") + + "/app3.app/Contents/MacOS/app3.dwarf")); + QVERIFY(directoryExists(relativeProductBuildDir("app4") + "/app4.dSYM")); + QVERIFY(regularFileExists(relativeProductBuildDir("app4") + + "/app4.dSYM/Contents/Info.plist")); + QVERIFY(regularFileExists(relativeProductBuildDir("app4") + + "/app4.dSYM/Contents/Resources/DWARF/app4")); + QCOMPARE(QDir(relativeProductBuildDir("app4") + + "/app4.dSYM/Contents/Resources/DWARF") + .entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries).size(), 1); + QVERIFY(regularFileExists(relativeProductBuildDir("app5") + "/app5.dwarf")); + QVERIFY(directoryExists(relativeProductBuildDir("foo1") + "/foo1.framework.dSYM")); + QVERIFY(regularFileExists(relativeProductBuildDir("foo1") + + "/foo1.framework.dSYM/Contents/Info.plist")); + QVERIFY(regularFileExists(relativeProductBuildDir("foo1") + + "/foo1.framework.dSYM/Contents/Resources/DWARF/foo1")); + QCOMPARE(QDir(relativeProductBuildDir("foo1") + + "/foo1.framework.dSYM/Contents/Resources/DWARF") + .entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries).size(), 1); + QVERIFY(!QFile::exists(relativeProductBuildDir("foo2") + "/foo2.framework.dSYM")); + QVERIFY(!QFile::exists(relativeProductBuildDir("foo3") + "/foo3.framework.dSYM")); + QVERIFY(regularFileExists(relativeProductBuildDir("foo3") + + "/foo3.framework/Versions/A/foo3.dwarf")); + QVERIFY(directoryExists(relativeProductBuildDir("foo4") + "/libfoo4.dylib.dSYM")); + QVERIFY(regularFileExists(relativeProductBuildDir("foo4") + + "/libfoo4.dylib.dSYM/Contents/Info.plist")); + QVERIFY(regularFileExists(relativeProductBuildDir("foo4") + + "/libfoo4.dylib.dSYM/Contents/Resources/DWARF/libfoo4.dylib")); + QCOMPARE(QDir(relativeProductBuildDir("foo4") + + "/libfoo4.dylib.dSYM/Contents/Resources/DWARF") + .entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries).size(), 1); + QVERIFY(regularFileExists(relativeProductBuildDir("foo5") + "/libfoo5.dylib.dwarf")); + QVERIFY(directoryExists(relativeProductBuildDir("bar1") + "/bar1.bundle.dSYM")); + QVERIFY(regularFileExists(relativeProductBuildDir("bar1") + + "/bar1.bundle.dSYM/Contents/Info.plist")); + QVERIFY(regularFileExists(relativeProductBuildDir("bar1") + + "/bar1.bundle.dSYM/Contents/Resources/DWARF/bar1")); + QCOMPARE(QDir(relativeProductBuildDir("bar1") + + "/bar1.bundle.dSYM/Contents/Resources/DWARF") + .entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries).size(), 1); + QVERIFY(!QFile::exists(relativeProductBuildDir("bar2") + "/bar2.bundle.dSYM")); + QVERIFY(!QFile::exists(relativeProductBuildDir("bar3") + "/bar3.bundle.dSYM")); + QVERIFY(regularFileExists(relativeProductBuildDir("bar3") + + "/bar3.bundle/Contents/MacOS/bar3.dwarf")); + QVERIFY(directoryExists(relativeProductBuildDir("bar4") + "/bar4.bundle.dSYM")); + QVERIFY(regularFileExists(relativeProductBuildDir("bar4") + + "/bar4.bundle.dSYM/Contents/Info.plist")); + QVERIFY(regularFileExists(relativeProductBuildDir("bar4") + + "/bar4.bundle.dSYM/Contents/Resources/DWARF/bar4.bundle")); + QCOMPARE(QDir(relativeProductBuildDir("bar4") + + "/bar4.bundle.dSYM/Contents/Resources/DWARF") + .entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries).size(), 1); + QVERIFY(regularFileExists(relativeProductBuildDir("bar5") + "/bar5.bundle.dwarf")); + } else if (toolchain.contains("gcc")) { + const QString exeSuffix = targetOS.contains("windows") ? ".exe" : ""; + const QString dllPrefix = targetOS.contains("windows") ? "" : "lib"; + const QString dllSuffix = targetOS.contains("windows") ? ".dll" : ".so"; + QVERIFY(QFile::exists(relativeProductBuildDir("app1") + "/app1" + exeSuffix + ".debug")); + QVERIFY(!QFile::exists(relativeProductBuildDir("app2") + "/app2" + exeSuffix + ".debug")); + QVERIFY(QFile::exists(relativeProductBuildDir("foo1") + + '/' + dllPrefix + "foo1" + dllSuffix + ".debug")); + QVERIFY(!QFile::exists(relativeProductBuildDir("foo2") + + '/' + "foo2" + dllSuffix + ".debug")); + QVERIFY(QFile::exists(relativeProductBuildDir("bar1") + + '/' + dllPrefix + "bar1" + dllSuffix + ".debug")); + QVERIFY(!QFile::exists(relativeProductBuildDir("bar2") + + '/' + dllPrefix + "bar2" + dllSuffix + ".debug")); + } else if (toolchain.contains("msvc")) { + QVERIFY(QFile::exists(relativeProductBuildDir("app1") + "/app1.pdb")); + QVERIFY(QFile::exists(relativeProductBuildDir("foo1") + "/foo1.pdb")); + QVERIFY(QFile::exists(relativeProductBuildDir("bar1") + "/bar1.pdb")); + // MSVC's linker even creates a pdb file if /Z7 is passed to the compiler. + } else { + QSKIP("Unsupported toolchain. Skipping."); + } +} + +void TestBlackbox::track_qrc() +{ + QDir::setCurrent(testDataDir + "/qrc"); + QCOMPARE(runQbs(), 0); + const QString fileName = relativeExecutableFilePath("i"); + QVERIFY2(regularFileExists(fileName), qPrintable(fileName)); + QDateTime dt = QFileInfo(fileName).lastModified(); + WAIT_FOR_NEW_TIMESTAMP(); + { + QFile f("stuff.txt"); + f.remove(); + QVERIFY(f.open(QFile::WriteOnly)); + f.write("bla"); + f.close(); + } + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(fileName)); + QVERIFY(dt < QFileInfo(fileName).lastModified()); +} + +void TestBlackbox::track_qobject_change() +{ + QDir::setCurrent(testDataDir + "/trackQObjChange"); + copyFileAndUpdateTimestamp("bla_qobject.h", "bla.h"); + QCOMPARE(runQbs(), 0); + const QString productFilePath = relativeExecutableFilePath("i"); + QVERIFY2(regularFileExists(productFilePath), qPrintable(productFilePath)); + QString moc_bla_objectFileName = relativeProductBuildDir("i") + "/.obj/" + + inputDirHash("qt.headers") + objectFileName("/moc_bla.cpp", profileName()); + QVERIFY2(regularFileExists(moc_bla_objectFileName), qPrintable(moc_bla_objectFileName)); + + WAIT_FOR_NEW_TIMESTAMP(); + copyFileAndUpdateTimestamp("bla_noqobject.h", "bla.h"); + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(productFilePath)); + QVERIFY(!QFile(moc_bla_objectFileName).exists()); +} + +void TestBlackbox::trackAddFile() +{ + QProcess process; + QList output; + QDir::setCurrent(testDataDir + "/trackAddFile"); + if (QFile::exists("work")) + rmDirR("work"); + QDir().mkdir("work"); + ccp("before", "work"); + QDir::setCurrent(testDataDir + "/trackAddFile/work"); + QCOMPARE(runQbs(), 0); + + process.start(relativeExecutableFilePath("someapp"), QStringList()); + QVERIFY2(process.waitForStarted(), qPrintable(process.errorString())); + QVERIFY2(process.waitForFinished(), qPrintable(process.errorString())); + QCOMPARE(process.exitCode(), 0); + output = process.readAllStandardOutput().split('\n'); + QCOMPARE(output.takeFirst().trimmed().constData(), "Hello World!"); + QCOMPARE(output.takeFirst().trimmed().constData(), "NARF!"); + QString unchangedObjectFile = relativeBuildDir() + + objectFileName("/someapp/narf.cpp", profileName()); + QDateTime unchangedObjectFileTime1 = QFileInfo(unchangedObjectFile).lastModified(); + + WAIT_FOR_NEW_TIMESTAMP(); + ccp("../after", "."); + touch("project.qbs"); + touch("main.cpp"); + QCOMPARE(runQbs(), 0); + + process.start(relativeExecutableFilePath("someapp"), QStringList()); + QVERIFY(process.waitForStarted()); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitCode(), 0); + output = process.readAllStandardOutput().split('\n'); + QCOMPARE(output.takeFirst().trimmed().constData(), "Hello World!"); + QCOMPARE(output.takeFirst().trimmed().constData(), "NARF!"); + QCOMPARE(output.takeFirst().trimmed().constData(), "ZORT!"); + + // the object file of the untouched source should not have changed + QDateTime unchangedObjectFileTime2 = QFileInfo(unchangedObjectFile).lastModified(); + QCOMPARE(unchangedObjectFileTime1, unchangedObjectFileTime2); +} + +void TestBlackbox::trackExternalProductChanges() +{ + QDir::setCurrent(testDataDir + "/trackExternalProductChanges"); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("compiling main.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling environmentChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling jsFileChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling fileExists.cpp")); + + QbsRunParameters params; + params.environment.insert("QBS_TEST_PULL_IN_FILE_VIA_ENV", "1"); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compiling main.cpp")); + QVERIFY(m_qbsStdout.contains("compiling environmentChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling jsFileChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling fileExists.cpp")); + + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("compiling main.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling environmentChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling jsFileChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling fileExists.cpp")); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile jsFile("fileList.js"); + QVERIFY(jsFile.open(QIODevice::ReadWrite)); + QByteArray jsCode = jsFile.readAll(); + jsCode.replace("return []", "return ['jsFileChange.cpp']"); + jsFile.resize(0); + jsFile.write(jsCode); + jsFile.close(); + QCOMPARE(runQbs(), 0); + QVERIFY(!m_qbsStdout.contains("compiling main.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling environmentChange.cpp")); + QVERIFY(m_qbsStdout.contains("compiling jsFileChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling fileExists.cpp")); + + rmDirR(relativeBuildDir()); + QVERIFY(jsFile.open(QIODevice::ReadWrite)); + jsCode = jsFile.readAll(); + jsCode.replace("['jsFileChange.cpp']", "[]"); + jsFile.resize(0); + jsFile.write(jsCode); + jsFile.close(); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("compiling main.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling environmentChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling jsFileChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling fileExists.cpp")); + + QFile cppFile("fileExists.cpp"); + QVERIFY(cppFile.open(QIODevice::WriteOnly)); + cppFile.write("void fileExists() { }\n"); + cppFile.close(); + QCOMPARE(runQbs(), 0); + QVERIFY(!m_qbsStdout.contains("compiling main.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling environmentChange.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling jsFileChange.cpp")); + QVERIFY(m_qbsStdout.contains("compiling fileExists.cpp")); + + rmDirR(relativeBuildDir()); + Settings s((QString())); + const Profile profile(profileName(), &s); + const QStringList toolchainTypes = profile.value("qbs.toolchain").toStringList(); + if (!toolchainTypes.contains("gcc")) + QSKIP("Need GCC-like compiler to run this test"); + params.environment = QProcessEnvironment::systemEnvironment(); + params.environment.insert("INCLUDE_PATH_TEST", "1"); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("hiddenheaderqbs.h"), m_qbsStderr.constData()); + params.environment.insert("CPLUS_INCLUDE_PATH", + QDir::toNativeSeparators(QDir::currentPath() + "/hidden")); + params.expectFailure = false; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::trackGroupConditionChange() +{ + QbsRunParameters params; + params.expectFailure = true; + QDir::setCurrent(testDataDir + "/group-condition-change"); + QVERIFY(runQbs(params) != 0); + QVERIFY(m_qbsStderr.contains("jibbetnich")); + + params.arguments = QStringList("project.kaputt:false"); + params.expectFailure = false; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::trackRemoveFile() +{ + QProcess process; + QList output; + QDir::setCurrent(testDataDir + "/trackAddFile"); + if (QFile::exists("work")) + rmDirR("work"); + QDir().mkdir("work"); + ccp("before", "work"); + ccp("after", "work"); + QDir::setCurrent(testDataDir + "/trackAddFile/work"); + QCOMPARE(runQbs(), 0); + + process.start(relativeExecutableFilePath("someapp"), QStringList()); + QVERIFY2(process.waitForStarted(), qPrintable(process.errorString())); + QVERIFY2(process.waitForFinished(), qPrintable(process.errorString())); + QCOMPARE(process.exitCode(), 0); + output = process.readAllStandardOutput().split('\n'); + QCOMPARE(output.takeFirst().trimmed().constData(), "Hello World!"); + QCOMPARE(output.takeFirst().trimmed().constData(), "NARF!"); + QCOMPARE(output.takeFirst().trimmed().constData(), "ZORT!"); + QString unchangedObjectFile = relativeBuildDir() + + objectFileName("/someapp/narf.cpp", profileName()); + QDateTime unchangedObjectFileTime1 = QFileInfo(unchangedObjectFile).lastModified(); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile::remove("project.qbs"); + QFile::remove("main.cpp"); + QFile::copy("../before/project.qbs", "project.qbs"); + QFile::copy("../before/main.cpp", "main.cpp"); + QVERIFY(QFile::remove("zort.h")); + QVERIFY(QFile::remove("zort.cpp")); + QCOMPARE(runQbs(QbsRunParameters(QLatin1String("resolve"))), 0); + + touch("main.cpp"); + touch("project.qbs"); + QCOMPARE(runQbs(), 0); + + process.start(relativeExecutableFilePath("someapp"), QStringList()); + QVERIFY(process.waitForStarted()); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitCode(), 0); + output = process.readAllStandardOutput().split('\n'); + QCOMPARE(output.takeFirst().trimmed().constData(), "Hello World!"); + QCOMPARE(output.takeFirst().trimmed().constData(), "NARF!"); + + // the object file of the untouched source should not have changed + QDateTime unchangedObjectFileTime2 = QFileInfo(unchangedObjectFile).lastModified(); + QCOMPARE(unchangedObjectFileTime1, unchangedObjectFileTime2); + + // the object file for the removed cpp file should have vanished too + QVERIFY(!regularFileExists(relativeBuildDir() + + objectFileName("/someapp/zort.cpp", profileName()))); +} + +void TestBlackbox::trackAddFileTag() +{ + QProcess process; + QList output; + QDir::setCurrent(testDataDir + "/trackFileTags"); + if (QFile::exists("work")) + rmDirR("work"); + QDir().mkdir("work"); + ccp("before", "work"); + QDir::setCurrent(testDataDir + "/trackFileTags/work"); + QCOMPARE(runQbs(), 0); + + process.start(relativeExecutableFilePath("someapp"), QStringList()); + QVERIFY2(process.waitForStarted(), qPrintable(process.errorString())); + QVERIFY2(process.waitForFinished(), qPrintable(process.errorString())); + QCOMPARE(process.exitCode(), 0); + output = process.readAllStandardOutput().split('\n'); + QCOMPARE(output.takeFirst().trimmed().constData(), "there's no foo here"); + + WAIT_FOR_NEW_TIMESTAMP(); + ccp("../after", "."); + touch("main.cpp"); + touch("project.qbs"); + QCOMPARE(runQbs(), 0); + + process.start(relativeExecutableFilePath("someapp"), QStringList()); + QVERIFY(process.waitForStarted()); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitCode(), 0); + output = process.readAllStandardOutput().split('\n'); + QCOMPARE(output.takeFirst().trimmed().constData(), "there's 15 foo here"); +} + +void TestBlackbox::trackRemoveFileTag() +{ + QProcess process; + QList output; + QDir::setCurrent(testDataDir + "/trackFileTags"); + if (QFile::exists("work")) + rmDirR("work"); + QDir().mkdir("work"); + ccp("after", "work"); + QDir::setCurrent(testDataDir + "/trackFileTags/work"); + QCOMPARE(runQbs(), 0); + + // check if the artifacts are here that will become stale in the 2nd step + QVERIFY(regularFileExists(relativeProductBuildDir("someapp") + "/.obj/" + inputDirHash(".") + + objectFileName("/main_foo.cpp", profileName()))); + QVERIFY(regularFileExists(relativeProductBuildDir("someapp") + "/main_foo.cpp")); + QVERIFY(regularFileExists(relativeProductBuildDir("someapp") + "/main.foo")); + + process.start(relativeExecutableFilePath("someapp"), QStringList()); + QVERIFY(process.waitForStarted()); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitCode(), 0); + output = process.readAllStandardOutput().split('\n'); + QCOMPARE(output.takeFirst().trimmed().constData(), "there's 15 foo here"); + + WAIT_FOR_NEW_TIMESTAMP(); + ccp("../before", "."); + touch("main.cpp"); + touch("project.qbs"); + QCOMPARE(runQbs(), 0); + + process.start(relativeExecutableFilePath("someapp"), QStringList()); + QVERIFY(process.waitForStarted()); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitCode(), 0); + output = process.readAllStandardOutput().split('\n'); + QCOMPARE(output.takeFirst().trimmed().constData(), "there's no foo here"); + + // check if stale artifacts have been removed + QCOMPARE(regularFileExists(relativeProductBuildDir("someapp") + "/.obj/" + inputDirHash(".") + + objectFileName("/main_foo.cpp", profileName())), false); + QCOMPARE(regularFileExists(relativeProductBuildDir("someapp") + "/main_foo.cpp"), false); + QCOMPARE(regularFileExists(relativeProductBuildDir("someapp") + "/main.foo"), false); +} + +void TestBlackbox::trackAddMocInclude() +{ + QDir::setCurrent(testDataDir + "/trackAddMocInclude"); + if (QFile::exists("work")) + rmDirR("work"); + QDir().mkdir("work"); + ccp("before", "work"); + QDir::setCurrent(testDataDir + "/trackAddMocInclude/work"); + // The build must fail because the main.moc include is missing. + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + + WAIT_FOR_NEW_TIMESTAMP(); + ccp("../after", "."); + touch("main.cpp"); + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::trackAddProduct() +{ + QDir::setCurrent(testDataDir + "/trackProducts"); + if (QFile::exists("work")) + rmDirR("work"); + QDir().mkdir("work"); + ccp("before", "work"); + QDir::setCurrent(testDataDir + "/trackProducts/work"); + QbsRunParameters params(QStringList() << "-f" << "trackProducts.qbs"); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling foo.cpp")); + QVERIFY(m_qbsStdout.contains("compiling bar.cpp")); + QVERIFY(m_qbsStdout.contains("linking product1")); + QVERIFY(m_qbsStdout.contains("linking product2")); + + WAIT_FOR_NEW_TIMESTAMP(); + ccp("../after", "."); + touch("trackProducts.qbs"); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling zoo.cpp")); + QVERIFY(m_qbsStdout.contains("linking product3")); + QVERIFY(!m_qbsStdout.contains("compiling foo.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling bar.cpp")); + QVERIFY(!m_qbsStdout.contains("linking product1")); + QVERIFY(!m_qbsStdout.contains("linking product2")); +} + +void TestBlackbox::trackRemoveProduct() +{ + QDir::setCurrent(testDataDir + "/trackProducts"); + if (QFile::exists("work")) + rmDirR("work"); + QDir().mkdir("work"); + ccp("before", "work"); + ccp("after", "work"); + QDir::setCurrent(testDataDir + "/trackProducts/work"); + QbsRunParameters params(QStringList() << "-f" << "trackProducts.qbs"); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling foo.cpp")); + QVERIFY(m_qbsStdout.contains("compiling bar.cpp")); + QVERIFY(m_qbsStdout.contains("compiling zoo.cpp")); + QVERIFY(m_qbsStdout.contains("linking product1")); + QVERIFY(m_qbsStdout.contains("linking product2")); + QVERIFY(m_qbsStdout.contains("linking product3")); + + WAIT_FOR_NEW_TIMESTAMP(); + QFile::remove("zoo.cpp"); + QFile::remove("product3.qbs"); + copyFileAndUpdateTimestamp("../before/trackProducts.qbs", "trackProducts.qbs"); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compiling foo.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling bar.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling zoo.cpp")); + QVERIFY(!m_qbsStdout.contains("linking product1")); + QVERIFY(!m_qbsStdout.contains("linking product2")); + QVERIFY(!m_qbsStdout.contains("linking product3")); +} + +void TestBlackbox::wildcardRenaming() +{ + QDir::setCurrent(testDataDir + "/wildcard_renaming"); + QCOMPARE(runQbs(QbsRunParameters("install")), 0); + QVERIFY(QFileInfo(defaultInstallRoot + "/pioniere.txt").exists()); + QFile::rename(QDir::currentPath() + "/pioniere.txt", QDir::currentPath() + "/fdj.txt"); + QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"), + QStringList("--clean-install-root"))), 0); + QVERIFY(!QFileInfo(defaultInstallRoot + "/pioniere.txt").exists()); + QVERIFY(QFileInfo(defaultInstallRoot + "/fdj.txt").exists()); +} + +void TestBlackbox::recursiveRenaming() +{ + QDir::setCurrent(testDataDir + "/recursive_renaming"); + QCOMPARE(runQbs(QbsRunParameters("install")), 0); + QVERIFY(QFileInfo(defaultInstallRoot + "/dir/wasser.txt").exists()); + QVERIFY(QFileInfo(defaultInstallRoot + "/dir/subdir/blubb.txt").exists()); + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(QFile::rename(QDir::currentPath() + "/dir/wasser.txt", QDir::currentPath() + "/dir/wein.txt")); + QCOMPARE(runQbs(QbsRunParameters(QLatin1String("install"), + QStringList("--clean-install-root"))), 0); + QVERIFY(!QFileInfo(defaultInstallRoot + "/dir/wasser.txt").exists()); + QVERIFY(QFileInfo(defaultInstallRoot + "/dir/wein.txt").exists()); + QVERIFY(QFileInfo(defaultInstallRoot + "/dir/subdir/blubb.txt").exists()); +} + +void TestBlackbox::recursiveWildcards() +{ + QDir::setCurrent(testDataDir + "/recursive_wildcards"); + QCOMPARE(runQbs(QbsRunParameters("install")), 0); + QVERIFY(QFileInfo(defaultInstallRoot + "/dir/file1.txt").exists()); + QVERIFY(QFileInfo(defaultInstallRoot + "/dir/file2.txt").exists()); +} + +void TestBlackbox::referenceErrorInExport() +{ + QDir::setCurrent(testDataDir + "/referenceErrorInExport"); + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QEXPECT_FAIL(0, "QBS-946", Abort); + QVERIFY(m_qbsStderr.contains( + "project.qbs:17:31 ReferenceError: Can't find variable: includePaths")); +} + +void TestBlackbox::reproducibleBuild() +{ + Settings s((QString())); + const Profile profile(profileName(), &s); + const QStringList toolchains = profile.value("qbs.toolchain").toStringList(); + if (!toolchains.contains("gcc") || toolchains.contains("clang")) + QSKIP("reproducible builds only supported for gcc"); + + QFETCH(bool, reproducible); + + QDir::setCurrent(testDataDir + "/reproducible-build"); + QbsRunParameters params; + params.arguments << QString("cpp.enableReproducibleBuilds:") + + (reproducible ? "true" : "false"); + QCOMPARE(runQbs(params), 0); + QFile object(relativeProductBuildDir("the product") + "/.obj/" + inputDirHash(".") + '/' + + objectFileName("file1.cpp", profileName())); + QVERIFY2(object.open(QIODevice::ReadOnly), qPrintable(object.fileName())); + const QByteArray oldContents = object.readAll(); + object.close(); + QbsRunParameters cleanParams = params; + cleanParams.command = "clean"; + QCOMPARE(runQbs(cleanParams), 0); + QVERIFY(!object.exists()); + QCOMPARE(runQbs(params), 0); + QVERIFY(object.open(QIODevice::ReadOnly)); + const QByteArray newContents = object.readAll(); + QCOMPARE(oldContents == newContents, reproducible); + object.close(); + QCOMPARE(runQbs(cleanParams), 0); +} + +void TestBlackbox::reproducibleBuild_data() +{ + QTest::addColumn("reproducible"); + QTest::newRow("non-reproducible build") << false; + QTest::newRow("reproducible build") << true; +} + +void TestBlackbox::responseFiles() +{ + QDir::setCurrent(testDataDir + "/response-files"); + QbsRunParameters params; + params.command = "install"; + params.arguments << "--install-root" << "installed"; + QCOMPARE(runQbs(params), 0); + QFile file("installed/response-file-content.txt"); + QVERIFY(file.open(QIODevice::ReadOnly)); + const QList expected = QList() + << "foo" << qbs::Internal::shellQuote("with space").toUtf8() << "bar" << ""; + QList lines = file.readAll().split('\n'); + for (int i = 0; i < lines.count(); ++i) + lines[i] = lines.at(i).trimmed(); + QCOMPARE(lines, expected); +} + +void TestBlackbox::ruleConditions() +{ + QDir::setCurrent(testDataDir + "/ruleConditions"); + QCOMPARE(runQbs(), 0); + QVERIFY(QFileInfo(relativeExecutableFilePath("zorted")).exists()); + QVERIFY(QFileInfo(relativeExecutableFilePath("unzorted")).exists()); + QVERIFY(QFileInfo(relativeProductBuildDir("zorted") + "/zorted.foo.narf.zort").exists()); + QVERIFY(!QFileInfo(relativeProductBuildDir("unzorted") + "/unzorted.foo.narf.zort").exists()); +} + +void TestBlackbox::ruleCycle() +{ + QDir::setCurrent(testDataDir + "/ruleCycle"); + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY(m_qbsStderr.contains("Cycle detected in rule dependencies")); +} + +void TestBlackbox::ruleWithNoInputs() +{ + QDir::setCurrent(testDataDir + "/rule-with-no-inputs"); + QVERIFY2(runQbs() == 0, m_qbsStderr.constData()); + QVERIFY2(m_qbsStdout.contains("creating output"), m_qbsStdout.constData()); + QVERIFY2(runQbs() == 0, m_qbsStderr.constData()); + QVERIFY2(!m_qbsStdout.contains("creating output"), m_qbsStdout.constData()); + QbsRunParameters params(QStringList() << "theProduct.version:1"); + QVERIFY2(runQbs(params) == 0, m_qbsStderr.constData()); + QVERIFY2(!m_qbsStdout.contains("creating output"), m_qbsStdout.constData()); + params.arguments = QStringList() << "theProduct.version:2"; + QVERIFY2(runQbs(params) == 0, m_qbsStderr.constData()); + QVERIFY2(m_qbsStdout.contains("creating output"), m_qbsStdout.constData()); +} + + +static QString soName(const QString &readElfPath, const QString &libFilePath) +{ + QProcess readElf; + readElf.start(readElfPath, QStringList() << "-a" << libFilePath); + if (!readElf.waitForStarted() || !readElf.waitForFinished() || readElf.exitCode() != 0) { + qDebug() << readElf.errorString() << readElf.readAllStandardError(); + return QString(); + } + const QByteArray output = readElf.readAllStandardOutput(); + const QByteArray magicString = "Library soname: ["; + const int magicStringIndex = output.indexOf(magicString); + if (magicStringIndex == -1) + return QString(); + const int endIndex = output.indexOf(']', magicStringIndex); + if (endIndex == -1) + return QString(); + const int nameIndex = magicStringIndex + magicString.count(); + const QByteArray theName = output.mid(nameIndex, endIndex - nameIndex); + return QString::fromLatin1(theName); +} + +void TestBlackbox::soVersion() +{ + const QString readElfPath = findExecutable(QStringList("readelf")); + if (readElfPath.isEmpty() || readElfPath.endsWith("exe")) + QSKIP("soversion test not applicable on this system"); + QDir::setCurrent(testDataDir + "/soversion"); + + QFETCH(QString, soVersion); + QFETCH(bool, useVersion); + QFETCH(QString, expectedSoName); + + QbsRunParameters params; + params.arguments << ("mylib.useVersion:" + QString((useVersion ? "true" : "false"))); + if (!soVersion.isNull()) + params.arguments << ("cpp.soVersion:" + soVersion); + const QString libFilePath = relativeProductBuildDir("mylib") + "/libmylib.so" + + (useVersion ? ".1.2.3" : QString()); + QCOMPARE(runQbs(params), 0); + QVERIFY2(regularFileExists(libFilePath), qPrintable(libFilePath)); + QCOMPARE(soName(readElfPath, libFilePath), expectedSoName); +} + +void TestBlackbox::soVersion_data() +{ + QTest::addColumn("soVersion"); + QTest::addColumn("useVersion"); + QTest::addColumn("expectedSoName"); + + QTest::newRow("default") << QString() << true << QString("libmylib.so.1"); + QTest::newRow("explicit soVersion") << QString("1.2") << true << QString("libmylib.so.1.2"); + QTest::newRow("empty soVersion") << QString("") << true << QString("libmylib.so.1.2.3"); + QTest::newRow("no version, explicit soVersion") << QString("5") << false + << QString("libmylib.so.5"); + QTest::newRow("no version, default soVersion") << QString() << false << QString("libmylib.so"); + QTest::newRow("no version, empty soVersion") << QString("") << false << QString("libmylib.so"); +} + +void TestBlackbox::overrideProjectProperties() +{ + QDir::setCurrent(testDataDir + "/overrideProjectProperties"); + QCOMPARE(runQbs(QbsRunParameters(QStringList() + << QLatin1String("-f") + << QLatin1String("project.qbs") + << QLatin1String("project.nameSuffix:ForYou") + << QLatin1String("project.someBool:false") + << QLatin1String("project.someInt:156") + << QLatin1String("project.someStringList:one") + << QLatin1String("MyAppForYou.mainFile:main.cpp"))), 0); + QVERIFY(regularFileExists(relativeExecutableFilePath("MyAppForYou"))); + QVERIFY(QFile::remove(relativeBuildGraphFilePath())); + QbsRunParameters params; + params.arguments << QLatin1String("-f") << QLatin1String("project_using_helper_lib.qbs"); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + + rmDirR(relativeBuildDir()); + params.arguments = QStringList() << QLatin1String("-f") + << QLatin1String("project_using_helper_lib.qbs") + << QLatin1String("project.linkSuccessfully:true"); + params.expectFailure = false; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::pchChangeTracking() +{ + QDir::setCurrent(testDataDir + "/pch-change-tracking"); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("compiling pch.h")); + WAIT_FOR_NEW_TIMESTAMP(); + touch("header1.h"); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("compiling pch.h")); + WAIT_FOR_NEW_TIMESTAMP(); + touch("header2.h"); + QCOMPARE(runQbs(), 0); + QVERIFY2(!m_qbsStdout.contains("compiling pch.h"), m_qbsStdout.constData()); +} + +void TestBlackbox::pkgConfigProbe() +{ + const QString exe = findExecutable(QStringList() << "pkg-config"); + if (exe.isEmpty()) + QSKIP("This test requires the pkg-config tool"); + + QDir::setCurrent(testDataDir + "/pkg-config-probe"); + + QFETCH(QString, packageBaseName); + QFETCH(QStringList, found); + QFETCH(QStringList, libs); + QFETCH(QStringList, cflags); + QFETCH(QStringList, version); + + QbsRunParameters params(QStringList() << ("project.packageBaseName:" + packageBaseName)); + QCOMPARE(runQbs(params), 0); + const QString stdOut = m_qbsStdout; + QVERIFY2(stdOut.contains("theProduct1 found: " + found.at(0)), m_qbsStdout.constData()); + QVERIFY2(stdOut.contains("theProduct2 found: " + found.at(1)), m_qbsStdout.constData()); + QVERIFY2(stdOut.contains("theProduct1 libs: " + libs.at(0)), m_qbsStdout.constData()); + QVERIFY2(stdOut.contains("theProduct2 libs: " + libs.at(1)), m_qbsStdout.constData()); + QVERIFY2(stdOut.contains("theProduct1 cflags: " + cflags.at(0)), m_qbsStdout.constData()); + QVERIFY2(stdOut.contains("theProduct2 cflags: " + cflags.at(1)), m_qbsStdout.constData()); + QVERIFY2(stdOut.contains("theProduct1 version: " + version.at(0)), m_qbsStdout.constData()); + QVERIFY2(stdOut.contains("theProduct2 version: " + version.at(1)), m_qbsStdout.constData()); +} + +void TestBlackbox::pkgConfigProbe_data() +{ + QTest::addColumn("packageBaseName"); + QTest::addColumn("found"); + QTest::addColumn("libs"); + QTest::addColumn("cflags"); + QTest::addColumn("version"); + + QTest::newRow("existing package") + << "dummy" << (QStringList() << "true" << "true") + << (QStringList() << "[\"-Ldummydir1\",\"-ldummy1\"]" + << "[\"-Ldummydir2\",\"-ldummy2\"]") + << (QStringList() << "[]" << "[]") << (QStringList() << "0.0.1" << "0.0.2"); + + // Note: The array values should be "undefined", but we lose that information when + // converting to QVariants in the ProjectResolver. + QTest::newRow("non-existing package") + << "blubb" << (QStringList() << "false" << "false") << (QStringList() << "[]" << "[]") + << (QStringList() << "[]" << "[]") << (QStringList() << "undefined" << "undefined"); +} + +void TestBlackbox::pkgConfigProbeSysroot() +{ + const QString exe = findExecutable(QStringList() << "pkg-config"); + if (exe.isEmpty()) + QSKIP("This test requires the pkg-config tool"); + + QDir::setCurrent(testDataDir + "/pkg-config-probe-sysroot"); + QCOMPARE(runQbs(), 0); + QCOMPARE(m_qbsStdout.count("PkgConfigProbe: found library"), 2); + const QString outputTemplate = "theProduct%1 libs: [\"-L%2/usr/dummy\",\"-ldummy1\"]"; + QVERIFY2(m_qbsStdout.contains(outputTemplate + .arg("1", QDir::currentPath() + "/sysroot1").toLocal8Bit()), + m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains(outputTemplate + .arg("2", QDir::currentPath() + "/sysroot2").toLocal8Bit()), + m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains(outputTemplate + .arg("3", QDir::currentPath() + "/sysroot1").toLocal8Bit()), + m_qbsStdout.constData()); +} + +void TestBlackbox::pluginMetaData() +{ + QDir::setCurrent(testDataDir + "/plugin-meta-data"); + QCOMPARE(runQbs(), 0); + const QString appFilePath = relativeBuildDir() + "/install-root/" + + qbs::Internal::HostOsInfo::appendExecutableSuffix("plugin-meta-data"); + QVERIFY(regularFileExists(appFilePath)); + QProcess app; + app.start(appFilePath); + QVERIFY(app.waitForStarted()); + QVERIFY(app.waitForFinished()); + QVERIFY2(app.exitCode() == 0, app.readAllStandardError().constData()); +} + +void TestBlackbox::probeChangeTracking() +{ + QDir::setCurrent(testDataDir + "/probe-change-tracking"); + + // Probe disabled. + QbsRunParameters params; + params.arguments = QStringList("theProduct.runProbe:false"); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("running probe")); + + // Probe newly enabled. + params.arguments = QStringList("theProduct.runProbe:true"); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("running probe")); + + // Re-resolving with unchanged probe. + WAIT_FOR_NEW_TIMESTAMP(); + QFile projectFile("probe-change-tracking.qbs"); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + QByteArray content = projectFile.readAll(); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("Resolving")); + QVERIFY(!m_qbsStdout.contains("running probe")); + + // Re-resolving with changed configure script. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + content = projectFile.readAll(); + content.replace("console.info", " console.info"); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("Resolving")); + QVERIFY(m_qbsStdout.contains("running probe")); + + // Re-resolving with added property. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + content = projectFile.readAll(); + content.replace("condition: product.runProbe", + "condition: product.runProbe\nproperty string something: 'x'"); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("Resolving")); + QVERIFY(m_qbsStdout.contains("running probe")); + + // Re-resolving with changed property. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + content = projectFile.readAll(); + content.replace("'x'", "'y'"); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("Resolving")); + QVERIFY(m_qbsStdout.contains("running probe")); + + // Re-resolving with removed property. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + content = projectFile.readAll(); + content.replace("property string something: 'y'", ""); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("Resolving")); + QVERIFY(m_qbsStdout.contains("running probe")); + + // Re-resolving with unchanged probe again. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + content = projectFile.readAll(); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("Resolving")); + QVERIFY(!m_qbsStdout.contains("running probe")); + + // Enforcing re-running via command-line option. + params.arguments.prepend("--force-probe-execution"); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("Resolving")); + QVERIFY(m_qbsStdout.contains("running probe")); +} + +void TestBlackbox::probeProperties() +{ + QDir::setCurrent(testDataDir + "/probeProperties"); + const QByteArray dir = QDir::cleanPath(testDataDir).toLatin1() + "/probeProperties"; + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("probe1.fileName=bin/tool"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("probe1.path=" + dir), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("probe1.filePath=" + dir + "/bin/tool"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("probe2.fileName=tool"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("probe2.path=" + dir + "/bin"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("probe2.filePath=" + dir + "/bin/tool"), m_qbsStdout.constData()); +} + +void TestBlackbox::probeInExportedModule() +{ + QDir::setCurrent(testDataDir + "/probe-in-exported-module"); + QCOMPARE(runQbs(QbsRunParameters(QStringList() << QLatin1String("-f") + << QLatin1String("probe-in-exported-module.qbs"))), 0); + QVERIFY2(m_qbsStdout.contains("found: true"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("prop: yes"), m_qbsStdout.constData()); + QVERIFY2(m_qbsStdout.contains("listProp: my,myother"), m_qbsStdout.constData()); +} + +void TestBlackbox::probesAndArrayProperties() +{ + QDir::setCurrent(testDataDir + "/probes-and-array-properties"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("prop: [\"probe\"]"), m_qbsStdout.constData()); + WAIT_FOR_NEW_TIMESTAMP(); + QFile projectFile("probes-and-array-properties.qbs"); + QVERIFY2(projectFile.open(QIODevice::ReadWrite), qPrintable(projectFile.errorString())); + QByteArray content = projectFile.readAll(); + content.replace("//", ""); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("prop: [\"product\",\"probe\"]"), m_qbsStdout.constData()); +} + +void TestBlackbox::productProperties() +{ + QDir::setCurrent(testDataDir + "/productproperties"); + QCOMPARE(runQbs(QbsRunParameters(QStringList() << QLatin1String("-f") + << QLatin1String("project.qbs"))), 0); + QVERIFY(regularFileExists(relativeExecutableFilePath("blubb_user"))); +} + +void TestBlackbox::propertyChanges() +{ + QDir::setCurrent(testDataDir + "/propertyChanges"); + QFile projectFile("project.qbs"); + QbsRunParameters params(QStringList() << "-f" << "project.qbs"); + + // Initial build. + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(m_qbsStdout.contains("compiling lib.cpp")); + QVERIFY(m_qbsStdout.contains("linking product 1.debug")); + QVERIFY(m_qbsStdout.contains("generated.txt")); + QVERIFY(m_qbsStdout.contains("Making output from input")); + QVERIFY(m_qbsStdout.contains("default value")); + QVERIFY(m_qbsStdout.contains("Making output from other output")); + QFile generatedFile(relativeProductBuildDir("generated text file") + "/generated.txt"); + QVERIFY(generatedFile.open(QIODevice::ReadOnly)); + QCOMPARE(generatedFile.readAll(), QByteArray("prefix 1contents 1suffix 1")); + generatedFile.close(); + + // Incremental build with no changes. + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling lib.cpp.cpp")); + QVERIFY(!m_qbsStdout.contains("linking")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + + // Incremental build with no changes, but updated project file timestamp. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(projectFile.open(QIODevice::ReadWrite | QIODevice::Append)); + projectFile.write("\n"); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling lib.cpp")); + QVERIFY(!m_qbsStdout.contains("linking")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + + // Incremental build, input property changed for first product + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + QByteArray contents = projectFile.readAll(); + contents.replace("blubb1", "blubb01"); + projectFile.resize(0); + projectFile.write(contents); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(m_qbsStdout.contains("linking product 1.debug")); + QVERIFY(!m_qbsStdout.contains("linking product 2")); + QVERIFY(!m_qbsStdout.contains("linking product 3")); + QVERIFY(!m_qbsStdout.contains("linking library")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + + // Incremental build, input property changed via project for second product. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + contents = projectFile.readAll(); + contents.replace("blubb2", "blubb02"); + projectFile.resize(0); + projectFile.write(contents); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("linking product 1")); + QVERIFY(m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("linking product 3")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + + // Incremental build, input property changed via command line for second product. + params.arguments << "project.projectDefines:blubb002"; + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("linking product 1")); + QVERIFY(m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("linking product 3")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + params.arguments.removeLast(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("linking product 1")); + QVERIFY(m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("linking product 3")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + + // Incremental build, input property changed via environment for third product. + params.environment.insert("QBS_BLACKBOX_DEFINE", "newvalue"); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("linking product 1")); + QVERIFY(!m_qbsStdout.contains("linking product 2")); + QVERIFY(m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + params.environment.clear(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("linking product 1")); + QVERIFY(!m_qbsStdout.contains("linking product 2")); + QVERIFY(m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + + // Incremental build, module property changed via command line. + params.arguments << "qbs.enableDebugCode:false"; + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(m_qbsStdout.contains("linking product 1.release")); + QVERIFY(m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(m_qbsStdout.contains("compiling lib.cpp")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + params.arguments.removeLast(); + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(m_qbsStdout.contains("linking product 1.debug")); + QVERIFY(m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + + // Incremental build, non-essential dependency removed. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + contents = projectFile.readAll(); + contents.replace("Depends { name: 'library' }", "// Depends { name: 'library' }"); + projectFile.resize(0); + projectFile.write(contents); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("linking product 1")); + QVERIFY(m_qbsStdout.contains("linking product 2")); + QVERIFY(!m_qbsStdout.contains("linking product 3")); + QVERIFY(!m_qbsStdout.contains("linking library")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + + // Incremental build, prepare script of a transformer changed. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + contents = projectFile.readAll(); + contents.replace("contents 1", "contents 2"); + projectFile.resize(0); + projectFile.write(contents); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling lib.cpp")); + QVERIFY(m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + QVERIFY(generatedFile.open(QIODevice::ReadOnly)); + QCOMPARE(generatedFile.readAll(), QByteArray("prefix 1contents 2suffix 1")); + generatedFile.close(); + + // Incremental build, product property used in JavaScript command changed. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + contents = projectFile.readAll(); + contents.replace("prefix 1", "prefix 2"); + projectFile.resize(0); + projectFile.write(contents); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling lib.cpp")); + QVERIFY(m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + QVERIFY(generatedFile.open(QIODevice::ReadOnly)); + QCOMPARE(generatedFile.readAll(), QByteArray("prefix 2contents 2suffix 1")); + generatedFile.close(); + + // Incremental build, project property used in JavaScript command changed. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + contents = projectFile.readAll(); + contents.replace("suffix 1", "suffix 2"); + projectFile.resize(0); + projectFile.write(contents); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling lib.cpp")); + QVERIFY(m_qbsStdout.contains("generated.txt")); + QVERIFY(!m_qbsStdout.contains("Making output from input")); + QVERIFY(!m_qbsStdout.contains("Making output from other output")); + QVERIFY(generatedFile.open(QIODevice::ReadOnly)); + QCOMPARE(generatedFile.readAll(), QByteArray("prefix 2contents 2suffix 2")); + generatedFile.close(); + + // Incremental build, module property used in JavaScript command changed. + WAIT_FOR_NEW_TIMESTAMP(); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + contents = projectFile.readAll(); + contents.replace("default value", "new value"); + projectFile.resize(0); + projectFile.write(contents); + projectFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling lib.cpp")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(m_qbsStdout.contains("Making output from input")); + QVERIFY(m_qbsStdout.contains("Making output from other output")); + QVERIFY(m_qbsStdout.contains("new value")); + + // Incremental build, prepare script of a rule in a module changed. + WAIT_FOR_NEW_TIMESTAMP(); + QFile moduleFile("modules/TestModule/module.qbs"); + QVERIFY(moduleFile.open(QIODevice::ReadWrite)); + contents = moduleFile.readAll(); + contents.replace("// console.info('Change in source code')", + "console.info('Change in source code')"); + moduleFile.resize(0); + moduleFile.write(contents); + moduleFile.close(); + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compiling source1.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source2.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling source3.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling lib.cpp")); + QVERIFY(!m_qbsStdout.contains("generated.txt")); + QVERIFY(m_qbsStdout.contains("Making output from input")); + QVERIFY(m_qbsStdout.contains("Making output from other output")); +} + +void TestBlackbox::qobjectInObjectiveCpp() +{ + if (!HostOsInfo::isMacosHost()) + QSKIP("only applies on macOS"); + const QString testDir = testDataDir + "/qobject-in-mm"; + QDir::setCurrent(testDir); + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::qtBug51237() +{ + const QString profileName = "profile-qtBug51237"; + const QString propertyName = "mymodule.theProperty"; + { + Settings settings((QString())); + Profile profile(profileName, &settings); + profile.setValue(propertyName, QStringList()); + } + QDir::setCurrent(testDataDir + "/QTBUG-51237"); + QbsRunParameters params; + params.arguments << "profile:" + profileName; + params.useProfile = false; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::qtScxml() +{ + QDir::setCurrent(testDataDir + "/qtscxml"); + QCOMPARE(runQbs(), 0); + if (m_qbsStdout.contains("QtScxml not present")) + QSKIP("QtScxml module not present"); + QVERIFY2(m_qbsStdout.contains("state machine name: qbs_test_machine"), + m_qbsStdout.constData()); +} + +void TestBlackbox::dynamicMultiplexRule() +{ + const QString testDir = testDataDir + "/dynamicMultiplexRule"; + QDir::setCurrent(testDir); + QCOMPARE(runQbs(), 0); + const QString outputFilePath = relativeProductBuildDir("dynamicMultiplexRule") + "/stuff-from-3-inputs"; + QVERIFY(regularFileExists(outputFilePath)); + WAIT_FOR_NEW_TIMESTAMP(); + touch("two.txt"); + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(outputFilePath)); +} + +void TestBlackbox::dynamicRuleOutputs() +{ + const QString testDir = testDataDir + "/dynamicRuleOutputs"; + QDir::setCurrent(testDir); + if (QFile::exists("work")) + rmDirR("work"); + QDir().mkdir("work"); + ccp("before", "work"); + QDir::setCurrent(testDir + "/work"); + QCOMPARE(runQbs(), 0); + + const QString appFile = relativeExecutableFilePath("genlexer"); + const QString headerFile1 = relativeProductBuildDir("genlexer") + "/GeneratedFiles/numberscanner.h"; + const QString sourceFile1 = relativeProductBuildDir("genlexer") + "/GeneratedFiles/numberscanner.c"; + const QString sourceFile2 = relativeProductBuildDir("genlexer") + "/GeneratedFiles/lex.yy.c"; + + // Check build #1: source and header file name are specified in numbers.l + QVERIFY(regularFileExists(appFile)); + QVERIFY(regularFileExists(headerFile1)); + QVERIFY(regularFileExists(sourceFile1)); + QVERIFY(!QFile::exists(sourceFile2)); + + QDateTime appFileTimeStamp1 = QFileInfo(appFile).lastModified(); + WAIT_FOR_NEW_TIMESTAMP(); + copyFileAndUpdateTimestamp("../after/numbers.l", "numbers.l"); + QCOMPARE(runQbs(), 0); + + // Check build #2: no file names are specified in numbers.l + // flex will default to lex.yy.c without header file. + QDateTime appFileTimeStamp2 = QFileInfo(appFile).lastModified(); + QVERIFY(appFileTimeStamp1 < appFileTimeStamp2); + QVERIFY(!QFile::exists(headerFile1)); + QVERIFY(!QFile::exists(sourceFile1)); + QVERIFY(regularFileExists(sourceFile2)); + + WAIT_FOR_NEW_TIMESTAMP(); + copyFileAndUpdateTimestamp("../before/numbers.l", "numbers.l"); + QCOMPARE(runQbs(), 0); + + // Check build #3: source and header file name are specified in numbers.l + QDateTime appFileTimeStamp3 = QFileInfo(appFile).lastModified(); + QVERIFY(appFileTimeStamp2 < appFileTimeStamp3); + QVERIFY(regularFileExists(appFile)); + QVERIFY(regularFileExists(headerFile1)); + QVERIFY(regularFileExists(sourceFile1)); + QVERIFY(!QFile::exists(sourceFile2)); +} + +void TestBlackbox::erroneousFiles_data() +{ + QTest::addColumn("errorMessage"); + QTest::newRow("nonexistentWorkingDir") + << "The working directory '.does.not.exist' for process '.*ls' is invalid."; +} + +void TestBlackbox::erroneousFiles() +{ + QFETCH(QString, errorMessage); + QDir::setCurrent(testDataDir + "/erroneous/" + QTest::currentDataTag()); + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QString err = QString::fromLocal8Bit(m_qbsStderr); + if (!err.contains(QRegExp(errorMessage))) { + qDebug() << "Output: " << err; + qDebug() << "Expected: " << errorMessage; + QFAIL("Unexpected error message."); + } +} + +void TestBlackbox::errorInfo() +{ + QDir::setCurrent(testDataDir + "/error-info"); + QCOMPARE(runQbs(), 0); + + QbsRunParameters params; + params.expectFailure = true; + + params.arguments = QStringList() << "project.fail1:true"; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("project.qbs:25"), m_qbsStderr); + + params.arguments = QStringList() << "project.fail2:true"; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("project.qbs:37"), m_qbsStderr); + + params.arguments = QStringList() << "project.fail3:true"; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("project.qbs:52"), m_qbsStderr); + + params.arguments = QStringList() << "project.fail4:true"; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("project.qbs:67"), m_qbsStderr); + + params.arguments = QStringList() << "project.fail5:true"; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("helper.js:4"), m_qbsStderr); + + params.arguments = QStringList() << "project.fail6:true"; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("helper.js:8"), m_qbsStderr); + + params.arguments = QStringList() << "project.fail7:true"; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("JavaScriptCommand.sourceCode"), m_qbsStderr); + QVERIFY2(m_qbsStderr.contains("project.qbs:58"), m_qbsStderr); +} + +void TestBlackbox::systemRunPaths() +{ + Settings settings((QString())); + const Profile buildProfile(profileName(), &settings); + switch (targetOs()) { + case HostOsInfo::HostOsLinux: + case HostOsInfo::HostOsMacos: + case HostOsInfo::HostOsOtherUnix: + break; + default: + QSKIP("only applies on Unix"); + } + + const QString lddFilePath = findExecutable(QStringList() << "ldd"); + if (lddFilePath.isEmpty()) + QSKIP("ldd not found"); + QDir::setCurrent(testDataDir + "/system-run-paths"); + QFETCH(bool, setRunPaths); + QbsRunParameters params; + params.arguments << QString("project.setRunPaths:") + (setRunPaths ? "true" : "false"); + QCOMPARE(runQbs(params), 0); + QProcess ldd; + ldd.start(lddFilePath, QStringList() << relativeExecutableFilePath("app")); + QVERIFY2(ldd.waitForStarted(), qPrintable(ldd.errorString())); + QVERIFY2(ldd.waitForFinished(), qPrintable(ldd.errorString())); + QVERIFY2(ldd.exitCode() == 0, ldd.readAllStandardError().constData()); + const QByteArray output = ldd.readAllStandardOutput(); + const QList outputLines = output.split('\n'); + QByteArray libLine; + foreach (const auto &line, outputLines) { + if (line.contains("theLib")) { + libLine = line; + break; + } + } + QVERIFY2(!libLine.isEmpty(), output.constData()); + QVERIFY2(setRunPaths == libLine.contains("not found"), libLine.constData()); +} + +void TestBlackbox::systemRunPaths_data() +{ + QTest::addColumn("setRunPaths"); + QTest::newRow("do not set system run paths") << false; + QTest::newRow("do set system run paths") << true; +} + +void TestBlackbox::exportRule() +{ + QDir::setCurrent(testDataDir + "/export-rule"); + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::exportToOutsideSearchPath() +{ + QDir::setCurrent(testDataDir + "/export-to-outside-searchpath"); + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("Module 'aModule' not found when setting up transitive " + "dependencies for product 'theProduct'"), m_qbsStderr.constData()); +} + +void TestBlackbox::fileDependencies() +{ + QDir::setCurrent(testDataDir + "/fileDependencies"); + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("compiling narf.cpp")); + QVERIFY(m_qbsStdout.contains("compiling zort.cpp")); + const QString productFileName = relativeExecutableFilePath("myapp"); + QVERIFY2(regularFileExists(productFileName), qPrintable(productFileName)); + + // Incremental build without changes. + QCOMPARE(runQbs(), 0); + QVERIFY(!m_qbsStdout.contains("compiling")); + QVERIFY(!m_qbsStdout.contains("linking")); + + // Incremental build with changed file dependency. + WAIT_FOR_NEW_TIMESTAMP(); + touch("awesomelib/awesome.h"); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("compiling narf.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling zort.cpp")); + + // Incremental build with changed 2nd level file dependency. + WAIT_FOR_NEW_TIMESTAMP(); + touch("awesomelib/magnificent.h"); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("compiling narf.cpp")); + QVERIFY(!m_qbsStdout.contains("compiling zort.cpp")); +} + +void TestBlackbox::installedTransformerOutput() +{ + QDir::setCurrent(testDataDir + "/installed-transformer-output"); + QCOMPARE(runQbs(), 0); + const QString installedFilePath = defaultInstallRoot + "/textfiles/HelloWorld.txt"; + QVERIFY2(QFile::exists(installedFilePath), qPrintable(installedFilePath)); +} + +void TestBlackbox::inputsFromDependencies() +{ + QDir::setCurrent(testDataDir + "/inputs-from-dependencies"); + QCOMPARE(runQbs(), 0); + const QList output = m_qbsStdout.trimmed().split('\n'); + QVERIFY2(output.contains((QDir::currentPath() + "/file1.txt").toUtf8()), + m_qbsStdout.constData()); + QVERIFY2(output.contains((QDir::currentPath() + "/file2.txt").toUtf8()), + m_qbsStdout.constData()); + QVERIFY2(output.contains((QDir::currentPath() + "/file3.txt").toUtf8()), + m_qbsStdout.constData()); + QVERIFY2(!output.contains((QDir::currentPath() + "/file4.txt").toUtf8()), + m_qbsStdout.constData()); +} + +void TestBlackbox::installPackage() +{ + if (HostOsInfo::hostOs() == HostOsInfo::HostOsWindows) + QSKIP("Beware of the msys tar"); + QString binary = findArchiver("tar"); + if (binary.isEmpty()) + QSKIP("tar not found"); + MacosTarHealer tarHealer; + QDir::setCurrent(testDataDir + "/installpackage"); + QCOMPARE(runQbs(), 0); + const QString tarFilePath = relativeProductBuildDir("tar-package") + "/tar-package.tar.gz"; + QVERIFY2(regularFileExists(tarFilePath), qPrintable(tarFilePath)); + QProcess tarList; + tarList.start(binary, QStringList() << "tf" << tarFilePath); + QVERIFY2(tarList.waitForStarted(), qPrintable(tarList.errorString())); + QVERIFY2(tarList.waitForFinished(), qPrintable(tarList.errorString())); + const QList outputLines = tarList.readAllStandardOutput().split('\n'); + QList cleanOutputLines; + foreach (const QByteArray &line, outputLines) { + const QByteArray trimmedLine = line.trimmed(); + if (!trimmedLine.isEmpty()) + cleanOutputLines << trimmedLine; + } + QCOMPARE(cleanOutputLines.count(), 3); + foreach (const QByteArray &line, cleanOutputLines) { + QVERIFY2(line.contains("public_tool") || line.contains("mylib") || line.contains("lib.h"), + line.constData()); + } +} + +void TestBlackbox::installable() +{ + QDir::setCurrent(testDataDir + "/installable"); + QCOMPARE(runQbs(), 0); + QFile installList(relativeProductBuildDir("install-list") + "/installed-files.txt"); + QVERIFY2(installList.open(QIODevice::ReadOnly), qPrintable(installList.errorString())); + QCOMPARE(installList.readAll().count('\n'), 2); +} + +void TestBlackbox::installTree() +{ + QDir::setCurrent(testDataDir + "/install-tree"); + QbsRunParameters params; + params.command = "install"; + QCOMPARE(runQbs(params), 0); + const QString installRoot = relativeBuildDir() + "/install-root/"; + QVERIFY(QFile::exists(installRoot + "content/foo.txt")); + QVERIFY(QFile::exists(installRoot + "content/subdir1/bar.txt")); + QVERIFY(QFile::exists(installRoot + "content/subdir2/baz.txt")); +} + +void TestBlackbox::invalidCommandProperty() +{ + QDir::setCurrent(testDataDir + "/invalid-command-property"); + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("unsuitable"), m_qbsStderr.constData()); +} + +void TestBlackbox::invalidLibraryNames() +{ + QDir::setCurrent(testDataDir + "/invalid-library-names"); + QFETCH(QString, index); + QFETCH(bool, success); + QFETCH(QStringList, diagnostics); + QbsRunParameters params(QStringList("project.valueIndex:" + index)); + params.expectFailure = !success; + QCOMPARE(runQbs(params) == 0, success); + foreach (const QString &diag, diagnostics) + QVERIFY2(m_qbsStderr.contains(diag.toLocal8Bit()), m_qbsStderr.constData()); +} + +void TestBlackbox::invalidLibraryNames_data() +{ + QTest::addColumn("index"); + QTest::addColumn("success"); + QTest::addColumn("diagnostics"); + + QTest::newRow("null") << "0" << false << QStringList("is null"); + QTest::newRow("undefined") << "1" << false << QStringList("is undefined"); + QTest::newRow("number") << "2" << false << QStringList("does not have string type"); + QTest::newRow("array") << "3" << false << QStringList("does not have string type"); + QTest::newRow("empty string") << "4" << true << (QStringList() + << "WARNING: Removing empty string from value of property " + "'cpp.dynamicLibraries' in product 'invalid-library-names'." + << "WARNING: Removing empty string from value of property " + "'cpp.staticLibraries' in product 'invalid-library-names'."); +} + +void TestBlackbox::invalidExtensionInstantiation() +{ + QDir::setCurrent(testDataDir + "/invalid-extension-instantiation"); + QbsRunParameters params; + params.expectFailure = true; + params.arguments << (QString("theProduct.extension:") + QTest::currentDataTag()); + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("invalid-extension-instantiation.qbs:18") + && m_qbsStderr.contains('\'' + QByteArray(QTest::currentDataTag()) + + "' cannot be instantiated"), + m_qbsStderr.constData()); +} + +void TestBlackbox::invalidExtensionInstantiation_data() +{ + QTest::addColumn("dummy"); + + QTest::newRow("Environment"); + QTest::newRow("File"); + QTest::newRow("FileInfo"); + QTest::newRow("Utilities"); +} + +void TestBlackbox::cli() +{ + int status; + findCli(&status); + QCOMPARE(status, 0); + + Settings s((QString())); + Profile p("qbs_autotests-cli", &s); + const QStringList toolchain = p.value("qbs.toolchain").toStringList(); + if (!p.exists() || !(toolchain.contains("dotnet") || toolchain.contains("mono"))) + QSKIP("No suitable Common Language Infrastructure test profile"); + + QDir::setCurrent(testDataDir + "/cli"); + QbsRunParameters params(QStringList() << "-f" << "dotnettest.qbs" + << "profile:" + p.name()); + params.useProfile = false; + + status = runQbs(params); + if (p.value("cli.toolchainInstallPath").toString().isEmpty() + && status != 0 && m_qbsStderr.contains("toolchainInstallPath")) + QSKIP("cli.toolchainInstallPath not set and automatic detection failed"); + + QCOMPARE(status, 0); + rmDirR(relativeBuildDir()); + + QbsRunParameters params2(QStringList() << "-f" << "fshello.qbs" + << "profile:" + p.name()); + params2.useProfile = false; + QCOMPARE(runQbs(params2), 0); + rmDirR(relativeBuildDir()); +} + +void TestBlackbox::jsExtensionsFile() +{ + QDir::setCurrent(testDataDir + "/jsextensions-file"); + QbsRunParameters params(QStringList() << "-f" << "file.qbs"); + QCOMPARE(runQbs(params), 0); + QVERIFY(!QFileInfo("original.txt").exists()); + QFile copy("copy.txt"); + QVERIFY(copy.exists()); + QVERIFY(copy.open(QIODevice::ReadOnly)); + const QList lines = copy.readAll().trimmed().split('\n'); + QCOMPARE(lines.count(), 2); + QCOMPARE(lines.at(0).trimmed().constData(), "false"); + QCOMPARE(lines.at(1).trimmed().constData(), "true"); +} + +void TestBlackbox::jsExtensionsFileInfo() +{ + QDir::setCurrent(testDataDir + "/jsextensions-fileinfo"); + QbsRunParameters params(QStringList() << "-f" << "fileinfo.qbs"); + QCOMPARE(runQbs(params), 0); + QFile output("output.txt"); + QVERIFY(output.exists()); + QVERIFY(output.open(QIODevice::ReadOnly)); + const QList lines = output.readAll().trimmed().split('\n'); + QCOMPARE(lines.count(), 23); + int i = 0; + QCOMPARE(lines.at(i++).trimmed().constData(), "blubb"); + QCOMPARE(lines.at(i++).trimmed().constData(), "blubb.tar"); + QCOMPARE(lines.at(i++).trimmed().constData(), "blubb.tar.gz"); + QCOMPARE(lines.at(i++).trimmed().constData(), "/tmp/blubb.tar.gz"); + QCOMPARE(lines.at(i++).trimmed().constData(), "c:/tmp/blubb.tar.gz"); + QCOMPARE(lines.at(i++).trimmed().constData(), "true"); + QCOMPARE(lines.at(i++).trimmed().constData(), HostOsInfo::isWindowsHost() ? "true" : "false"); + QCOMPARE(lines.at(i++).trimmed().constData(), "false"); + QCOMPARE(lines.at(i++).trimmed().constData(), "true"); + QCOMPARE(lines.at(i++).trimmed().constData(), "false"); + QCOMPARE(lines.at(i++).trimmed().constData(), "false"); + QCOMPARE(lines.at(i++).trimmed().constData(), "/tmp/blubb.tar.gz"); + QCOMPARE(lines.at(i++).trimmed().constData(), "/tmp"); + QCOMPARE(lines.at(i++).trimmed().constData(), "/tmp"); + QCOMPARE(lines.at(i++).trimmed().constData(), "/"); + QCOMPARE(lines.at(i++).trimmed().constData(), HostOsInfo::isWindowsHost() ? "d:/" : "d:"); + QCOMPARE(lines.at(i++).trimmed().constData(), "d:"); + QCOMPARE(lines.at(i++).trimmed().constData(), "d:/"); + QCOMPARE(lines.at(i++).trimmed().constData(), "blubb.tar.gz"); + QCOMPARE(lines.at(i++).trimmed().constData(), "tmp/blubb.tar.gz"); + QCOMPARE(lines.at(i++).trimmed().constData(), "../blubb.tar.gz"); + QCOMPARE(lines.at(i++).trimmed().constData(), "\\tmp\\blubb.tar.gz"); + QCOMPARE(lines.at(i++).trimmed().constData(), "c:\\tmp\\blubb.tar.gz"); +} + +void TestBlackbox::jsExtensionsProcess() +{ + QDir::setCurrent(testDataDir + "/jsextensions-process"); + QbsRunParameters params(QStringList() << "-f" << "process.qbs" << "project.qbsFilePath:" + + qbsExecutableFilePath); + QCOMPARE(runQbs(params), 0); + QFile output("output.txt"); + QVERIFY(output.exists()); + QVERIFY(output.open(QIODevice::ReadOnly)); + const QList lines = output.readAll().trimmed().split('\n'); + QCOMPARE(lines.count(), 8); + QCOMPARE(lines.at(0).trimmed().constData(), "0"); + QVERIFY(lines.at(1).startsWith("qbs ")); + QCOMPARE(lines.at(2).trimmed().constData(), "true"); + QCOMPARE(lines.at(3).trimmed().constData(), "true"); + QCOMPARE(lines.at(4).trimmed().constData(), "0"); + QVERIFY(lines.at(5).startsWith("qbs ")); + QCOMPARE(lines.at(6).trimmed().constData(), "false"); + QCOMPARE(lines.at(7).trimmed().constData(), "should be"); +} + +void TestBlackbox::jsExtensionsPropertyList() +{ + if (!HostOsInfo::isMacosHost()) + QSKIP("temporarily only applies on macOS"); + + QDir::setCurrent(testDataDir + "/jsextensions-propertylist"); + QbsRunParameters params(QStringList() << "-nf" << "propertylist.qbs"); + QCOMPARE(runQbs(params), 0); + QFile file1("test.json"); + QVERIFY(file1.exists()); + QVERIFY(file1.open(QIODevice::ReadOnly)); + QFile file2("test.xml"); + QVERIFY(file2.exists()); + QVERIFY(file2.open(QIODevice::ReadOnly)); + QFile file3("test2.json"); + QVERIFY(file3.exists()); + QVERIFY(file3.open(QIODevice::ReadOnly)); + QByteArray file1Contents = file1.readAll(); + QCOMPARE(file3.readAll(), file1Contents); + //QCOMPARE(file1Contents, file2.readAll()); // keys don't have guaranteed order + QJsonParseError err1, err2; + QCOMPARE(QJsonDocument::fromJson(file1Contents, &err1), + QJsonDocument::fromJson(file2.readAll(), &err2)); + QVERIFY(err1.error == QJsonParseError::NoError && err2.error == QJsonParseError::NoError); + QFile file4("test.openstep.plist"); + QVERIFY(file4.exists()); + QFile file5("test3.json"); + QVERIFY(file5.exists()); + QVERIFY(file5.open(QIODevice::ReadOnly)); + QVERIFY(file1Contents != file5.readAll()); +} + +void TestBlackbox::jsExtensionsTemporaryDir() +{ + QDir::setCurrent(testDataDir + "/jsextensions-temporarydir"); + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::jsExtensionsTextFile() +{ + QDir::setCurrent(testDataDir + "/jsextensions-textfile"); + QbsRunParameters params(QStringList() << "-f" << "textfile.qbs"); + QCOMPARE(runQbs(params), 0); + QFile file1("file1.txt"); + QVERIFY(file1.exists()); + QVERIFY(file1.open(QIODevice::ReadOnly)); + QCOMPARE(file1.size(), qint64(0)); + QFile file2("file2.txt"); + QVERIFY(file2.exists()); + QVERIFY(file2.open(QIODevice::ReadOnly)); + const QList lines = file2.readAll().trimmed().split('\n'); + QCOMPARE(lines.count(), 5); + QCOMPARE(lines.at(0).trimmed().constData(), "false"); + QCOMPARE(lines.at(1).trimmed().constData(), "First line."); + QCOMPARE(lines.at(2).trimmed().constData(), "Second line."); + QCOMPARE(lines.at(3).trimmed().constData(), "Third line."); + QCOMPARE(lines.at(4).trimmed().constData(), "true"); +} + +void TestBlackbox::ld() +{ + QDir::setCurrent(testDataDir + "/ld"); + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::linkerMode() +{ + if (!HostOsInfo::isAnyUnixHost()) + QSKIP("only applies on Unix"); + + QDir::setCurrent(testDataDir + "/linkerMode"); + QCOMPARE(runQbs(), 0); + + auto testCondition = [&](const QString &lang, + const std::function &condition) { + if ((lang == "Objective-C" || lang == "Objective-C++") + && HostOsInfo::hostOs() != HostOsInfo::HostOsMacos) + return; + const QString binary = defaultInstallRoot + "/LinkedProduct-" + lang; + QProcess deptool; + if (HostOsInfo::hostOs() == HostOsInfo::HostOsMacos) + deptool.start("otool", QStringList() << "-L" << binary); + else + deptool.start("readelf", QStringList() << "-a" << binary); + QVERIFY(deptool.waitForStarted()); + QVERIFY(deptool.waitForFinished()); + QByteArray deptoolOutput = deptool.readAllStandardOutput(); + if (HostOsInfo::hostOs() != HostOsInfo::HostOsMacos) { + QList lines = deptoolOutput.split('\n'); + int sz = lines.size(); + for (int i = 0; i < sz; ++i) { + if (!lines.at(i).contains("(NEEDED)")) { + lines.removeAt(i--); + sz--; + } + } + + deptoolOutput = lines.join('\n'); + } + QCOMPARE(deptool.exitCode(), 0); + QVERIFY2(condition(deptoolOutput), deptoolOutput.constData()); + }; + + const QStringList nocpplangs = QStringList() << "Assembly" << "C" << "Objective-C"; + for (const QString &lang : nocpplangs) + testCondition(lang, [](const QByteArray &lddOutput) { return !lddOutput.contains("c++"); }); + + const QStringList cpplangs = QStringList() << "C++" << "Objective-C++"; + for (const QString &lang : cpplangs) + testCondition(lang, [](const QByteArray &lddOutput) { return lddOutput.contains("c++"); }); + + const QStringList objclangs = QStringList() << "Objective-C" << "Objective-C++"; + for (const QString &lang : objclangs) + testCondition(lang, [](const QByteArray &lddOutput) { return lddOutput.contains("objc"); }); +} + +void TestBlackbox::lexyacc() +{ + if (findExecutable(QStringList("lex")).isEmpty() + || findExecutable(QStringList("yacc")).isEmpty()) { + QSKIP("lex or yacc not present"); + } + QDir::setCurrent(testDataDir + "/lexyacc/one-grammar"); + QCOMPARE(runQbs(), 0); + const QString parserBinary = relativeExecutableFilePath("one-grammar"); + QProcess p; + p.start(parserBinary); + QVERIFY2(p.waitForStarted(), qPrintable(p.errorString())); + p.write("a && b || c && !d"); + p.closeWriteChannel(); + QVERIFY2(p.waitForFinished(), qPrintable(p.errorString())); + QVERIFY2(p.exitCode() == 0, p.readAllStandardError().constData()); + const QByteArray parserOutput = p.readAllStandardOutput(); + QVERIFY2(parserOutput.contains("OR AND a b AND c NOT d"), parserOutput.constData()); + + QDir::setCurrent(testDataDir + "/lexyacc/two-grammars"); + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + + params.expectFailure = false; + params.arguments << (QStringList() << "lex_yacc.uniqueSymbolPrefix:true"); + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::linkerScripts() +{ + Settings settings((QString())); + Profile buildProfile(profileName(), &settings); + QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList(); + if (!toolchain.contains("gcc") || targetOs() != HostOsInfo::HostOsLinux) + QSKIP("linker script test only applies to Linux "); + QDir::setCurrent(testDataDir + "/linkerscripts"); + QCOMPARE(runQbs(QbsRunParameters(QStringList("-q") + << ("qbs.installRoot:" + QDir::currentPath()))), 0); + const QString output = QString::fromLocal8Bit(m_qbsStderr); + QRegExp pattern(".*---(.*)---.*"); + QVERIFY2(pattern.exactMatch(output), qPrintable(output)); + QCOMPARE(pattern.captureCount(), 1); + const QString nmPath = pattern.capturedTexts().at(1); + if (!QFile::exists(nmPath)) + QSKIP("Cannot check for symbol presence: No nm found."); + QProcess nm; + nm.start(nmPath, QStringList(QDir::currentPath() + "/liblinkerscripts.so")); + QVERIFY(nm.waitForStarted()); + QVERIFY(nm.waitForFinished()); + const QByteArray nmOutput = nm.readAllStandardOutput(); + QCOMPARE(nm.exitCode(), 0); + QVERIFY2(nmOutput.contains("TEST_SYMBOL1"), nmOutput.constData()); + QVERIFY2(nmOutput.contains("TEST_SYMBOL2"), nmOutput.constData()); +} + +void TestBlackbox::listPropertiesWithOuter() +{ + QDir::setCurrent(testDataDir + "/list-properties-with-outer"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("listProp: [\"product\",\"higher\",\"group\"]"), + m_qbsStdout.constData()); +} + +void TestBlackbox::listPropertyOrder() +{ + QDir::setCurrent(testDataDir + "/list-property-order"); + const QbsRunParameters params(QStringList() << "-qq"); + QCOMPARE(runQbs(params), 0); + const QByteArray firstOutput = m_qbsStderr; + for (int i = 0; i < 25; ++i) { + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(params), 0); + if (m_qbsStderr != firstOutput) + break; + } + QCOMPARE(m_qbsStderr.constData(), firstOutput.constData()); +} + +void TestBlackbox::mixedBuildVariants() +{ + QDir::setCurrent(testDataDir + "/mixed-build-variants"); + Settings settings((QString())); + Profile profile(profileName(), &settings); + if (profile.value("qbs.toolchain").toStringList().contains("msvc")) { + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("not allowed"), m_qbsStderr.constData()); + } else if (!profile.value("Qt.core.availableBuildVariants").toStringList().contains("release")) { + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("not supported"), m_qbsStderr.constData()); + } else { + QCOMPARE(runQbs(), 0); + } +} + +void TestBlackbox::mocFlags() +{ + QDir::setCurrent(testDataDir + "/moc-flags"); + QCOMPARE(runQbs(), 0); + WAIT_FOR_NEW_TIMESTAMP(); + QbsRunParameters params; + params.expectFailure = true; + params.arguments << "Qt.core.mocFlags:-E"; + QVERIFY(runQbs(params) != 0); +} + +void TestBlackbox::multipleChanges() +{ + QDir::setCurrent(testDataDir + "/multiple-changes"); + QCOMPARE(runQbs(), 0); + QFile newFile("test.blubb"); + QVERIFY(newFile.open(QIODevice::WriteOnly)); + newFile.close(); + QCOMPARE(runQbs(QStringList() << "project.prop:true"), 0); + QVERIFY(m_qbsStdout.contains("prop: true")); +} + +void TestBlackbox::nestedGroups() +{ + QDir::setCurrent(testDataDir + "/nested-groups"); + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(relativeExecutableFilePath("nested-groups"))); +} + +void TestBlackbox::nestedProperties() +{ + QDir::setCurrent(testDataDir + "/nested-properties"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.contains("value in higherlevel"), m_qbsStdout.constData()); +} + +void TestBlackbox::newOutputArtifact() +{ + QDir::setCurrent(testDataDir + "/new-output-artifact"); + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(relativeBuildDir() + "/install-root/output_98.out")); + const QString the100thArtifact = relativeBuildDir() + "/install-root/output_99.out"; + QVERIFY(!regularFileExists(the100thArtifact)); + QbsRunParameters params(QStringList() << "theProduct.artifactCount:100"); + QCOMPARE(runQbs(params), 0); + QVERIFY(regularFileExists(the100thArtifact)); +} + +void TestBlackbox::nonBrokenFilesInBrokenProduct() +{ + QDir::setCurrent(testDataDir + "/non-broken-files-in-broken-product"); + QbsRunParameters params(QStringList() << "-k"); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY(m_qbsStdout.contains("fine.cpp")); + QVERIFY(runQbs(params) != 0); + QVERIFY(!m_qbsStdout.contains("fine.cpp")); // The non-broken file must not be recompiled. +} + +void TestBlackbox::nonDefaultProduct() +{ + QDir::setCurrent(testDataDir + "/non-default-product"); + const QString defaultAppExe = relativeExecutableFilePath("default app"); + const QString nonDefaultAppExe = relativeExecutableFilePath("non-default app"); + + QCOMPARE(runQbs(), 0); + QVERIFY2(QFile::exists(defaultAppExe), qPrintable(defaultAppExe)); + QVERIFY2(!QFile::exists(nonDefaultAppExe), qPrintable(nonDefaultAppExe)); + + QCOMPARE(runQbs(QbsRunParameters(QStringList() << "--all-products")), 0); + QVERIFY2(QFile::exists(nonDefaultAppExe), qPrintable(nonDefaultAppExe)); +} + +static void switchProfileContents(qbs::Profile &p, Settings *s, bool on) +{ + const QString scalarKey = "leaf.scalarProp"; + const QString listKey = "leaf.listProp"; + if (on) { + p.setValue(scalarKey, "profile"); + p.setValue(listKey, QStringList() << "profile"); + } else { + p.remove(scalarKey); + p.remove(listKey); + } + s->sync(); +} + +static void switchFileContents(QFile &f, bool on) +{ + f.seek(0); + QByteArray contents = f.readAll(); + f.resize(0); + if (on) + contents.replace("// leaf.", "leaf."); + else + contents.replace("leaf.", "// leaf."); + f.write(contents); + f.flush(); +} + +void TestBlackbox::propertyPrecedence() +{ + QDir::setCurrent(testDataDir + "/property-precedence"); + qbs::Settings s((QString())); + qbs::Internal::TemporaryProfile profile("qbs_autotests_propPrecedence", &s); + profile.p.setValue("qbs.architecture", "x86"); // Profiles must not be empty... + s.sync(); + const QStringList args = QStringList() << "-f" << "project.qbs" + << ("profile:" + profile.p.name()); + QbsRunParameters params(args); + params.useProfile = false; + + // Case 1: [cmdline=0,prod=0,export=0,nonleaf=0,profile=0] + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: leaf\n") + && m_qbsStdout.contains("list prop: [\"leaf\"]\n"), + m_qbsStdout.constData()); + + // Case 2: [cmdline=0,prod=0,export=0,nonleaf=0,profile=1] + switchProfileContents(profile.p, &s, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: profile\n") + && m_qbsStdout.contains("list prop: [\"profile\"]\n"), + m_qbsStdout.constData()); + + // Case 3: [cmdline=0,prod=0,export=0,nonleaf=1,profile=0] + QFile nonleafFile("modules/nonleaf/nonleaf.qbs"); + QVERIFY2(nonleafFile.open(QIODevice::ReadWrite), qPrintable(nonleafFile.errorString())); + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: nonleaf\n") + && m_qbsStdout.contains("list prop: [\"nonleaf\",\"leaf\"]\n"), + m_qbsStdout.constData()); + + // Case 4: [cmdline=0,prod=0,export=0,nonleaf=1,profile=1] + switchProfileContents(profile.p, &s, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: nonleaf\n") + && m_qbsStdout.contains("list prop: [\"nonleaf\",\"profile\"]\n"), + m_qbsStdout.constData()); + + // Case 5: [cmdline=0,prod=0,export=1,nonleaf=0,profile=0] + QFile depFile("dep.qbs"); + QVERIFY2(depFile.open(QIODevice::ReadWrite), qPrintable(depFile.errorString())); + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, false); + switchFileContents(depFile, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: export\n") + && m_qbsStdout.contains("list prop: [\"export\",\"leaf\"]\n"), + m_qbsStdout.constData()); + + // Case 6: [cmdline=0,prod=0,export=1,nonleaf=0,profile=1] + switchProfileContents(profile.p, &s, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: export\n") + && m_qbsStdout.contains("list prop: [\"export\",\"profile\"]\n"), + m_qbsStdout.constData()); + + // Case 7: [cmdline=0,prod=0,export=1,nonleaf=1,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: export\n") + && m_qbsStdout.contains("list prop: [\"export\",\"nonleaf\",\"leaf\"]\n"), + m_qbsStdout.constData()); + + // Case 8: [cmdline=0,prod=0,export=1,nonleaf=1,profile=1] + switchProfileContents(profile.p, &s, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: export\n") + && m_qbsStdout.contains("list prop: [\"export\",\"nonleaf\",\"profile\"]\n"), + m_qbsStdout.constData()); + + // Case 9: [cmdline=0,prod=1,export=0,nonleaf=0,profile=0] + QFile productFile("project.qbs"); + QVERIFY2(productFile.open(QIODevice::ReadWrite), qPrintable(productFile.errorString())); + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, false); + switchFileContents(depFile, false); + switchFileContents(productFile, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") + && m_qbsStdout.contains("list prop: [\"product\",\"leaf\"]\n"), + m_qbsStdout.constData()); + + // Case 10: [cmdline=0,prod=1,export=0,nonleaf=0,profile=1] + switchProfileContents(profile.p, &s, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") + && m_qbsStdout.contains("list prop: [\"product\",\"profile\"]\n"), + m_qbsStdout.constData()); + + // Case 11: [cmdline=0,prod=1,export=0,nonleaf=1,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") + && m_qbsStdout.contains("list prop: [\"product\",\"nonleaf\",\"leaf\"]\n"), + m_qbsStdout.constData()); + + // Case 12: [cmdline=0,prod=1,export=0,nonleaf=1,profile=1] + switchProfileContents(profile.p, &s, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") + && m_qbsStdout.contains("list prop: [\"product\",\"nonleaf\",\"profile\"]\n"), + m_qbsStdout.constData()); + + // Case 13: [cmdline=0,prod=1,export=1,nonleaf=0,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, false); + switchFileContents(depFile, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") + && m_qbsStdout.contains("list prop: [\"product\",\"export\",\"leaf\"]\n"), + m_qbsStdout.constData()); + + // Case 14: [cmdline=0,prod=1,export=1,nonleaf=0,profile=1] + switchProfileContents(profile.p, &s, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") + && m_qbsStdout.contains("list prop: [\"product\",\"export\",\"profile\"]\n"), + m_qbsStdout.constData()); + + // Case 15: [cmdline=0,prod=1,export=1,nonleaf=1,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") + && m_qbsStdout.contains("list prop: [\"product\",\"export\",\"nonleaf\",\"leaf\"]\n"), + m_qbsStdout.constData()); + + // Case 16: [cmdline=0,prod=1,export=1,nonleaf=1,profile=1] + switchProfileContents(profile.p, &s, true); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: product\n") + && m_qbsStdout.contains("list prop: [\"product\",\"export\",\"nonleaf\",\"profile\"]\n"), + m_qbsStdout.constData()); + + // Command line properties wipe everything, including lists. + // Case 17: [cmdline=1,prod=0,export=0,nonleaf=0,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, false); + switchFileContents(depFile, false); + switchFileContents(productFile, false); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 18: [cmdline=1,prod=0,export=0,nonleaf=0,profile=1] + switchProfileContents(profile.p, &s, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 19: [cmdline=1,prod=0,export=0,nonleaf=1,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 20: [cmdline=1,prod=0,export=0,nonleaf=1,profile=1] + switchProfileContents(profile.p, &s, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 21: [cmdline=1,prod=0,export=1,nonleaf=0,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, false); + switchFileContents(depFile, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 22: [cmdline=1,prod=0,export=1,nonleaf=0,profile=1] + switchProfileContents(profile.p, &s, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 23: [cmdline=1,prod=0,export=1,nonleaf=1,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 24: [cmdline=1,prod=0,export=1,nonleaf=1,profile=1] + switchProfileContents(profile.p, &s, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 25: [cmdline=1,prod=1,export=0,nonleaf=0,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, false); + switchFileContents(depFile, false); + switchFileContents(productFile, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 26: [cmdline=1,prod=1,export=0,nonleaf=0,profile=1] + switchProfileContents(profile.p, &s, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 27: [cmdline=1,prod=1,export=0,nonleaf=1,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 28: [cmdline=1,prod=1,export=0,nonleaf=1,profile=1] + switchProfileContents(profile.p, &s, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 29: [cmdline=1,prod=1,export=1,nonleaf=0,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, false); + switchFileContents(depFile, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 30: [cmdline=1,prod=1,export=1,nonleaf=0,profile=1] + switchProfileContents(profile.p, &s, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 31: [cmdline=1,prod=1,export=1,nonleaf=1,profile=0] + switchProfileContents(profile.p, &s, false); + switchFileContents(nonleafFile, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); + + // Case 32: [cmdline=1,prod=1,export=1,nonleaf=1,profile=1] + switchProfileContents(profile.p, &s, true); + params.arguments << "leaf.scalarProp:cmdline" << "leaf.listProp:cmdline"; + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n") + && m_qbsStdout.contains("list prop: [\"cmdline\"]\n"), + m_qbsStdout.constData()); +} + +void TestBlackbox::qmlDebugging() +{ + QDir::setCurrent(testDataDir + "/qml-debugging"); + QCOMPARE(runQbs(), 0); + Settings settings((QString())); + Profile profile(profileName(), &settings); + if (!profile.value("qbs.toolchain").toStringList().contains("gcc")) + return; + QProcess nm; + nm.start("nm", QStringList(relativeExecutableFilePath("debuggable-app"))); + if (nm.waitForStarted()) { // Let's ignore hosts without nm. + QVERIFY2(nm.waitForFinished(), qPrintable(nm.errorString())); + QVERIFY2(nm.exitCode() == 0, nm.readAllStandardError().constData()); + const QByteArray output = nm.readAllStandardOutput(); + QVERIFY2(output.toLower().contains("debugginghelper"), output.constData()); + } +} + +void TestBlackbox::productDependenciesByType() +{ + QDir::setCurrent(testDataDir + "/product-dependencies-by-type"); + QCOMPARE(runQbs(), 0); + QFile appListFile(relativeProductBuildDir("app list") + "/app-list.txt"); + QVERIFY2(appListFile.open(QIODevice::ReadOnly), qPrintable(appListFile.fileName())); + const QList appList = appListFile.readAll().trimmed().split('\n'); + QCOMPARE(appList.count(), 3); + QStringList apps = QStringList() + << QDir::currentPath() + '/' + relativeExecutableFilePath("app1") + << QDir::currentPath() + '/' + relativeExecutableFilePath("app2") + << QDir::currentPath() + '/' + relativeExecutableFilePath("app3"); + foreach (const QByteArray &line, appList) { + const QString cleanLine = QString::fromLocal8Bit(line.trimmed()); + QVERIFY2(apps.removeOne(cleanLine), qPrintable(cleanLine)); + } + QVERIFY(apps.isEmpty()); +} + +void TestBlackbox::properQuoting() +{ + QDir::setCurrent(testDataDir + "/proper quoting"); + QCOMPARE(runQbs(), 0); + QbsRunParameters params(QLatin1String("run"), QStringList() << "-q" << "-p" << "Hello World"); + params.expectFailure = true; // Because the exit code is non-zero. + QCOMPARE(runQbs(params), 156); + const char * const expectedOutput = "whitespaceless\ncontains space\ncontains\ttab\n" + "backslash\\\nHello World! The magic number is 156."; + QCOMPARE(unifiedLineEndings(m_qbsStdout).constData(), expectedOutput); +} + +void TestBlackbox::propertiesInExportItems() +{ + QDir::setCurrent(testDataDir + "/properties-in-export-items"); + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(relativeExecutableFilePath("p1"))); + QVERIFY(regularFileExists(relativeExecutableFilePath("p2"))); + QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); +} + +void TestBlackbox::radAfterIncompleteBuild_data() +{ + QTest::addColumn("projectFileName"); + QTest::newRow("Project with Rule") << "project_with_rule.qbs"; + QTest::newRow("Project with Transformer") << "project_with_transformer.qbs"; +} + +void TestBlackbox::radAfterIncompleteBuild() +{ + QDir::setCurrent(testDataDir + "/rad-after-incomplete-build"); + rmDirR(relativeBuildDir()); + const QString projectFileName = "project_with_rule.qbs"; + + // Step 1: Have a directory where a file used to be. + QbsRunParameters params(QStringList() << "-f" << projectFileName); + QCOMPARE(runQbs(params), 0); + WAIT_FOR_NEW_TIMESTAMP(); + QFile projectFile(projectFileName); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + QByteArray content = projectFile.readAll(); + content.replace("oldfile", "oldfile/newfile"); + projectFile.resize(0); + projectFile.write(content); + projectFile.flush(); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + WAIT_FOR_NEW_TIMESTAMP(); + content.replace("oldfile/newfile", "newfile"); + projectFile.resize(0); + projectFile.write(content); + projectFile.flush(); + params.expectFailure = false; + QCOMPARE(runQbs(params), 0); + WAIT_FOR_NEW_TIMESTAMP(); + content.replace("newfile", "oldfile/newfile"); + projectFile.resize(0); + projectFile.write(content); + projectFile.flush(); + QCOMPARE(runQbs(params), 0); + + // Step 2: Have a file where a directory used to be. + WAIT_FOR_NEW_TIMESTAMP(); + content.replace("oldfile/newfile", "oldfile"); + projectFile.resize(0); + projectFile.write(content); + projectFile.flush(); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + WAIT_FOR_NEW_TIMESTAMP(); + content.replace("oldfile", "newfile"); + projectFile.resize(0); + projectFile.write(content); + projectFile.flush(); + params.expectFailure = false; + QCOMPARE(runQbs(params), 0); + WAIT_FOR_NEW_TIMESTAMP(); + content.replace("newfile", "oldfile"); + projectFile.resize(0); + projectFile.write(content); + projectFile.flush(); + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::subProfileChangeTracking() +{ + QDir::setCurrent(testDataDir + "/subprofile-change-tracking"); + qbs::Settings settings((QString())); + qbs::Internal::TemporaryProfile subProfile("qbs-autotests-subprofile", &settings); + subProfile.p.setValue("baseProfile", profileName()); + subProfile.p.setValue("cpp.includePaths", QStringList("/tmp/include1")); + settings.sync(); + QCOMPARE(runQbs(), 0); + + subProfile.p.setValue("cpp.includePaths", QStringList("/tmp/include2")); + settings.sync(); + QCOMPARE(runQbs(), 0); + QVERIFY(!m_qbsStdout.contains("main1.cpp")); + QVERIFY(m_qbsStdout.contains("main2.cpp")); +} + +void TestBlackbox::successiveChanges() +{ + QDir::setCurrent(testDataDir + "/successive-changes"); + QCOMPARE(runQbs(), 0); + + QbsRunParameters params(QStringList() << "theProduct.type:output,blubb"); + QCOMPARE(runQbs(params), 0); + + params.arguments << "project.version:2"; + QCOMPARE(runQbs(params), 0); + QFile output(relativeProductBuildDir("theProduct") + "/output.out"); + QVERIFY2(output.open(QIODevice::ReadOnly), qPrintable(output.errorString())); + const QByteArray version = output.readAll(); + QCOMPARE(version.constData(), "2"); +} + +void TestBlackbox::installedApp() +{ + QDir::setCurrent(testDataDir + "/installed_artifact"); + + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(defaultInstallRoot + + HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/bin/installedApp")))); + + QCOMPARE(runQbs(QbsRunParameters(QStringList("qbs.installRoot:" + testDataDir + + "/installed-app"))), 0); + QVERIFY(regularFileExists(testDataDir + + HostOsInfo::appendExecutableSuffix("/installed-app/usr/bin/installedApp"))); + + QFile addedFile(defaultInstallRoot + QLatin1String("/blubb.txt")); + QVERIFY(addedFile.open(QIODevice::WriteOnly)); + addedFile.close(); + QVERIFY(addedFile.exists()); + QCOMPARE(runQbs(QbsRunParameters(QStringList("--clean-install-root"))), 0); + QVERIFY(regularFileExists(defaultInstallRoot + + HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/bin/installedApp")))); + QVERIFY(regularFileExists(defaultInstallRoot + QLatin1String("/usr/src/main.cpp"))); + QVERIFY(!addedFile.exists()); + + // Check whether changing install parameters on the product causes re-installation. + QFile projectFile("installed_artifact.qbs"); + QVERIFY(projectFile.open(QIODevice::ReadWrite)); + QByteArray content = projectFile.readAll(); + content.replace("qbs.installPrefix: \"/usr\"", "qbs.installPrefix: '/usr/local'"); + WAIT_FOR_NEW_TIMESTAMP(); + projectFile.resize(0); + projectFile.write(content); + QVERIFY(projectFile.flush()); + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(defaultInstallRoot + + HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/local/bin/installedApp")))); + QVERIFY(regularFileExists(defaultInstallRoot + QLatin1String("/usr/local/src/main.cpp"))); + + // Check whether changing install parameters on the artifact causes re-installation. + content.replace("qbs.installDir: \"bin\"", "qbs.installDir: 'custom'"); + WAIT_FOR_NEW_TIMESTAMP(); + projectFile.resize(0); + projectFile.write(content); + QVERIFY(projectFile.flush()); + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(defaultInstallRoot + + HostOsInfo::appendExecutableSuffix(QLatin1String("/usr/local/custom/installedApp")))); + + // Check whether changing install parameters on a source file causes re-installation. + content.replace("qbs.installDir: \"src\"", "qbs.installDir: 'source'"); + WAIT_FOR_NEW_TIMESTAMP(); + projectFile.resize(0); + projectFile.write(content); + projectFile.close(); + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(defaultInstallRoot + QLatin1String("/usr/local/source/main.cpp"))); + + // Check whether changing install parameters on the command line causes re-installation. + QbsRunParameters(QStringList("qbs.installRoot:" + relativeBuildDir() + "/blubb")); + QCOMPARE(runQbs(QbsRunParameters(QStringList("qbs.installRoot:" + relativeBuildDir() + + "/blubb"))), 0); + QVERIFY(regularFileExists(relativeBuildDir() + "/blubb/usr/local/source/main.cpp")); + + // Check --no-install + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(QbsRunParameters(QStringList("--no-install"))), 0); + QCOMPARE(QDir(defaultInstallRoot).entryList(QDir::NoDotAndDotDot).count(), 0); + + // Check --no-build (with and without an existing build graph) + QbsRunParameters params("install", QStringList("--no-build")); + QCOMPARE(runQbs(params), 0); + rmDirR(relativeBuildDir()); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("No build graph"), m_qbsStderr.constData()); +} + +void TestBlackbox::installDuplicates() +{ + QDir::setCurrent(testDataDir + "/install-duplicates"); + + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY(m_qbsStderr.contains("Cannot install files")); +} + +void TestBlackbox::installedSourceFiles() +{ + QDir::setCurrent(testDataDir + "/installed-source-files"); + + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(defaultInstallRoot + QLatin1String("/readme.txt"))); + QVERIFY(regularFileExists(defaultInstallRoot + QLatin1String("/main.cpp"))); +} + +void TestBlackbox::toolLookup() +{ + QbsRunParameters params(QLatin1String("setup-toolchains"), QStringList("--help")); + params.useProfile = false; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::topLevelSearchPath() +{ + QDir::setCurrent(testDataDir + "/toplevel-searchpath"); + + QbsRunParameters params; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY2(m_qbsStderr.contains("MyProduct"), m_qbsStderr.constData()); + params.arguments << ("project.qbsSearchPaths:" + QDir::currentPath() + "/qbs-resources"); + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::checkProjectFilePath() +{ + QDir::setCurrent(testDataDir + "/project_filepath_check"); + QbsRunParameters params(QStringList("-f") << "project1.qbs"); + QCOMPARE(runQbs(params), 0); + QCOMPARE(runQbs(params), 0); + + params.arguments = QStringList("-f") << "project2.qbs"; + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); + QVERIFY(m_qbsStderr.contains("project file")); + + params.arguments = QStringList("-f") << "project2.qbs" << "--force"; + params.expectFailure = false; + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStderr.contains("project file")); +} + +class TemporaryDefaultProfileRemover +{ +public: + TemporaryDefaultProfileRemover(Settings *settings) + : m_settings(settings), m_defaultProfile(settings->defaultProfile()) + { + m_settings->remove(QLatin1String("defaultProfile")); + } + + ~TemporaryDefaultProfileRemover() + { + if (!m_defaultProfile.isEmpty()) + m_settings->setValue(QLatin1String("defaultProfile"), m_defaultProfile); + } + +private: + Settings *m_settings; + const QString m_defaultProfile; +}; + +void TestBlackbox::missingProfile() +{ + Settings settings((QString())); + TemporaryDefaultProfileRemover dpr(&settings); + settings.sync(); + QVERIFY(settings.defaultProfile().isEmpty()); + QDir::setCurrent(testDataDir + "/project_filepath_check"); + QbsRunParameters params; + params.arguments = QStringList("-f") << "project1.qbs"; + params.expectFailure = true; + params.useProfile = false; + QVERIFY(runQbs(params) != 0); + QVERIFY(m_qbsStderr.contains("No profile")); +} + +void TestBlackbox::assembly() +{ + Settings settings((QString())); + Profile profile(profileName(), &settings); + bool haveGcc = profile.value("qbs.toolchain").toStringList().contains("gcc"); + bool haveMSVC = profile.value("qbs.toolchain").toStringList().contains("msvc"); + QDir::setCurrent(testDataDir + "/assembly"); + QVERIFY(runQbs() == 0); + QCOMPARE(m_qbsStdout.contains("assembling testa.s"), haveGcc); + QCOMPARE(m_qbsStdout.contains("compiling testb.S"), haveGcc); + QCOMPARE(m_qbsStdout.contains("compiling testc.sx"), haveGcc); + QCOMPARE(m_qbsStdout.contains("creating libtesta.a"), haveGcc); + QCOMPARE(m_qbsStdout.contains("creating libtestb.a"), haveGcc); + QCOMPARE(m_qbsStdout.contains("creating libtestc.a"), haveGcc); + QCOMPARE(m_qbsStdout.contains("creating testd.lib"), haveMSVC); +} + +void TestBlackbox::nsis() +{ + QStringList regKeys; + regKeys << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS") + << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS"); + + QStringList paths = QProcessEnvironment::systemEnvironment().value("PATH") + .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + + foreach (const QString &key, regKeys) { + QSettings settings(key, QSettings::NativeFormat); + QString str = settings.value(QLatin1String(".")).toString(); + if (!str.isEmpty()) + paths.prepend(str); + } + + bool haveMakeNsis = false; + foreach (const QString &path, paths) { + if (regularFileExists(QDir::fromNativeSeparators(path) + + HostOsInfo::appendExecutableSuffix(QLatin1String("/makensis")))) { + haveMakeNsis = true; + break; + } + } + + if (!haveMakeNsis) { + QSKIP("makensis is not installed"); + return; + } + + bool targetIsWindows = targetOs() == HostOsInfo::HostOsWindows; + QDir::setCurrent(testDataDir + "/nsis"); + QVERIFY(runQbs() == 0); + QCOMPARE((bool)m_qbsStdout.contains("compiling hello.nsi"), targetIsWindows); + QCOMPARE((bool)m_qbsStdout.contains("SetCompressor ignored due to previous call with the /FINAL switch"), targetIsWindows); + QVERIFY(!QFile::exists(defaultInstallRoot + "/you-should-not-see-a-file-with-this-name.exe")); +} + +QString getEmbeddedBinaryPlist(const QString &file) +{ + QProcess p; + p.start("otool", QStringList() << "-v" << "-X" << "-s" << "__TEXT" << "__info_plist" << file); + p.waitForFinished(); + return QString::fromUtf8(p.readAllStandardOutput()).trimmed(); +} + +void TestBlackbox::embedInfoPlist() +{ + if (!HostOsInfo::isMacosHost()) + QSKIP("only applies on macOS"); + + QDir::setCurrent(testDataDir + QLatin1String("/embedInfoPlist")); + + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); + + QVERIFY(!getEmbeddedBinaryPlist(defaultInstallRoot + "/app").isEmpty()); + QVERIFY(!getEmbeddedBinaryPlist(defaultInstallRoot + "/liblib.dylib").isEmpty()); + QVERIFY(!getEmbeddedBinaryPlist(defaultInstallRoot + "/mod.bundle").isEmpty()); + + params.arguments = QStringList(QLatin1String("bundle.embedInfoPlist:false")); + params.expectFailure = true; + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(params), 0); + + QVERIFY(getEmbeddedBinaryPlist(defaultInstallRoot + "/app").isEmpty()); + QVERIFY(getEmbeddedBinaryPlist(defaultInstallRoot + "/liblib.dylib").isEmpty()); + QVERIFY(getEmbeddedBinaryPlist(defaultInstallRoot + "/mod.bundle").isEmpty()); +} + +void TestBlackbox::enableExceptions() +{ + QFETCH(QString, file); + QFETCH(bool, enable); + QFETCH(bool, expectSuccess); + + QDir::setCurrent(testDataDir + QStringLiteral("/enableExceptions")); + + QbsRunParameters params; + params.arguments = QStringList() << "-f" << file << (QStringLiteral("cpp.enableExceptions:") + + (enable ? "true" : "false")); + params.expectFailure = !expectSuccess; + rmDirR(relativeBuildDir()); + if (!params.expectFailure) + QCOMPARE(runQbs(params), 0); + else + QVERIFY(runQbs(params) != 0); +} + +void TestBlackbox::enableExceptions_data() +{ + QTest::addColumn("file"); + QTest::addColumn("enable"); + QTest::addColumn("expectSuccess"); + + QTest::newRow("no exceptions, enabled") << "none.qbs" << true << true; + QTest::newRow("no exceptions, disabled") << "none.qbs" << false << true; + + QTest::newRow("C++ exceptions, enabled") << "exceptions.qbs" << true << true; + QTest::newRow("C++ exceptions, disabled") << "exceptions.qbs" << false << false; + + if (HostOsInfo::isMacosHost()) { + QTest::newRow("Objective-C exceptions, enabled") << "exceptions-objc.qbs" << true << true; + QTest::newRow("Objective-C exceptions in Objective-C++ source, enabled") << "exceptions-objcpp.qbs" << true << true; + QTest::newRow("C++ exceptions in Objective-C++ source, enabled") << "exceptions-objcpp-cpp.qbs" << true << true; + QTest::newRow("Objective-C, disabled") << "exceptions-objc.qbs" << false << false; + QTest::newRow("Objective-C exceptions in Objective-C++ source, disabled") << "exceptions-objcpp.qbs" << false << false; + QTest::newRow("C++ exceptions in Objective-C++ source, disabled") << "exceptions-objcpp-cpp.qbs" << false << false; + } +} + +void TestBlackbox::enableRtti() +{ + QDir::setCurrent(testDataDir + QStringLiteral("/enableRtti")); + + QbsRunParameters params; + + params.arguments = QStringList() << "cpp.enableRtti:true"; + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(params), 0); + + if (HostOsInfo::isMacosHost()) { + params.arguments = QStringList() << "cpp.enableRtti:true" << "project.treatAsObjcpp:true"; + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(params), 0); + } + + params.expectFailure = true; + + params.arguments = QStringList() << "cpp.enableRtti:false"; + rmDirR(relativeBuildDir()); + QVERIFY(runQbs(params) != 0); + + if (HostOsInfo::isMacosHost()) { + params.arguments = QStringList() << "cpp.enableRtti:false" << "project.treatAsObjcpp:true"; + rmDirR(relativeBuildDir()); + QVERIFY(runQbs(params) != 0); + } +} + +void TestBlackbox::frameworkStructure() +{ + if (!HostOsInfo::isMacosHost()) + QSKIP("only applies on macOS"); + + QDir::setCurrent(testDataDir + QLatin1String("/frameworkStructure")); + + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); + + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/Widget")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/Headers/Widget.h")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/PrivateHeaders/WidgetPrivate.h")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/Resources/BaseResource")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/A/Resources/en.lproj/EnglishResource")); + QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/Versions/Current")); + QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Widget")); + QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/Headers")); + QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/PrivateHeaders")); + QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/Resources")); + + params.arguments = QStringList() << "project.includeHeaders:false"; + QCOMPARE(runQbs(params), 0); + + QVERIFY(!directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/Headers")); + QVERIFY(!directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/PrivateHeaders")); +} + +void TestBlackbox::generatedArtifactAsInputToDynamicRule() +{ + QDir::setCurrent(testDataDir + "/generated-artifact-as-input-to-dynamic-rule"); + QCOMPARE(runQbs(), 0); + const QString oldFile = relativeProductBuildDir("p") + "/old.txt"; + QVERIFY2(regularFileExists(oldFile), qPrintable(oldFile)); + WAIT_FOR_NEW_TIMESTAMP(); + QFile inputFile("input.txt"); + QVERIFY2(inputFile.open(QIODevice::WriteOnly), qPrintable(inputFile.errorString())); + inputFile.resize(0); + inputFile.write("new.txt"); + inputFile.close(); + QCOMPARE(runQbs(), 0); + QVERIFY2(!regularFileExists(oldFile), qPrintable(oldFile)); + const QString newFile = relativeProductBuildDir("p") + "/new.txt"; + QVERIFY2(regularFileExists(newFile), qPrintable(oldFile)); + QVERIFY2(m_qbsStdout.contains("generating"), m_qbsStdout.constData()); + QCOMPARE(runQbs(), 0); + QVERIFY2(!m_qbsStdout.contains("generating"), m_qbsStdout.constData()); +} + +void TestBlackbox::groupLocationWarning() +{ + QDir::setCurrent(testDataDir + "/group-location-warning"); + QCOMPARE(runQbs(QStringList() << "-f" << "group-location-warning.qbs"), 0); + QCOMPARE(m_qbsStderr.count("base directory"), 4); + QCOMPARE(m_qbsStderr.count("ParentInOtherDir.qbs"), 1); + QCOMPARE(m_qbsStderr.count("AGroupInOtherDir.qbs"), 1); + QCOMPARE(m_qbsStderr.count("gm.qbs"), 1); + QCOMPARE(m_qbsStderr.count("AndAnotherGroupInOtherDir.qbs"), 1); +} + +static bool haveWiX(const Profile &profile) +{ + if (profile.value("wix.toolchainInstallPath").isValid() && + profile.value("wix.toolchainInstallRoot").isValid()) { + return true; + } + + QStringList regKeys; + regKeys << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows Installer XML") + << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Installer XML"); + + QStringList paths = QProcessEnvironment::systemEnvironment().value("PATH") + .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + + foreach (const QString &key, regKeys) { + const QStringList versions = QSettings(key, QSettings::NativeFormat).childGroups(); + foreach (const QString &version, versions) { + QSettings settings(key + version, QSettings::NativeFormat); + QString str = settings.value(QLatin1String("InstallRoot")).toString(); + if (!str.isEmpty()) + paths.prepend(str); + } + } + + foreach (const QString &path, paths) { + if (regularFileExists(QDir::fromNativeSeparators(path) + + HostOsInfo::appendExecutableSuffix(QLatin1String("/candle"))) && + regularFileExists(QDir::fromNativeSeparators(path) + + HostOsInfo::appendExecutableSuffix(QLatin1String("/light")))) { + return true; + } + } + + return false; +} + +void TestBlackbox::wix() +{ + Settings settings((QString())); + Profile profile(profileName(), &settings); + + if (!haveWiX(profile)) { + QSKIP("WiX is not installed"); + return; + } + + const QByteArray arch = profile.value("qbs.architecture").toString().toLatin1(); + + QDir::setCurrent(testDataDir + "/wix"); + QbsRunParameters params; + if (!HostOsInfo::isWindowsHost()) + params.arguments << "qbs.targetOS:windows"; + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling QbsSetup.wxs")); + QVERIFY(m_qbsStdout.contains("linking qbs-" + arch + ".msi")); + QVERIFY(regularFileExists(relativeProductBuildDir("QbsSetup") + "/qbs-" + arch + ".msi")); + + if (HostOsInfo::isWindowsHost()) { + QVERIFY(m_qbsStdout.contains("compiling QbsBootstrapper.wxs")); + QVERIFY(m_qbsStdout.contains("linking qbs-setup-" + arch + ".exe")); + QVERIFY(regularFileExists(relativeProductBuildDir("QbsBootstrapper") + + "/qbs-setup-" + arch + ".exe")); + } +} + +void TestBlackbox::nodejs() +{ + Settings settings((QString())); + Profile p(profileName(), &settings); + + int status; + findNodejs(&status); + QCOMPARE(status, 0); + + QDir::setCurrent(testDataDir + QLatin1String("/nodejs")); + + status = runQbs(); + if (p.value("nodejs.toolchainInstallPath").toString().isEmpty() + && status != 0 && m_qbsStderr.contains("toolchainInstallPath")) { + QSKIP("nodejs.toolchainInstallPath not set and automatic detection failed"); + } + + if (p.value("nodejs.packageManagerPrefixPath").toString().isEmpty() + && status != 0 && m_qbsStderr.contains("nodejs.packageManagerPrefixPath")) { + QSKIP("nodejs.packageManagerFilePath not set and automatic detection failed"); + } + + QCOMPARE(status, 0); + + QbsRunParameters params; + params.command = QLatin1String("run"); + QCOMPARE(runQbs(params), 0); + QVERIFY((bool)m_qbsStdout.contains("hello world")); + QVERIFY(regularFileExists(relativeProductBuildDir("hello") + "/hello.js")); +} + +void TestBlackbox::typescript() +{ + Settings settings((QString())); + Profile p(profileName(), &settings); + + int status; + findTypeScript(&status); + QCOMPARE(status, 0); + + QDir::setCurrent(testDataDir + QLatin1String("/typescript")); + + status = runQbs(); + if (p.value("typescript.toolchainInstallPath").toString().isEmpty() && status != 0) { + if (m_qbsStderr.contains("typescript.toolchainInstallPath")) + QSKIP("typescript.toolchainInstallPath not set and automatic detection failed"); + if (m_qbsStderr.contains("nodejs.interpreterFilePath")) + QSKIP("nodejs.interpreterFilePath not set and automatic detection failed"); + } + + QCOMPARE(status, 0); + + QbsRunParameters params; + params.command = QLatin1String("run"); + params.arguments = QStringList() << "-p" << "animals"; + QCOMPARE(runQbs(params), 0); + + QVERIFY(regularFileExists(relativeProductBuildDir("animals") + "/animals.js")); + QVERIFY(regularFileExists(relativeProductBuildDir("animals") + "/extra.js")); + QVERIFY(regularFileExists(relativeProductBuildDir("animals") + "/main.js")); +} + +void TestBlackbox::iconset() +{ + if (!HostOsInfo::isMacosHost() || !isXcodeProfile(profileName())) + QSKIP("only applies on macOS with Xcode based profiles"); + + QDir::setCurrent(testDataDir + QLatin1String("/ib/iconset")); + + QbsRunParameters params; + params.arguments = QStringList() << "-f" << "iconset.qbs"; + QCOMPARE(runQbs(params), 0); + + QVERIFY(regularFileExists(relativeProductBuildDir("iconset") + "/white.icns")); +} + +void TestBlackbox::iconsetApp() +{ + if (!HostOsInfo::isMacosHost() || !isXcodeProfile(profileName())) + QSKIP("only applies on macOS with Xcode based profiles"); + + QDir::setCurrent(testDataDir + QLatin1String("/ib/iconsetapp")); + + QbsRunParameters params; + params.arguments = QStringList() << "-f" << "iconsetapp.qbs"; + QCOMPARE(runQbs(params), 0); + + QVERIFY(regularFileExists(relativeProductBuildDir("iconsetapp") + "/iconsetapp.app/Contents/Resources/white.icns")); +} + +void TestBlackbox::importInPropertiesCondition() +{ + QDir::setCurrent(testDataDir + "/import-in-properties-condition"); + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::importingProduct() +{ + QDir::setCurrent(testDataDir + "/importing-product"); + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::importsConflict() +{ + QDir::setCurrent(testDataDir + "/imports-conflict"); + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::infoPlist() +{ + if (!HostOsInfo::isMacosHost()) + QSKIP("only applies on macOS"); + + QDir::setCurrent(testDataDir + "/infoplist"); + + QbsRunParameters params; + params.arguments = QStringList() << "-f" << "infoplist.qbs"; + QCOMPARE(runQbs(params), 0); + + QFile infoplist(relativeProductBuildDir("infoplist") + "/infoplist.app/Contents/Info.plist"); + QVERIFY(infoplist.open(QIODevice::ReadOnly)); + const QByteArray fileContents = infoplist.readAll(); + QVERIFY2(fileContents.contains("LSMinimumSystemVersion"), fileContents.constData()); + QVERIFY2(fileContents.contains("10.7"), fileContents.constData()); + QVERIFY2(fileContents.contains("NSPrincipalClass"), fileContents.constData()); +} + +static bool haveInnoSetup(const Profile &profile) +{ + if (profile.value("innosetup.toolchainInstallPath").isValid()) + return true; + + QStringList regKeys; + regKeys << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Inno Setup 5_is1") + << QLatin1String("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Inno Setup 5_is1"); + + QStringList paths = QProcessEnvironment::systemEnvironment().value("PATH") + .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + + for (const QString &key : regKeys) { + QSettings settings(key, QSettings::NativeFormat); + QString str = settings.value(QLatin1String("InstallLocation")).toString(); + if (!str.isEmpty()) + paths.prepend(str); + } + + for (const QString &path : paths) { + if (regularFileExists(QDir::fromNativeSeparators(path) + + HostOsInfo::appendExecutableSuffix(QLatin1String("/ISCC")))) + return true; + } + + return false; +} + +void TestBlackbox::innoSetup() +{ + Settings settings((QString())); + Profile profile(profileName(), &settings); + + if (!haveInnoSetup(profile)) { + QSKIP("Inno Setup is not installed"); + return; + } + + QDir::setCurrent(testDataDir + "/innosetup"); + QbsRunParameters params; + if (!HostOsInfo::isWindowsHost()) + params.arguments << "qbs.targetOS:windows"; + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling test.iss")); + QVERIFY(m_qbsStdout.contains("compiling Example1.iss")); + QVERIFY(regularFileExists(relativeProductBuildDir("QbsSetup") + "/qbs.setup.test.exe")); + QVERIFY(regularFileExists(relativeProductBuildDir("Example1") + "/Example1.exe")); +} + +void TestBlackbox::assetCatalog() +{ + QFETCH(bool, flatten); + + if (!HostOsInfo::isMacosHost() || !isXcodeProfile(profileName())) + QSKIP("only applies on macOS with Xcode based profiles"); + + if (HostOsInfo::hostOsVersion() < qbs::Internal::Version(10, 9)) + QSKIP("This test needs at least macOS 10.9."); + + QDir::setCurrent(testDataDir + QLatin1String("/ib/assetcatalog")); + + rmDirR(relativeBuildDir()); + + QbsRunParameters params; + const QString flattens = "ib.flatten:" + QString(flatten ? "true" : "false"); + + // Make sure a dry run does not write anything + params.arguments = QStringList() << "-f" << "assetcatalogempty.qbs" << "--dry-run" << flattens; + QCOMPARE(runQbs(params), 0); + QVERIFY(!directoryExists(relativeBuildDir())); + + params.arguments = QStringList() << "-f" << "assetcatalogempty.qbs" << flattens; + QCOMPARE(runQbs(params), 0); + + // empty asset catalogs must still produce output + QVERIFY((bool)m_qbsStdout.contains("compiling empty.xcassets")); + + // should not produce a CAR since minimumMacosVersion will be < 10.9 + QVERIFY(!regularFileExists(relativeProductBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/Assets.car")); + + rmDirR(relativeBuildDir()); + params.arguments.append("cpp.minimumMacosVersion:10.9"); // force CAR generation + QCOMPARE(runQbs(params), 0); + + // empty asset catalogs must still produce output + QVERIFY((bool)m_qbsStdout.contains("compiling empty.xcassets")); + QVERIFY(regularFileExists(relativeProductBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/Assets.car")); + + // this asset catalog happens to have an embedded icon set, + // but this should NOT be built since it is not in the files list + QVERIFY(!(bool)m_qbsStdout.contains(".iconset")); + + // now we'll add the iconset + rmDirR(relativeBuildDir()); + params.arguments.append("project.includeIconset:true"); + QCOMPARE(runQbs(params), 0); + QVERIFY(!(bool)m_qbsStdout.contains("compiling empty.xcassets")); + QVERIFY((bool)m_qbsStdout.contains("compiling empty.iconset")); + + // make sure the nibs/storyboards are in there + QString nib = relativeProductBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/MainMenu.nib"; + QStringList nibFiles; + if (flatten) { + QVERIFY(regularFileExists(nib)); + } else { + QVERIFY(directoryExists(nib)); + nibFiles = QStringList() << "designable.nib" << "keyedobjects.nib"; + } + + QString storyboardc = relativeProductBuildDir("assetcatalogempty") + "/assetcatalogempty.app/Contents/Resources/Storyboard.storyboardc"; + QStringList storyboardcFiles; + if (HostOsInfo::hostOsVersion() >= qbs::Internal::Version(10, 10)) { + QVERIFY(directoryExists(storyboardc)); + + storyboardcFiles = QStringList() + << "1os-k8-h10-view-qKA-a5-eUe.nib" + << "Info.plist" + << "Iqk-Fi-Vhk-view-HRv-3O-Qxh.nib" + << "Main.nib" + << "NSViewController-Iqk-Fi-Vhk.nib" + << "NSViewController-Yem-rc-72E.nib" + << "Yem-rc-72E-view-ODp-aO-Dmf.nib"; + + if (!flatten) { + storyboardcFiles << "designable.storyboard"; + storyboardcFiles.sort(); + } + } + + QCOMPARE(QDir(nib).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name), nibFiles); + QCOMPARE(QDir(storyboardc).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name), storyboardcFiles); + QbsRunParameters params2 = params; + params2.command = "clean"; + QCOMPARE(runQbs(params2), 0); + QCOMPARE(QDir(nib).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name), QStringList()); + QCOMPARE(QDir(storyboardc).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name), QStringList()); + + QDir::setCurrent(testDataDir + QLatin1String("/ib/multiple-asset-catalogs")); + rmDirR(relativeBuildDir()); + params.arguments = QStringList(); + QCOMPARE(runQbs(params), 0); + QVERIFY2(m_qbsStdout.contains("compiling assetcatalog1.xcassets"), m_qbsStdout); + QVERIFY2(m_qbsStdout.contains("compiling assetcatalog2.xcassets"), m_qbsStdout); + + QDir::setCurrent(testDataDir + QLatin1String("/ib/empty-asset-catalogs")); + rmDirR(relativeBuildDir()); + params.arguments = QStringList(); + QCOMPARE(runQbs(params), 0); + QVERIFY2(!m_qbsStdout.contains("compiling assetcatalog1.xcassets"), m_qbsStdout); + QVERIFY2(!m_qbsStdout.contains("compiling assetcatalog2.xcassets"), m_qbsStdout); +} + +void TestBlackbox::assetCatalog_data() +{ + QTest::addColumn("flatten"); + QTest::newRow("flattened") << true; + QTest::newRow("unflattened") << false; +} + +void TestBlackbox::autoQrc() +{ + QDir::setCurrent(testDataDir + "/auto-qrc"); + QCOMPARE(runQbs(), 0); + QVERIFY2(m_qbsStdout.simplified().contains("resource data: resource1 resource2"), + m_qbsStdout.constData()); +} + +void TestBlackbox::objcArc() +{ + if (!HostOsInfo::isMacosHost()) + QSKIP("only applies on platforms supporting Objective-C"); + + QDir::setCurrent(testDataDir + QLatin1String("/objc-arc")); + + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::outputArtifactAutoTagging() +{ + QDir::setCurrent(testDataDir + QLatin1String("/output-artifact-auto-tagging")); + + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(relativeExecutableFilePath("output-artifact-auto-tagging"))); +} + +void TestBlackbox::wildCardsAndRules() +{ + QDir::setCurrent(testDataDir + "/wildcards-and-rules"); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("Creating output artifact")); + QFile output(relativeProductBuildDir("wildcards-and-rules") + "/test.mytype"); + QVERIFY2(output.open(QIODevice::ReadOnly), qPrintable(output.errorString())); + QCOMPARE(output.readAll().count('\n'), 1); + output.close(); + + // Add input. + touch("input2.inp"); + QbsRunParameters params; + params.expectFailure = true; + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("Creating output artifact")); + QVERIFY2(output.open(QIODevice::ReadOnly), qPrintable(output.errorString())); + QCOMPARE(output.readAll().count('\n'), 2); + output.close(); + + // Add "explicitlyDependsOn". + touch("dep.dep"); + QCOMPARE(runQbs(), 0); + QVERIFY(m_qbsStdout.contains("Creating output artifact")); + + // Add nothing. + QCOMPARE(runQbs(), 0); + QVERIFY(!m_qbsStdout.contains("Creating output artifact")); +} + +void TestBlackbox::loadableModule() +{ + QDir::setCurrent(testDataDir + QLatin1String("/loadablemodule")); + + QCOMPARE(runQbs(), 0); +} + +void TestBlackbox::lrelease() +{ + QDir::setCurrent(testDataDir + QLatin1String("/lrelease")); + QCOMPARE(runQbs(), 0); + QVERIFY(regularFileExists(relativeProductBuildDir("lrelease-test") + "/de.qm")); + QVERIFY(regularFileExists(relativeProductBuildDir("lrelease-test") + "/hu.qm")); + + QCOMPARE(runQbs(QString("clean")), 0); + QbsRunParameters params(QStringList({ "Qt.core.lreleaseMultiplexMode:true"})); + QCOMPARE(runQbs(params), 0); + QVERIFY(regularFileExists(relativeProductBuildDir("lrelease-test") + "/lrelease-test.qm")); + QVERIFY(!regularFileExists(relativeProductBuildDir("lrelease-test") + "/de.qm")); + QVERIFY(!regularFileExists(relativeProductBuildDir("lrelease-test") + "/hu.qm")); + + QCOMPARE(runQbs(QString("clean")), 0); + params.arguments << "Qt.core.qmBaseName:somename"; + QCOMPARE(runQbs(params), 0); + QVERIFY(regularFileExists(relativeProductBuildDir("lrelease-test") + "/somename.qm")); + QVERIFY(!regularFileExists(relativeProductBuildDir("lrelease-test") + "/lrelease-test.qm")); + QVERIFY(!regularFileExists(relativeProductBuildDir("lrelease-test") + "/de.qm")); + QVERIFY(!regularFileExists(relativeProductBuildDir("lrelease-test") + "/hu.qm")); +} + +void TestBlackbox::missingDependency() +{ + QDir::setCurrent(testDataDir + "/missing-dependency"); + QbsRunParameters params; + params.expectFailure = true; + params.arguments << "-p" << "theApp"; + QVERIFY(runQbs(params) != 0); + QVERIFY2(!m_qbsStderr.contains("ASSERT"), m_qbsStderr.constData()); + QCOMPARE(runQbs(QbsRunParameters(QStringList() << "-p" << "theDep")), 0); + params.expectFailure = false; + params.arguments << "-vv"; + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStderr.contains("false positive")); +} + +void TestBlackbox::badInterpreter() +{ + if (!HostOsInfo::isAnyUnixHost()) + QSKIP("only applies on Unix"); + + QDir::setCurrent(testDataDir + QLatin1String("/badInterpreter")); + QCOMPARE(runQbs(), 0); + + QbsRunParameters params("run"); + params.expectFailure = true; + + const QRegExp reNoSuchFileOrDir("bad interpreter:.* No such file or directory"); + const QRegExp rePermissionDenied("bad interpreter:.* Permission denied"); + + params.arguments = QStringList() << "-p" << "script-interp-missing"; + QCOMPARE(runQbs(params), 1); + QString strerr = QString::fromLocal8Bit(m_qbsStderr); + QVERIFY(strerr.contains(reNoSuchFileOrDir)); + + params.arguments = QStringList() << "-p" << "script-interp-noexec"; + QCOMPARE(runQbs(params), 1); + strerr = QString::fromLocal8Bit(m_qbsStderr); + QVERIFY(strerr.contains(reNoSuchFileOrDir) || strerr.contains(rePermissionDenied)); + + params.arguments = QStringList() << "-p" << "script-noexec"; + QCOMPARE(runQbs(params), 1); + QCOMPARE(runQbs(QbsRunParameters("run", QStringList() << "-p" << "script-ok")), 0); +} + +void TestBlackbox::qbsVersion() +{ + const qbs::Internal::Version v = qbs::Internal::Version::qbsVersion(); + QDir::setCurrent(testDataDir + QLatin1String("/qbsVersion")); + QbsRunParameters params; + params.arguments = QStringList() + << "project.qbsVersion:" + v.toString() + << "project.qbsVersionMajor:" + QString::number(v.majorVersion()) + << "project.qbsVersionMinor:" + QString::number(v.minorVersion()) + << "project.qbsVersionPatch:" + QString::number(v.patchLevel()); + QCOMPARE(runQbs(params), 0); + + params.arguments.append("project.qbsVersionPatch:" + QString::number(v.patchLevel() + 1)); + params.expectFailure = true; + QVERIFY(runQbs(params) != 0); +} + +void TestBlackbox::transitiveOptionalDependencies() +{ + QDir::setCurrent(testDataDir + "/transitive-optional-dependencies"); + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); +} + +void TestBlackbox::groupsInModules() +{ + QDir::setCurrent(testDataDir + "/groups-in-modules"); + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compile rock.coal => rock.diamond")); + QVERIFY(m_qbsStdout.contains("compile chunk.coal => chunk.diamond")); + QVERIFY(m_qbsStdout.contains("compiling helper2.c")); + QVERIFY(!m_qbsStdout.contains("compiling helper3.c")); + QVERIFY(m_qbsStdout.contains("compiling helper4.c")); + QVERIFY(m_qbsStdout.contains("compiling helper5.c")); + QVERIFY(!m_qbsStdout.contains("compiling helper6.c")); + + QCOMPARE(runQbs(params), 0); + QVERIFY(!m_qbsStdout.contains("compile rock.coal => rock.diamond")); + QVERIFY(!m_qbsStdout.contains("compile chunk.coal => chunk.diamond")); + + WAIT_FOR_NEW_TIMESTAMP(); + touch("modules/helper/diamondc.c"); + + QCOMPARE(runQbs(params), 0); + QVERIFY(m_qbsStdout.contains("compiling diamondc.c")); + QVERIFY(m_qbsStdout.contains("compile rock.coal => rock.diamond")); + QVERIFY(m_qbsStdout.contains("compile chunk.coal => chunk.diamond")); + QVERIFY(regularFileExists(relativeProductBuildDir("groups-in-modules") + "/rock.diamond")); + QFile output(relativeProductBuildDir("groups-in-modules") + "/rock.diamond"); + QVERIFY(output.open(QIODevice::ReadOnly)); + QCOMPARE(output.readAll().trimmed(), QByteArray("diamond")); +} + +void TestBlackbox::probesInNestedModules() +{ + QDir::setCurrent(testDataDir + "/probes-in-nested-modules"); + QbsRunParameters params; + QCOMPARE(runQbs(params), 0); + + QCOMPARE(m_qbsStdout.count("running probe a"), 1); + QCOMPARE(m_qbsStdout.count("running probe b"), 1); + QCOMPARE(m_qbsStdout.count("running probe c"), 1); + QCOMPARE(m_qbsStdout.count("running second probe a"), 1); + + QVERIFY(m_qbsStdout.contains("product a, outer.somethingElse = goodbye")); + QVERIFY(m_qbsStdout.contains("product b, inner.something = hahaha")); + QVERIFY(m_qbsStdout.contains("product c, inner.something = hello")); + + QVERIFY(m_qbsStdout.contains("product a, inner.something = hahaha")); + QVERIFY(m_qbsStdout.contains("product a, outer.something = hahaha")); +} + +void TestBlackbox::xcode() +{ + if (!HostOsInfo::isMacosHost() || !isXcodeProfile(profileName())) + QSKIP("only applies on macOS with Xcode based profiles"); + + QProcess xcodeSelect; + xcodeSelect.start("xcode-select", QStringList() << "--print-path"); + QVERIFY2(xcodeSelect.waitForStarted(), qPrintable(xcodeSelect.errorString())); + QVERIFY2(xcodeSelect.waitForFinished(), qPrintable(xcodeSelect.errorString())); + QVERIFY2(xcodeSelect.exitCode() == 0, qPrintable(xcodeSelect.readAllStandardError().constData())); + const QString developerPath(QString::fromLocal8Bit(xcodeSelect.readAllStandardOutput().trimmed())); + + QMultiMap sdks; + + QProcess xcodebuildShowSdks; + xcodebuildShowSdks.start("xcrun", QStringList() << "xcodebuild" << "-showsdks"); + QVERIFY2(xcodebuildShowSdks.waitForStarted(), qPrintable(xcodebuildShowSdks.errorString())); + QVERIFY2(xcodebuildShowSdks.waitForFinished(), qPrintable(xcodebuildShowSdks.errorString())); + QVERIFY2(xcodebuildShowSdks.exitCode() == 0, qPrintable(xcodebuildShowSdks.readAllStandardError().constData())); + for (const QString &line : QString::fromLocal8Bit(xcodebuildShowSdks.readAllStandardOutput().trimmed()).split('\n', QString::SkipEmptyParts)) { + static const QRegularExpression regexp(QStringLiteral("\\s+\\-sdk\\s+(?[a-z]+)(?[0-9]+\\.[0-9]+)$")); + QRegularExpressionMatch match = regexp.match(line); + if (match.isValid()) { + sdks.insert(match.captured("platform"), match.captured("version")); + } + } + + // values() returns items with most recently added first; we want the reverse of that + QStringList sdkValues(sdks.values("macosx")); + std::reverse(std::begin(sdkValues), std::end(sdkValues)); + + QDir::setCurrent(testDataDir + "/xcode"); + QbsRunParameters params; + params.arguments = (QStringList() + << (QStringLiteral("xcode.developerPath:") + developerPath) + << (QStringLiteral("project.sdks:['") + sdkValues.join("','") + "']")); + QCOMPARE(runQbs(params), 0); +} + +QTEST_MAIN(TestBlackbox) diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h new file mode 100644 index 00000000..b8d93b5c --- /dev/null +++ b/tests/auto/blackbox/tst_blackbox.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_BLACKBOX_H +#define TST_BLACKBOX_H + +#include "tst_blackboxbase.h" + +class TestBlackbox : public TestBlackboxBase +{ + Q_OBJECT + +public: + TestBlackbox(); + +private slots: + void alwaysRun(); + void alwaysRun_data(); + void assembly(); + void assetCatalog(); + void assetCatalog_data(); + void autoQrc(); + void badInterpreter(); + void buildDirectories(); + void bundleStructure(); + void bundleStructure_data(); + void changedFiles_data(); + void changedFiles(); + void changeInDisabledProduct(); + void changeInImportedFile(); + void checkProjectFilePath(); + void clean(); + void cli(); + void concurrentExecutor(); + void conditionalExport(); + void conflictingArtifacts(); + void dbusAdaptors(); + void dbusInterfaces(); + void dependenciesProperty(); + void dependencyProfileMismatch(); + void deploymentTarget(); + void deploymentTarget_data(); + void deprecatedProperty(); + void dynamicMultiplexRule(); + void dynamicRuleOutputs(); + void embedInfoPlist(); + void enableExceptions(); + void enableExceptions_data(); + void enableRtti(); + void erroneousFiles_data(); + void erroneousFiles(); + void errorInfo(); + void exportRule(); + void exportToOutsideSearchPath(); + void fileDependencies(); + void frameworkStructure(); + void generatedArtifactAsInputToDynamicRule(); + void groupLocationWarning(); + void groupsInModules(); + void iconset(); + void iconsetApp(); + void importInPropertiesCondition(); + void importingProduct(); + void importsConflict(); + void infoPlist(); + void innoSetup(); + void inputsFromDependencies(); + void installable(); + void installedApp(); + void installDuplicates(); + void installedSourceFiles(); + void installedTransformerOutput(); + void installPackage(); + void installTree(); + void invalidCommandProperty(); + void invalidExtensionInstantiation(); + void invalidExtensionInstantiation_data(); + void invalidLibraryNames(); + void invalidLibraryNames_data(); + void jsExtensionsFile(); + void jsExtensionsFileInfo(); + void jsExtensionsProcess(); + void jsExtensionsPropertyList(); + void jsExtensionsTemporaryDir(); + void jsExtensionsTextFile(); + void ld(); + void linkerMode(); + void lexyacc(); + void linkerScripts(); + void listPropertiesWithOuter(); + void listPropertyOrder(); + void loadableModule(); + void lrelease(); + void missingDependency(); + void missingProfile(); + void mixedBuildVariants(); + void mocFlags(); + void multipleChanges(); + void nestedGroups(); + void nestedProperties(); + void newOutputArtifact(); + void nodejs(); + void nonBrokenFilesInBrokenProduct(); + void nonDefaultProduct(); + void nsis(); + void objcArc(); + void outputArtifactAutoTagging(); + void overrideProjectProperties(); + void pchChangeTracking(); + void pkgConfigProbe(); + void pkgConfigProbe_data(); + void pkgConfigProbeSysroot(); + void pluginMetaData(); + void probeChangeTracking(); + void probeProperties(); + void probeInExportedModule(); + void probesAndArrayProperties(); + void probesInNestedModules(); + void productDependenciesByType(); + void productProperties(); + void propertyChanges(); + void propertyPrecedence(); + void properQuoting(); + void propertiesInExportItems(); + void qbsVersion(); + void qmlDebugging(); + void qobjectInObjectiveCpp(); + void qtBug51237(); + void qtScxml(); + void radAfterIncompleteBuild(); + void radAfterIncompleteBuild_data(); + void recursiveRenaming(); + void recursiveWildcards(); + void referenceErrorInExport(); + void reproducibleBuild(); + void reproducibleBuild_data(); + void responseFiles(); + void ruleConditions(); + void ruleCycle(); + void ruleWithNoInputs(); + void soVersion(); + void soVersion_data(); + void subProfileChangeTracking(); + void successiveChanges(); + void symlinkRemoval(); + void renameDependency(); + void separateDebugInfo(); + void sevenZip(); + void suspiciousCalls(); + void suspiciousCalls_data(); + void systemRunPaths(); + void systemRunPaths_data(); + void tar(); + void toolLookup(); + void topLevelSearchPath(); + void track_qobject_change(); + void track_qrc(); + void trackAddFile(); + void trackAddFileTag(); + void trackAddMocInclude(); + void trackAddProduct(); + void trackExternalProductChanges(); + void trackGroupConditionChange(); + void trackRemoveFile(); + void trackRemoveFileTag(); + void trackRemoveProduct(); + void transitiveOptionalDependencies(); + void typescript(); + void usingsAsSoleInputsNonMultiplexed(); + void versionCheck(); + void versionCheck_data(); + void versionScript(); + void wildCardsAndRules(); + void wildcardRenaming(); + void wix(); + void xcode(); + void zip(); + void zip_data(); + void zipInvalid(); + +private: + QMap findCli(int *status); + QMap findNodejs(int *status); + QMap findTypeScript(int *status); + QString findArchiver(const QString &fileName, int *status = nullptr); +}; + +#endif // TST_BLACKBOX_H diff --git a/tests/auto/blackbox/tst_blackboxbase.cpp b/tests/auto/blackbox/tst_blackboxbase.cpp new file mode 100644 index 00000000..98e15ad2 --- /dev/null +++ b/tests/auto/blackbox/tst_blackboxbase.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_blackboxbase.h" + +#include "../shared.h" + +#include +#include +#include +#include +#include + +#include + +using qbs::Internal::HostOsInfo; +using qbs::Internal::removeDirectoryWithContents; +using qbs::InstallOptions; +using qbs::Profile; +using qbs::Settings; + +static QString initQbsExecutableFilePath() +{ + QString filePath = QCoreApplication::applicationDirPath() + QLatin1String("/qbs"); + filePath = HostOsInfo::appendExecutableSuffix(QDir::cleanPath(filePath)); + return filePath; +} + +static bool supportsBuildDirectoryOption(const QString &command) { + return !(QStringList() << "help" << "config" << "config-ui" << "qmltypes" + << "setup-android" << "setup-qt" << "setup-toolchains").contains(command); +} + +TestBlackboxBase::TestBlackboxBase(const QString &testDataSrcDir, const QString &testName) + : testDataDir(testWorkDir(testName)), + testSourceDir(QDir::cleanPath(testDataSrcDir)), + qbsExecutableFilePath(initQbsExecutableFilePath()), + defaultInstallRoot(relativeBuildDir() + QLatin1Char('/') + InstallOptions::defaultInstallRoot()) +{ + QLocale::setDefault(QLocale::c()); +} + +int TestBlackboxBase::runQbs(const QbsRunParameters ¶ms) +{ + QStringList args; + if (!params.command.isEmpty()) + args << params.command; + if (supportsBuildDirectoryOption(params.command)) { + args.append(QLatin1String("-d")); + args.append(params.buildDirectory.isEmpty() ? QLatin1String(".") : params.buildDirectory); + } + args << params.arguments; + if (params.useProfile) + args.append(QLatin1String("profile:") + profileName()); + QProcess process; + process.setProcessEnvironment(params.environment); + process.start(qbsExecutableFilePath, args); + const int waitTime = 10 * 60000; + if (!process.waitForStarted() || !process.waitForFinished(waitTime)) { + m_qbsStderr = process.readAllStandardError(); + if (!params.expectFailure) + qDebug("%s", qPrintable(process.errorString())); + return -1; + } + + m_qbsStderr = process.readAllStandardError(); + m_qbsStdout = process.readAllStandardOutput(); + sanitizeOutput(&m_qbsStderr); + sanitizeOutput(&m_qbsStdout); + if ((process.exitStatus() != QProcess::NormalExit + || process.exitCode() != 0) && !params.expectFailure) { + if (!m_qbsStderr.isEmpty()) + qDebug("%s", m_qbsStderr.constData()); + if (!m_qbsStdout.isEmpty()) + qDebug("%s", m_qbsStdout.constData()); + } + return process.exitStatus() == QProcess::NormalExit ? process.exitCode() : -1; +} + +/*! + Recursive copy from directory to another. + Note that this updates the file stamps on Linux but not on Windows. + */ +void TestBlackboxBase::ccp(const QString &sourceDirPath, const QString &targetDirPath) +{ + QDir currentDir; + QDirIterator dit(sourceDirPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden); + while (dit.hasNext()) { + dit.next(); + const QString targetPath = targetDirPath + QLatin1Char('/') + dit.fileName(); + currentDir.mkpath(targetPath); + ccp(dit.filePath(), targetPath); + } + + QDirIterator fit(sourceDirPath, QDir::Files | QDir::Hidden); + while (fit.hasNext()) { + fit.next(); + const QString targetPath = targetDirPath + QLatin1Char('/') + fit.fileName(); + QFile::remove(targetPath); // allowed to fail + QFile src(fit.filePath()); + QVERIFY2(src.copy(targetPath), qPrintable(src.errorString())); + } +} + +void TestBlackboxBase::rmDirR(const QString &dir) +{ + QString errorMessage; + removeDirectoryWithContents(dir, &errorMessage); +} + +QByteArray TestBlackboxBase::unifiedLineEndings(const QByteArray &ba) +{ + if (HostOsInfo::isWindowsHost()) { + QByteArray result; + result.reserve(ba.size()); + for (int i = 0; i < ba.size(); ++i) { + char c = ba.at(i); + if (c != '\r') + result.append(c); + } + return result; + } else { + return ba; + } +} + +void TestBlackboxBase::sanitizeOutput(QByteArray *ba) +{ + if (HostOsInfo::isWindowsHost()) + ba->replace('\r', ""); +} + +void TestBlackboxBase::initTestCase() +{ + QVERIFY(regularFileExists(qbsExecutableFilePath)); + + Settings settings((QString())); + if (!settings.profiles().contains(profileName())) + QFAIL(QByteArray("The build profile '" + profileName().toLocal8Bit() + + "' could not be found. Please set it up on your machine.")); + + Profile buildProfile(profileName(), &settings); + QVariant qtBinPath = buildProfile.value(QLatin1String("Qt.core.binPath")); + if (!qtBinPath.isValid()) + QFAIL(QByteArray("The build profile '" + profileName().toLocal8Bit() + + "' is not a valid Qt profile.")); + if (!QFile::exists(qtBinPath.toString())) + QFAIL(QByteArray("The build profile '" + profileName().toLocal8Bit() + + "' points to an invalid Qt path.")); + + // Initialize the test data directory. + QVERIFY(testDataDir != testSourceDir); + rmDirR(testDataDir); + QDir().mkpath(testDataDir); + ccp(testSourceDir, testDataDir); + QDir().mkpath(testDataDir + "/find"); + ccp(testSourceDir + "/../find", testDataDir + "/find"); +} + +QString TestBlackboxBase::findExecutable(const QStringList &fileNames) +{ + const QStringList path = QString::fromLocal8Bit(qgetenv("PATH")) + .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts); + + foreach (const QString &fileName, fileNames) { + QFileInfo fi(fileName); + if (fi.isAbsolute()) + return fi.exists() ? fileName : QString(); + foreach (const QString &ppath, path) { + const QString fullPath + = HostOsInfo::appendExecutableSuffix(ppath + QLatin1Char('/') + fileName); + if (QFileInfo(fullPath).exists()) + return QDir::cleanPath(fullPath); + } + } + return QString(); +} + +QMap TestBlackboxBase::findJdkTools(int *status) +{ + QTemporaryDir temp; + QDir::setCurrent(testDataDir + "/find"); + QbsRunParameters params = QStringList() << "-f" << "find-jdk.qbs"; + params.buildDirectory = temp.path(); + const int res = runQbs(params); + if (status) + *status = res; + QFile file(temp.path() + "/" + relativeProductBuildDir("find-jdk") + "/jdk.json"); + if (!file.open(QIODevice::ReadOnly)) + return QMap { }; + const auto tools = QJsonDocument::fromJson(file.readAll()).toVariant().toMap(); + return QMap { + {"java", QDir::fromNativeSeparators(tools["java"].toString())}, + {"javac", QDir::fromNativeSeparators(tools["javac"].toString())}, + {"jar", QDir::fromNativeSeparators(tools["jar"].toString())} + }; +} diff --git a/tests/auto/blackbox/tst_blackboxbase.h b/tests/auto/blackbox/tst_blackboxbase.h new file mode 100644 index 00000000..8ffde6e5 --- /dev/null +++ b/tests/auto/blackbox/tst_blackboxbase.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef TST_BLACKBOXBASE_H +#define TST_BLACKBOXBASE_H + +#include +#include +#include + +class QbsRunParameters +{ +public: + QbsRunParameters() + { + init(); + } + + QbsRunParameters(const QString &cmd, const QStringList &args = QStringList()) + : command(cmd), arguments(args) + { + init(); + } + + QbsRunParameters(const QStringList &args) + : arguments(args) + { + init(); + } + + void init() + { + expectFailure = false; + useProfile = true; + environment = QProcessEnvironment::systemEnvironment(); + } + + QString command; + QStringList arguments; + QString buildDirectory; + QProcessEnvironment environment; + bool expectFailure; + bool useProfile; +}; + +class TestBlackboxBase : public QObject +{ + Q_OBJECT +public: + TestBlackboxBase(const QString &testDataSrcDir, const QString &testName); + +public slots: + void initTestCase(); + +protected: + int runQbs(const QbsRunParameters ¶ms = QbsRunParameters()); + void rmDirR(const QString &dir); + static QByteArray unifiedLineEndings(const QByteArray &ba); + static void sanitizeOutput(QByteArray *ba); + static void ccp(const QString &sourceDirPath, const QString &targetDirPath); + static QString findExecutable(const QStringList &fileNames); + QMap findJdkTools(int *status); + + const QString testDataDir; + const QString testSourceDir; + const QString qbsExecutableFilePath; + const QString defaultInstallRoot; + + QByteArray m_qbsStderr; + QByteArray m_qbsStdout; +}; + +#endif // TST_BLACKBOXBASE_H diff --git a/tests/auto/blackbox/tst_blackboxjava.cpp b/tests/auto/blackbox/tst_blackboxjava.cpp new file mode 100644 index 00000000..8268d6ad --- /dev/null +++ b/tests/auto/blackbox/tst_blackboxjava.cpp @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_blackboxjava.h" + +#include "../shared.h" +#include +#include +#include + +#include +#include + +using qbs::Internal::HostOsInfo; +using qbs::Profile; +using qbs::Settings; + +QMap TestBlackboxJava::findAndroid(int *status) +{ + QTemporaryDir temp; + QDir::setCurrent(testDataDir + "/find"); + QbsRunParameters params = QStringList() << "-f" << "find-android.qbs"; + params.buildDirectory = temp.path(); + const int res = runQbs(params); + if (status) + *status = res; + QFile file(temp.path() + "/" + relativeProductBuildDir("find-android") + "/android.json"); + if (!file.open(QIODevice::ReadOnly)) + return QMap { }; + const auto tools = QJsonDocument::fromJson(file.readAll()).toVariant().toMap(); + return QMap { + {"sdk", QDir::fromNativeSeparators(tools["sdk"].toString())}, + {"ndk", QDir::fromNativeSeparators(tools["ndk"].toString())}, + }; +} + +TestBlackboxJava::TestBlackboxJava() : TestBlackboxBase (SRCDIR "/testdata-java", "blackbox-java") +{ +} + +void TestBlackboxJava::android() +{ + QFETCH(QString, projectDir); + QFETCH(QStringList, productNames); + QFETCH(QList, apkFileCounts); + + int status; + const auto androidPaths = findAndroid(&status); + + const auto ndkPath = androidPaths["ndk"]; + static const QStringList ndkSamplesDirs = QStringList() << "teapot" << "no-native"; + if (!ndkPath.isEmpty() && !QFileInfo(ndkPath + "/samples").isDir() + && ndkSamplesDirs.contains(projectDir)) + QSKIP("NDK samples directory not present"); + + QDir::setCurrent(testDataDir + "/android/" + projectDir); + Settings s((QString())); + Profile p("qbs_autotests-android", &s); + if (!p.exists() || (status != 0 && !p.value("Android.sdk.ndkDir").isValid())) + QSKIP("No suitable Android test profile"); + QbsRunParameters params(QStringList("profile:" + p.name()) + << "Android.ndk.platform:android-21"); + params.useProfile = false; + QCOMPARE(runQbs(params), 0); + for (int i = 0; i < productNames.count(); ++i) { + const QString productName = productNames.at(i); + QVERIFY(m_qbsStdout.contains("Creating " + productName.toLocal8Bit() + ".apk")); + const QString apkFilePath = relativeProductBuildDir(productName, p.name()) + + '/' + productName + ".apk"; + QVERIFY2(regularFileExists(apkFilePath), qPrintable(apkFilePath)); + const QString jarFilePath = findExecutable(QStringList("jar")); + QVERIFY(!jarFilePath.isEmpty()); + QProcess jar; + jar.start(jarFilePath, QStringList() << "-tf" << apkFilePath); + QVERIFY2(jar.waitForStarted(), qPrintable(jar.errorString())); + QVERIFY2(jar.waitForFinished(), qPrintable(jar.errorString())); + QVERIFY2(jar.exitCode() == 0, qPrintable(jar.readAllStandardError().constData())); + QCOMPARE(jar.readAllStandardOutput().trimmed().split('\n').count(), apkFileCounts.at(i)); + } +} + +void TestBlackboxJava::android_data() +{ + QTest::addColumn("projectDir"); + QTest::addColumn("productNames"); + QTest::addColumn>("apkFileCounts"); + QTest::newRow("teapot") << "teapot" << QStringList("com.sample.teapot") << (QList() << 31); + QTest::newRow("no native") << "no-native" + << QStringList("com.example.android.basicmediadecoder") << (QList() << 22); + QTest::newRow("multiple libs") << "multiple-libs-per-apk" << QStringList("twolibs") + << (QList() << 10); + QTest::newRow("multiple apks") << "multiple-apks-per-project" + << (QStringList() << "twolibs1" << "twolibs2") + << (QList() << 15 << 9); +} + +static QProcessEnvironment processEnvironmentWithCurrentDirectoryInLibraryPath() +{ + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(HostOsInfo::libraryPathEnvironmentVariable(), + (QStringList() << env.value(HostOsInfo::libraryPathEnvironmentVariable()) << ".") + .join(HostOsInfo::pathListSeparator())); + return env; +} + +void TestBlackboxJava::java() +{ +#if defined(Q_OS_WIN32) && !defined(Q_OS_WIN64) + QSKIP("QTBUG-3845"); +#endif + + Settings settings((QString())); + Profile p(profileName(), &settings); + + int status; + const auto jdkTools = findJdkTools(&status); + QCOMPARE(status, 0); + + QDir::setCurrent(testDataDir + "/java"); + + status = runQbs(); + if (p.value("java.jdkPath").toString().isEmpty() + && status != 0 && m_qbsStderr.contains("jdkPath")) { + QSKIP("java.jdkPath not set and automatic detection failed"); + } + + QCOMPARE(status, 0); + + const QStringList classFiles = + QStringList() << "Jet" << "Ship" << "Vehicles"; + QStringList classFiles1 = QStringList(classFiles) << "io/qt/qbs/HelloWorld" << "NoPackage"; + for (int i = 0; i < classFiles1.count(); ++i) { + QString &classFile = classFiles1[i]; + classFile = relativeProductBuildDir("class_collection") + "/classes/" + + classFile + ".class"; + QVERIFY2(regularFileExists(classFile), qPrintable(classFile)); + } + + foreach (const QString &classFile, classFiles) { + const QString filePath = relativeProductBuildDir("jar_file") + "/classes/" + classFile + + ".class"; + QVERIFY2(regularFileExists(filePath), qPrintable(filePath)); + } + const QString jarFilePath = relativeProductBuildDir("jar_file") + '/' + "jar_file.jar"; + QVERIFY2(regularFileExists(jarFilePath), qPrintable(jarFilePath)); + + // Now check whether we correctly predicted the class file output paths. + QCOMPARE(runQbs(QbsRunParameters("clean")), 0); + foreach (const QString &classFile, classFiles1) { + QVERIFY2(!regularFileExists(classFile), qPrintable(classFile)); + } + + // This tests various things: java.manifestClassPath, JNI, etc. + QDir::setCurrent(relativeBuildDir() + "/install-root"); + QProcess process; + process.setProcessEnvironment(processEnvironmentWithCurrentDirectoryInLibraryPath()); + process.start(HostOsInfo::appendExecutableSuffix(jdkTools["java"]), + QStringList() << "-jar" << "jar_file.jar"); + if (process.waitForStarted()) { + QVERIFY2(process.waitForFinished(), qPrintable(process.errorString())); + QVERIFY2(process.exitCode() == 0, process.readAllStandardError().constData()); + const QByteArray stdOut = process.readAllStandardOutput(); + QVERIFY2(stdOut.contains("Driving!"), stdOut.constData()); + QVERIFY2(stdOut.contains("Flying!"), stdOut.constData()); + QVERIFY2(stdOut.contains("Flying (this is a space ship)!"), stdOut.constData()); + QVERIFY2(stdOut.contains("Sailing!"), stdOut.constData()); + QVERIFY2(stdOut.contains("Native code performing complex internal combustion process ("), + stdOut.constData()); + } + + process.start("unzip", QStringList() << "-p" << "jar_file.jar"); + if (process.waitForStarted()) { + QVERIFY2(process.waitForFinished(), qPrintable(process.errorString())); + const QByteArray stdOut = process.readAllStandardOutput(); + QVERIFY2(stdOut.contains("Class-Path: car_jar.jar random_stuff.jar"), stdOut.constData()); + QVERIFY2(stdOut.contains("Main-Class: Vehicles"), stdOut.constData()); + } +} + +static QString dpkgArch(const QString &prefix = QString()) +{ + QProcess dpkg; + dpkg.start("/usr/bin/dpkg", QStringList() << "--print-architecture"); + dpkg.waitForFinished(); + if (dpkg.exitStatus() == QProcess::NormalExit && dpkg.exitCode() == 0) + return prefix + QString::fromLocal8Bit(dpkg.readAllStandardOutput().trimmed()); + return QString(); +} + +void TestBlackboxJava::javaDependencyTracking() +{ + Settings settings((QString())); + Profile p(profileName(), &settings); + + auto getSpecificJdkVersion = [](const QString &jdkVersion) -> QString { + if (HostOsInfo::isMacosHost()) { + QProcess java_home; + java_home.start("/usr/libexec/java_home", QStringList() << "--version" << jdkVersion); + java_home.waitForFinished(); + if (java_home.exitStatus() == QProcess::NormalExit && java_home.exitCode() == 0) + return QString::fromLocal8Bit(java_home.readAllStandardOutput().trimmed()); + } else if (HostOsInfo::isWindowsHost()) { + QSettings settings("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit\\" + + jdkVersion, QSettings::NativeFormat); + return settings.value("JavaHome").toString(); + } else { + QString minorVersion = jdkVersion; + if (minorVersion.startsWith("1.")) + minorVersion.remove(0, 2); + + const QStringList searchPaths = { + "/usr/lib/jvm/java-" + minorVersion + "-openjdk" + dpkgArch("-"), // Debian + "/usr/lib/jvm/java-" + minorVersion + "-openjdk", // Arch + "/usr/lib/jvm/jre-1." + minorVersion + ".0-openjdk", // Fedora + }; + for (const QString &searchPath : searchPaths) { + if (QFile::exists(searchPath + "/bin/javac")) + return searchPath; + } + } + + return QString(); + }; + + auto runQbsTest = [&](const QString &jdkPath, const QString &javaVersion, + const QString &arg) { + QDir::setCurrent(testDataDir + "/java"); + QbsRunParameters rp; + rp.arguments.append(arg); + if (!jdkPath.isEmpty()) + rp.arguments << ("java.jdkPath:" + jdkPath); + if (!javaVersion.isEmpty()) + rp.arguments << ("java.languageVersion:'" + javaVersion + "'"); + rmDirR(relativeBuildDir()); + QCOMPARE(runQbs(rp), 0); + }; + + static const auto knownJdkVersions = QStringList() << "1.6" << "1.7" << "1.8" << "1.9" + << QString(); // default JDK; + QStringList seenJdkVersions; + for (const auto &jdkVersion : knownJdkVersions) { + QString specificJdkPath = getSpecificJdkVersion(jdkVersion); + if (jdkVersion.isEmpty() || !specificJdkPath.isEmpty()) { + const auto jdkPath = jdkVersion.isEmpty() ? jdkVersion : specificJdkPath; + + if (!jdkVersion.isEmpty()) + seenJdkVersions << jdkVersion; + + if (!seenJdkVersions.isEmpty()) { + const auto javaVersions = QStringList() + << knownJdkVersions.mid(0, knownJdkVersions.indexOf(seenJdkVersions.last()) + 1) + << QString(); // also test with no explicitly specified source version + + for (const auto ¤tJavaVersion : javaVersions) { + runQbsTest(jdkPath, currentJavaVersion, "--check-outputs"); + runQbsTest(jdkPath, currentJavaVersion, "--dry-run"); + } + } + } + } + + if (seenJdkVersions.isEmpty()) + QSKIP("No JDKs installed"); +} + +void TestBlackboxJava::javaDependencyTrackingInnerClass() +{ + Settings settings((QString())); + Profile p(profileName(), &settings); + + QDir::setCurrent(testDataDir + "/java/inner-class"); + QbsRunParameters params; + params.expectFailure = true; + int status = runQbs(params); + if (p.value("java.jdkPath").toString().isEmpty() + && status != 0 && m_qbsStderr.contains("jdkPath")) { + QSKIP("java.jdkPath not set and automatic detection failed"); + } + QCOMPARE(status, 0); + QEXPECT_FAIL(0, "QBS-1069", Abort); + QVERIFY(!m_qbsStderr.contains("QBS-1069")); +} + +QTEST_MAIN(TestBlackboxJava) diff --git a/tests/auto/blackbox/tst_blackboxjava.h b/tests/auto/blackbox/tst_blackboxjava.h new file mode 100644 index 00000000..40f1b163 --- /dev/null +++ b/tests/auto/blackbox/tst_blackboxjava.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_BLACKBOX_H +#define TST_BLACKBOX_H + +#include "tst_blackboxbase.h" + +class TestBlackboxJava : public TestBlackboxBase +{ + Q_OBJECT + +public: + TestBlackboxJava(); + +private slots: + void android(); + void android_data(); + void java(); + void javaDependencyTracking(); + void javaDependencyTrackingInnerClass(); + +private: + QMap findAndroid(int *status); +}; + +#endif // TST_BLACKBOX_H diff --git a/tests/auto/blackbox/tst_clangdb.cpp b/tests/auto/blackbox/tst_clangdb.cpp new file mode 100644 index 00000000..7ef379c7 --- /dev/null +++ b/tests/auto/blackbox/tst_clangdb.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2016 Christian Gagneraud. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_clangdb.h" + +#include "../shared.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +using qbs::InstallOptions; +using qbs::Internal::HostOsInfo; +using qbs::Internal::Version; + +int TestClangDb::runProcess(const QString &exec, const QStringList &args, QByteArray &stdErr, + QByteArray &stdOut) +{ + QProcess process; + + process.start(exec, args); + const int waitTime = 10 * 60000; + if (!process.waitForStarted() || !process.waitForFinished(waitTime)) { + stdErr = process.readAllStandardError(); + return -1; + } + + stdErr = process.readAllStandardError(); + stdOut = process.readAllStandardOutput(); + sanitizeOutput(&stdErr); + sanitizeOutput(&stdOut); + + if (process.exitStatus() != QProcess::NormalExit || process.exitCode() != 0) { + if (!stdErr.isEmpty()) + qDebug("%s", stdErr.constData()); + if (!stdOut.isEmpty()) + qDebug("%s", stdOut.constData()); + } + + return process.exitStatus() == QProcess::NormalExit ? process.exitCode() : -1; +} + +Version TestClangDb::clangVersion() +{ + QByteArray stdErr; + QByteArray stdOut; + if (runProcess("clang-check", QStringList("--version"), stdErr, stdOut) != 0) + return Version(); + stdOut.remove(0, stdOut.indexOf("LLVM version ") + 13); + stdOut.truncate(stdOut.indexOf('\n')); + return Version::fromString(QString::fromLocal8Bit(stdOut)); +} + + +TestClangDb::TestClangDb() : TestBlackboxBase(SRCDIR "/testdata-clangdb", "blackbox-clangdb"), + projectDir(QDir::cleanPath(testDataDir + "/project1")), + projectFileName("project.qbs"), + buildDir(QDir::cleanPath(projectDir + "/" + relativeBuildDir())), + sourceFilePath(QDir::cleanPath(projectDir + "/i like spaces.cpp")), + dbFilePath(QDir::cleanPath(buildDir + "/compile_commands.json")) +{ +} + +void TestClangDb::initTestCase() +{ + TestBlackboxBase::initTestCase(); + QDir::setCurrent(projectDir); +} + +void TestClangDb::ensureBuildTreeCreated() +{ + QCOMPARE(runQbs(), 0); + QVERIFY(QFile::exists(buildDir)); +} + +void TestClangDb::checkCanGenerateDb() +{ + QbsRunParameters params; + params.command = "generate"; + params.arguments << "--generator" << "clangdb"; + QCOMPARE(runQbs(params), 0); + QVERIFY(QFile::exists(dbFilePath)); +} + +void TestClangDb::checkDbIsValidJson() +{ + QFile file(dbFilePath); + QVERIFY(file.open(QFile::ReadOnly)); + const QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + QVERIFY(!doc.isNull()); + QVERIFY(doc.isArray()); +} + +void TestClangDb::checkDbIsConsistentWithProject() +{ + QFile file(dbFilePath); + QVERIFY(file.open(QFile::ReadOnly)); + const QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); + + // We expect only one command for now + const QJsonArray array = doc.array(); + QVERIFY(array.count() == 1); + + // Validate the "command object" + const QJsonObject entry = array.at(0).toObject(); + QVERIFY(entry.contains("directory")); + QVERIFY(entry.value("directory").isString()); + QVERIFY(entry.contains("arguments")); + QVERIFY(entry.value("arguments").isArray()); + QVERIFY(entry.value("arguments").toArray().count() >= 2); + QVERIFY(entry.contains("file")); + QVERIFY(entry.value("file").isString()); + QVERIFY(entry.value("file").toString() == sourceFilePath); + + qbs::Settings settings((QString())); + qbs::Profile profile(profileName(), &settings); + if (profile.value("qbs.toolchain").toStringList().contains("msvc")) + QSKIP("MSVC command line is not self-contained"); + + // Validate the compile command itself, this requires a previous build since the command + // line contains 'deep' path that are created during Qbs build run + QByteArray stdErr; + QByteArray stdOut; + QStringList arguments; + const QJsonArray jsonArguments = entry.value("arguments").toArray(); + QString executable = jsonArguments.at(0).toString(); + for (int i=1; i/i like spaces.cpp:11:5: warning: Assigned value is garbage or undefined +// int unused = garbage; +// ^~~~~~~~~~ ~~~~~~~ +// <...>/i like spaces.cpp:11:9: warning: Value stored to 'unused' during its initialization is never read +// int unused = garbage; +// ^~~~~~ ~~~~~~~ +// 2 warnings generated. +void TestClangDb::checkClangDetectsSourceCodeProblems() +{ + QByteArray stdErr; + QByteArray stdOut; + QStringList arguments; + const QString executable = findExecutable(QStringList("clang-check")); + if (executable.isEmpty()) + QSKIP("No working clang-check executable found"); + + // Older clang versions do not support the "arguments" array in the compilation database. + // Should we really want to support them, we would have to fall back to "command" instead. + if (clangVersion() < Version(3, 7)) + QSKIP("This test requires clang-check to be based on at least LLVM 3.7.0."); + + arguments = QStringList() << "-analyze" << "-p" << relativeBuildDir() << sourceFilePath; + QVERIFY(runProcess(executable, arguments, stdErr, stdOut) == 0); + const QString output = QString::fromLocal8Bit(stdErr); + QVERIFY(output.contains(QRegExp(QStringLiteral("warning.*undefined"), Qt::CaseInsensitive))); + QVERIFY(output.contains(QRegExp(QStringLiteral("warning.*never read"), Qt::CaseInsensitive))); +} + +QTEST_MAIN(TestClangDb) diff --git a/tests/auto/blackbox/tst_clangdb.h b/tests/auto/blackbox/tst_clangdb.h new file mode 100644 index 00000000..1aa33c4d --- /dev/null +++ b/tests/auto/blackbox/tst_clangdb.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QBS_TST_CLANGDB_H +#define QBS_TST_CLANGDB_H + +#include "tst_blackbox.h" + +#include + +class TestClangDb : public TestBlackboxBase +{ + Q_OBJECT + +public: + TestClangDb(); + +private slots: + void initTestCase(); + void ensureBuildTreeCreated(); + void checkCanGenerateDb(); + void checkDbIsValidJson(); + void checkDbIsConsistentWithProject(); + void checkClangDetectsSourceCodeProblems(); + +private: + static int runProcess(const QString &exec, const QStringList &args, QByteArray &stdErr, + QByteArray &stdOut); + static qbs::Internal::Version clangVersion(); + + const QString projectDir; + const QString projectFileName; + const QString buildDir; + const QString sourceFilePath; + const QString dbFilePath; +}; + +#endif // Include guard. diff --git a/tests/auto/buildgraph/buildgraph.pro b/tests/auto/buildgraph/buildgraph.pro new file mode 100644 index 00000000..4388aac1 --- /dev/null +++ b/tests/auto/buildgraph/buildgraph.pro @@ -0,0 +1,6 @@ +TARGET = tst_buildgraph + +SOURCES = tst_buildgraph.cpp + +include(../auto.pri) +include(../../../src/app/shared/logging/logging.pri) diff --git a/tests/auto/buildgraph/buildgraph.qbs b/tests/auto/buildgraph/buildgraph.qbs new file mode 100644 index 00000000..f6c1cd1f --- /dev/null +++ b/tests/auto/buildgraph/buildgraph.qbs @@ -0,0 +1,7 @@ +import qbs + +QbsAutotest { + testName: "buildgraph" + condition: qbsbuildconfig.enableUnitTests + files: "tst_buildgraph.cpp" +} diff --git a/tests/auto/buildgraph/tst_buildgraph.cpp b/tests/auto/buildgraph/tst_buildgraph.cpp new file mode 100644 index 00000000..1c79d521 --- /dev/null +++ b/tests/auto/buildgraph/tst_buildgraph.cpp @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + qbs::Internal::TestBuildGraph tbg(ConsoleLogger::instance().logSink()); + return QTest::qExec(&tbg, argc, argv); +} diff --git a/tests/auto/cmdlineparser/cmdlineparser.pro b/tests/auto/cmdlineparser/cmdlineparser.pro new file mode 100644 index 00000000..a95676be --- /dev/null +++ b/tests/auto/cmdlineparser/cmdlineparser.pro @@ -0,0 +1,7 @@ +TARGET = tst_cmdlineparser + +SOURCES = tst_cmdlineparser.cpp ../../../src/app/qbs/qbstool.cpp + +include(../auto.pri) +include(../../../src/app/qbs/parser/parser.pri) +include(../../../src/app/shared/logging/logging.pri) diff --git a/tests/auto/cmdlineparser/cmdlineparser.qbs b/tests/auto/cmdlineparser/cmdlineparser.qbs new file mode 100644 index 00000000..ddd0e62c --- /dev/null +++ b/tests/auto/cmdlineparser/cmdlineparser.qbs @@ -0,0 +1,30 @@ +import qbs +import QbsFunctions + +QbsAutotest { + testName: "cmdlineparser" + files: ["tst_cmdlineparser.cpp", "../../../src/app/qbs/qbstool.cpp"] + cpp.defines: base.concat([ + 'SRCDIR="' + path + '"', + "QBS_VERSION=\"" + QbsFunctions.qbsVersion() + "\"" + ]) + + // TODO: Make parser a static library? + Group { + name: "parser" + prefix: "../../../src/app/qbs/parser/" + files: [ + "command.cpp", + "command.h", + "commandlineoption.cpp", + "commandlineoption.h", + "commandlineoptionpool.cpp", + "commandlineoptionpool.h", + "commandlineparser.cpp", + "commandlineparser.h", + "commandpool.cpp", + "commandpool.h", + "commandtype.h", + ] + } +} diff --git a/tests/auto/cmdlineparser/data/dirwithmultipleprojects/project.qbs b/tests/auto/cmdlineparser/data/dirwithmultipleprojects/project.qbs new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/cmdlineparser/data/dirwithmultipleprojects/project2.qbs b/tests/auto/cmdlineparser/data/dirwithmultipleprojects/project2.qbs new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/cmdlineparser/data/dirwithnoprojects/.gitignore b/tests/auto/cmdlineparser/data/dirwithnoprojects/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/tests/auto/cmdlineparser/data/dirwithnoprojects/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/auto/cmdlineparser/data/dirwithoneproject/project.qbs b/tests/auto/cmdlineparser/data/dirwithoneproject/project.qbs new file mode 100644 index 00000000..e69de29b diff --git a/tests/auto/cmdlineparser/tst_cmdlineparser.cpp b/tests/auto/cmdlineparser/tst_cmdlineparser.cpp new file mode 100644 index 00000000..52570e80 --- /dev/null +++ b/tests/auto/cmdlineparser/tst_cmdlineparser.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace qbs; + +class TestCmdLineParser : public QObject +{ + Q_OBJECT +public: + TestCmdLineParser() + { + ConsoleLogger::instance().logSink()->setEnabled(false); + } + +private slots: + void testValidCommandLine() + { + QTemporaryFile projectFile; + QVERIFY(projectFile.open()); + const QStringList fileArgs = QStringList() << "-f" << projectFile.fileName(); + QStringList args; + args.append("-vvk"); + args.append("-v"); + args << "--products" << "blubb"; + args << "--changed-files" << "foo,bar" << fileArgs; + args << "--force"; + args << "--check-timestamps"; + args << "--check-outputs"; + CommandLineParser parser; + + QVERIFY(parser.parseCommandLine(args)); + QCOMPARE(ConsoleLogger::instance().logSink()->logLevel(), LoggerTrace); + QCOMPARE(parser.command(), BuildCommandType); + QCOMPARE(parser.products(), QStringList() << "blubb"); + QCOMPARE(parser.buildOptions(QString()).changedFiles().count(), 2); + QVERIFY(parser.buildOptions(QString()).keepGoing()); + QVERIFY(parser.force()); + QVERIFY(parser.forceTimestampCheck()); + QVERIFY(parser.forceOutputCheck()); + QVERIFY(!parser.logTime()); + QCOMPARE(parser.buildConfigurations().count(), 1); + + QVERIFY(parser.parseCommandLine(QStringList() << "-vvvqqq" << fileArgs)); + QCOMPARE(ConsoleLogger::instance().logSink()->logLevel(), defaultLogLevel()); + QVERIFY(!parser.force()); + + QVERIFY(parser.parseCommandLine(QStringList() << "-t" << fileArgs)); + QVERIFY(parser.logTime()); + + if (!Internal::HostOsInfo::isWindowsHost()) { // Windows has no progress bar atm. + // Note: We cannot just check for !parser.logTime() here, because if the test is not + // run in a terminal, "--show-progress" is ignored, in which case "--log-time" + // takes effect. + QVERIFY(parser.parseCommandLine(QStringList() << "-t" << "--show-progress" + << fileArgs)); + QVERIFY(parser.showProgress() != parser.logTime()); + } + + QVERIFY(parser.parseCommandLine(QStringList() << "-vvqqq" << fileArgs)); + QCOMPARE(ConsoleLogger::instance().logSink()->logLevel(), LoggerWarning); + + QVERIFY(parser.parseCommandLine(QStringList() << "-vvvqq" << fileArgs)); + QCOMPARE(ConsoleLogger::instance().logSink()->logLevel(), LoggerDebug); + + QVERIFY(parser.parseCommandLine(QStringList() << "--log-level" << "trace" << fileArgs)); + QCOMPARE(ConsoleLogger::instance().logSink()->logLevel(), LoggerTrace); + + // Second "global" profile overwrites first. + QVERIFY(parser.parseCommandLine(QStringList(fileArgs) << "profile:a" << "profile:b")); + QCOMPARE(parser.buildConfigurations().count(), 1); + QCOMPARE(parser.buildConfigurations().first().value("qbs.profile").toString(), QLatin1String("b")); + + // Second build configuration-specific profile overwrites first. + QVERIFY(parser.parseCommandLine(QStringList(fileArgs) << "debug" << "profile:a" + << "profile:b")); + QCOMPARE(parser.buildConfigurations().count(), 1); + QCOMPARE(parser.buildConfigurations().first().value("qbs.profile").toString(), QLatin1String("b")); + + QVERIFY(parser.parseCommandLine(QStringList(fileArgs) << "a-debug" << "profile:a" + << "b-debug" << "profile:b")); + QCOMPARE(parser.buildConfigurations().count(), 2); + QCOMPARE(parser.buildConfigurations().first().value("qbs.configurationName").toString(), QLatin1String("a-debug")); + QCOMPARE(parser.buildConfigurations().first().value("qbs.profile").toString(), QLatin1String("a")); + QCOMPARE(parser.buildConfigurations().at(1).value("qbs.configurationName").toString(), QLatin1String("b-debug")); + QCOMPARE(parser.buildConfigurations().at(1).value("qbs.profile").toString(), QLatin1String("b")); + + // Redundant build request + QVERIFY(parser.parseCommandLine(QStringList(fileArgs) << "debug" << "profile:a" + << "debug" << "profile:a")); + QCOMPARE(parser.buildConfigurations().count(), 1); + + QVERIFY(parser.parseCommandLine(QStringList(fileArgs) << "debug" << "profile:a" + << "release" << "profile:b")); + QCOMPARE(parser.buildConfigurations().count(), 2); + QCOMPARE(parser.buildConfigurations().first().value("qbs.configurationName").toString(), QLatin1String("debug")); + QCOMPARE(parser.buildConfigurations().first().value("qbs.profile").toString(), QLatin1String("a")); + QCOMPARE(parser.buildConfigurations().at(1).value("qbs.configurationName").toString(), QLatin1String("release")); + QCOMPARE(parser.buildConfigurations().at(1).value("qbs.profile").toString(), QLatin1String("b")); + + // Non-global property takes precedence. + QVERIFY(parser.parseCommandLine(QStringList(fileArgs) << "profile:a" << "debug" + << "profile:b")); + QCOMPARE(parser.buildConfigurations().count(), 1); + QCOMPARE(parser.buildConfigurations().first().value("qbs.configurationName").toString(), QLatin1String("debug")); + QCOMPARE(parser.buildConfigurations().first().value("qbs.profile").toString(), QLatin1String("b")); + } + + void testInvalidCommandLine() + { + QTemporaryFile projectFile; + QVERIFY(projectFile.open()); + const QStringList fileArgs = QStringList() << "-f" << projectFile.fileName(); + CommandLineParser parser; + QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "-x")); // Unknown short option. + QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "--xyz")); // Unknown long option. + QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "-vjv")); // Invalid position. + QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "-j")); // Missing argument. + QVERIFY(!parser.parseCommandLine(QStringList() << "-j" << "0" << fileArgs)); // Wrong argument. + QVERIFY(!parser.parseCommandLine(QStringList() << fileArgs << "--products")); // Missing argument. + QVERIFY(!parser.parseCommandLine(QStringList() << "--changed-files" << "," << fileArgs)); // Wrong argument. + QVERIFY(!parser.parseCommandLine(QStringList() << "--log-level" << "blubb" << fileArgs)); // Wrong argument. + } + + void testProjectFileLookup() + { + const QString srcDir = QLatin1String(SRCDIR); + const QString noProjectsDir = srcDir + QLatin1String("/data/dirwithnoprojects"); + const QString oneProjectDir = srcDir + QLatin1String("/data/dirwithoneproject"); + const QString multiProjectsDir = srcDir + QLatin1String("/data/dirwithmultipleprojects"); + QVERIFY(QDir(noProjectsDir).exists() && QDir(oneProjectDir).exists() + && QDir(multiProjectsDir).exists()); + CommandLineParser parser; + const QStringList args(QLatin1String("-f")); + QString projectFilePath = multiProjectsDir + QLatin1String("/project.qbs"); + QVERIFY(parser.parseCommandLine(args + QStringList(projectFilePath))); + QCOMPARE(projectFilePath, parser.projectFilePath()); + projectFilePath = oneProjectDir + QLatin1String("/project.qbs"); + QVERIFY(parser.parseCommandLine(args + QStringList(oneProjectDir))); + QCOMPARE(projectFilePath, parser.projectFilePath()); + QVERIFY(!parser.parseCommandLine(args + QStringList(noProjectsDir))); + QVERIFY(!parser.parseCommandLine(args + QStringList(multiProjectsDir))); + } +}; + +QTEST_MAIN(TestCmdLineParser) + +#include "tst_cmdlineparser.moc" diff --git a/tests/auto/language/language.pro b/tests/auto/language/language.pro new file mode 100644 index 00000000..f52798e7 --- /dev/null +++ b/tests/auto/language/language.pro @@ -0,0 +1,6 @@ +TARGET = tst_language + +SOURCES = tst_language.cpp + +include(../auto.pri) +include(../../../src/app/shared/logging/logging.pri) diff --git a/tests/auto/language/language.qbs b/tests/auto/language/language.qbs new file mode 100644 index 00000000..9fda49ce --- /dev/null +++ b/tests/auto/language/language.qbs @@ -0,0 +1,7 @@ +import qbs + +QbsAutotest { + testName: "language" + condition: qbsbuildconfig.enableUnitTests + files: "tst_language.cpp" +} diff --git a/tests/auto/language/tst_language.cpp b/tests/auto/language/tst_language.cpp new file mode 100644 index 00000000..b1d7336b --- /dev/null +++ b/tests/auto/language/tst_language.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + qbs::Internal::TestLanguage tl(ConsoleLogger::instance().logSink()); + return QTest::qExec(&tl, argc, argv); +} + diff --git a/tests/auto/shared.h b/tests/auto/shared.h new file mode 100644 index 00000000..5ce2a1de --- /dev/null +++ b/tests/auto/shared.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_TEST_SHARED_H +#define QBS_TEST_SHARED_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +inline QString profileName() { return QLatin1String("qbs_autotests"); } +inline QString relativeBuildDir(const QString &configurationName = QString()) +{ + return !configurationName.isEmpty() ? configurationName : QLatin1String("default"); +} + +inline QString relativeBuildGraphFilePath() { + return relativeBuildDir() + QLatin1Char('/') + relativeBuildDir() + QLatin1String(".bg"); +} + +inline bool regularFileExists(const QString &filePath) +{ + const QFileInfo fi(filePath); + return fi.exists() && fi.isFile(); +} + +inline bool directoryExists(const QString &dirPath) +{ + const QFileInfo fi(dirPath); + return fi.exists() && fi.isDir(); +} + +inline QString uniqueProductName(const QString &productName, const QString &_profileName) +{ + const QString p = _profileName.isEmpty() ? profileName() : _profileName; + return productName + '.' + p; +} + +inline QString relativeProductBuildDir(const QString &productName, + const QString &productProfileName = QString()) +{ + const QString fullName = uniqueProductName(productName, productProfileName); + QString dirName = qbs::Internal::HostOsInfo::rfc1034Identifier(fullName); + const QByteArray hash = QCryptographicHash::hash(fullName.toUtf8(), QCryptographicHash::Sha1); + dirName.append('.').append(hash.toHex().left(8)); + return relativeBuildDir() + '/' + dirName; +} + +inline QString relativeExecutableFilePath(const QString &productName) +{ + return relativeProductBuildDir(productName) + '/' + + qbs::Internal::HostOsInfo::appendExecutableSuffix(productName); +} + +inline void waitForNewTimestamp(const QString &testDir) +{ + // Waits for the time that corresponds to the host file system's time stamp granularity. + if (qbs::Internal::HostOsInfo::isWindowsHost()) { + QTest::qWait(1); // NTFS has 100 ns precision. Let's ignore exFAT. + } else { + const QString nameTemplate = testDir + "/XXXXXX"; + QTemporaryFile f1(nameTemplate); + if (!f1.open()) + qFatal("Failed to open temp file"); + const QDateTime initialTime = QFileInfo(f1).lastModified(); + while (true) { + QTest::qWait(50); + QTemporaryFile f2(nameTemplate); + if (!f2.open()) + qFatal("Failed to open temp file"); + if (QFileInfo(f2).lastModified() > initialTime) + break; + } + } +} + +inline void touch(const QString &fn) +{ + QFile f(fn); + int s = f.size(); + if (!f.open(QFile::ReadWrite)) + qFatal("cannot open file %s", qPrintable(fn)); + f.resize(s+1); + f.resize(s); +} + +inline void copyFileAndUpdateTimestamp(const QString &source, const QString &target) +{ + QFile::remove(target); + if (!QFile::copy(source, target)) + qFatal("Failed to copy '%s' to '%s'", qPrintable(source), qPrintable(target)); + touch(target); +} + +inline QString objectFileName(const QString &baseName, const QString &profileName) +{ + qbs::Settings settings((QString())); + qbs::Profile profile(profileName, &settings); + const QString suffix = profile.value("qbs.toolchain").toStringList().contains("msvc") + ? "obj" : "o"; + return baseName + '.' + suffix; +} + +inline QString inputDirHash(const QString &dir) +{ + return QCryptographicHash::hash(dir.toLatin1(), QCryptographicHash::Sha1).toHex().left(16); +} + +inline QString testWorkDir(const QString &testName) +{ + QString dir = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv("QBS_TEST_WORK_ROOT"))); + if (dir.isEmpty()) { + dir = QCoreApplication::applicationDirPath() + QStringLiteral("/../tests/auto/"); + } else { + if (!dir.endsWith(QLatin1Char('/'))) + dir += QLatin1Char('/'); + } + return dir + testName + "/testWorkDir"; +} + +inline qbs::Internal::HostOsInfo::HostOs targetOs() +{ + qbs::Settings settings((QString())); + const qbs::Profile buildProfile(profileName(), &settings); + const QStringList targetOS = buildProfile.value("qbs.targetOS").toStringList(); + if (targetOS.contains("windows")) + return qbs::Internal::HostOsInfo::HostOsWindows; + if (targetOS.contains("linux")) + return qbs::Internal::HostOsInfo::HostOsLinux; + if (targetOS.contains("macos")) + return qbs::Internal::HostOsInfo::HostOsMacos; + if (targetOS.contains("unix")) + return qbs::Internal::HostOsInfo::HostOsOtherUnix; + if (!targetOS.isEmpty()) + return qbs::Internal::HostOsInfo::HostOsOther; + return qbs::Internal::HostOsInfo::hostOs(); +} + +#endif // Include guard. diff --git a/tests/auto/tools/tools.pro b/tests/auto/tools/tools.pro new file mode 100644 index 00000000..0ee10216 --- /dev/null +++ b/tests/auto/tools/tools.pro @@ -0,0 +1,5 @@ +TARGET = tst_tools + +SOURCES = tst_tools.cpp ../../../src/app/qbs/qbstool.cpp + +include(../auto.pri) diff --git a/tests/auto/tools/tools.qbs b/tests/auto/tools/tools.qbs new file mode 100644 index 00000000..612a70a4 --- /dev/null +++ b/tests/auto/tools/tools.qbs @@ -0,0 +1,7 @@ +import qbs + +QbsAutotest { + testName: "tools" + condition: qbsbuildconfig.enableUnitTests + files: ["tst_tools.cpp"] +} diff --git a/tests/auto/tools/tst_tools.cpp b/tests/auto/tools/tst_tools.cpp new file mode 100644 index 00000000..39ff65f6 --- /dev/null +++ b/tests/auto/tools/tst_tools.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + qbs::Settings settings((QString())); + qbs::Internal::TestTools tt(&settings); + return QTest::qExec(&tt, argc, argv); +} diff --git a/tests/benchmarker/activities.h b/tests/benchmarker/activities.h new file mode 100644 index 00000000..643fc0fd --- /dev/null +++ b/tests/benchmarker/activities.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BENCHMARKER_ACTIVITY_H +#define QBS_BENCHMARKER_ACTIVITY_H + +#include + +namespace qbsBenchmarker { + +enum Activity { ActivityResolving = 1, ActivityRuleExecution = 2, ActivityNullBuild = 4 }; +Q_DECLARE_FLAGS(Activities, Activity) +Q_DECLARE_OPERATORS_FOR_FLAGS(Activities) + +} // namespace qbsBenchmarker + +#endif // Include guard. + diff --git a/tests/benchmarker/benchmarker-main.cpp b/tests/benchmarker/benchmarker-main.cpp new file mode 100644 index 00000000..00fa0903 --- /dev/null +++ b/tests/benchmarker/benchmarker-main.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "benchmarker.h" +#include "commandlineparser.h" +#include "exception.h" + +#include + +#include +#include + +using namespace qbsBenchmarker; + +static QByteArray relativeChange(qint64 oldVal, qint64 newVal) +{ + QByteArray change = newVal == 0 + ? "0" : QByteArray::number(std::abs(newVal * 100 / oldVal - 100)); + change += " %"; + if (oldVal > newVal) + change.prepend('-'); + else if (oldVal < newVal) + change.prepend('+'); + return change; +} + +static void printResults(Activity activity, const BenchmarkResults &results) +{ + std::cout << "========== Performance data for "; + switch (activity) { + case ActivityResolving: + std::cout << "Resolving"; + break; + case ActivityRuleExecution: + std::cout << "Rule Execution"; + break; + case ActivityNullBuild: + std::cout << "Null Build"; + break; + } + std::cout << " ==========" << std::endl; + const BenchmarkResult result = results.value(activity); + const char * const indent = " "; + std::cout << indent << "Old instruction count: " << result.oldInstructionCount << std::endl; + std::cout << indent << "New instruction count: " << result.newInstructionCount << std::endl; + std::cout << indent << "Relative change: " + << relativeChange(result.oldInstructionCount, result.newInstructionCount).constData() + << std::endl; + std::cout << indent << "Old peak memory usage: " << result.oldPeakMemoryUsage << " Bytes" + << std::endl; + std::cout << indent + << "New peak memory usage: " << result.newPeakMemoryUsage << " Bytes" << std::endl; + std::cout << indent << "Relative change: " + << relativeChange(result.oldPeakMemoryUsage, result.newPeakMemoryUsage).constData() + << std::endl; +} + +static void printResults(Activities activities, const BenchmarkResults &results) +{ + if (activities & ActivityResolving) + printResults(ActivityResolving, results); + if (activities & ActivityRuleExecution) + printResults(ActivityRuleExecution, results); + if (activities & ActivityNullBuild) + printResults(ActivityNullBuild, results); +} + +int main(int argc, char *argv[]) +{ + try { + QCoreApplication app(argc, argv); + CommandLineParser clParser; + clParser.parse(); + Benchmarker benchmarker(clParser.activies(), clParser.oldCommit(), clParser.newCommit(), + clParser.testProjectFilePath(), clParser.qbsRepoDirPath()); + benchmarker.benchmark(); + printResults(clParser.activies(), benchmarker.results()); + } catch (const Exception &e) { + std::cerr << qPrintable(e.description()) << std::endl; + return EXIT_FAILURE; + } +} diff --git a/tests/benchmarker/benchmarker.cpp b/tests/benchmarker/benchmarker.cpp new file mode 100644 index 00000000..69faa780 --- /dev/null +++ b/tests/benchmarker/benchmarker.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "benchmarker.h" + +#include "exception.h" +#include "runsupport.h" +#include "valgrindrunner.h" + +#include + +#include + +namespace qbsBenchmarker { + +Benchmarker::Benchmarker(Activities activities, const QString &oldCommit, const QString &newCommit, + const QString &testProject, const QString &qbsRepo) + : m_activities(activities) + , m_oldCommit(oldCommit) + , m_newCommit(newCommit) + , m_testProject(testProject) + , m_qbsRepo(qbsRepo) +{ +} + +Benchmarker::~Benchmarker() +{ + if (!m_commitToRestore.isEmpty()) { + try { + runProcess(QStringList() << "git" << "checkout" << m_commitToRestore, m_qbsRepo); + } catch (const Exception &e) { + qDebug("Failed to restore original commit %s: %s", qPrintable(m_commitToRestore), + qPrintable(e.description())); + } + } +} + +void Benchmarker::benchmark() +{ + rememberCurrentRepoState(); + runProcess(QStringList() << "git" << "checkout" << m_oldCommit, m_qbsRepo); + const QString oldQbsBuildDir = m_baseOutputDir.path() + "/old-qbs-build"; + std::cout << "Building from old repo state..." << std::endl; + buildQbs(oldQbsBuildDir); + runProcess(QStringList() << "git" << "checkout" << m_newCommit, m_qbsRepo); + const QString newQbsBuildDir = m_baseOutputDir.path() + "/new-qbs-build"; + std::cout << "Building from new repo state..." << std::endl; + buildQbs(newQbsBuildDir); + std::cout << "Now running valgrind. This can take a while." << std::endl; + + ValgrindRunner oldDataRetriever(m_activities, m_testProject, oldQbsBuildDir, + m_baseOutputDir.path() + "/old-stuff"); + ValgrindRunner newDataRetriever(m_activities, m_testProject, newQbsBuildDir, + m_baseOutputDir.path() + "/new-stuff"); + QFuture oldFuture = QtConcurrent::run(&oldDataRetriever, &ValgrindRunner::run); + QFuture newFuture = QtConcurrent::run(&newDataRetriever, &ValgrindRunner::run); + oldFuture.waitForFinished(); + foreach (const ValgrindResult &valgrindResult, oldDataRetriever.results()) { + BenchmarkResult &benchmarkResult = m_results[valgrindResult.activity]; + benchmarkResult.oldInstructionCount = valgrindResult.instructionCount; + benchmarkResult.oldPeakMemoryUsage = valgrindResult.peakMemoryUsage; + } + newFuture.waitForFinished(); + foreach (const ValgrindResult &valgrindResult, newDataRetriever.results()) { + BenchmarkResult &benchmarkResult = m_results[valgrindResult.activity]; + benchmarkResult.newInstructionCount = valgrindResult.instructionCount; + benchmarkResult.newPeakMemoryUsage = valgrindResult.peakMemoryUsage; + } + std::cout << "Done!" << std::endl; +} + +void Benchmarker::rememberCurrentRepoState() +{ + QByteArray commit; + runProcess(QStringList() << "git" << "describe" << "HEAD", m_qbsRepo, &commit); + m_commitToRestore = QString::fromLatin1(commit); +} + +void Benchmarker::buildQbs(const QString &buildDir) const +{ + if (!QDir::root().mkpath(buildDir)) + throw Exception(QString::fromLatin1("Failed to create directory '%1'.").arg(buildDir)); + runProcess(QStringList() << "qmake" << m_qbsRepo + "/qbs.pro", buildDir); + runProcess(QStringList() << "make" << "-s", buildDir); +} + +} // namespace qbsBenchmarker diff --git a/tests/benchmarker/benchmarker.h b/tests/benchmarker/benchmarker.h new file mode 100644 index 00000000..3d2c4f58 --- /dev/null +++ b/tests/benchmarker/benchmarker.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BENCHMARKER_BENCHMARKER_H +#define QBS_BENCHMARKER_BENCHMARKER_H + +#include "activities.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE + +namespace qbsBenchmarker { + +class BenchmarkResult +{ +public: + qint64 oldInstructionCount; + qint64 newInstructionCount; + qint64 oldPeakMemoryUsage; + qint64 newPeakMemoryUsage; +}; +typedef QHash BenchmarkResults; + +class Benchmarker +{ +public: + Benchmarker(Activities activities, const QString &oldCommit, const QString &newCommit, + const QString &testProject, const QString &qbsRepo); + ~Benchmarker(); + + void benchmark(); + + BenchmarkResults results() const { return m_results; } + +private: + void rememberCurrentRepoState(); + void buildQbs(const QString &buildDir) const; + + const Activities m_activities; + const QString m_oldCommit; + const QString m_newCommit; + const QString m_testProject; + const QString m_qbsRepo; + QString m_commitToRestore; + QTemporaryDir m_baseOutputDir; + BenchmarkResults m_results; +}; + +} // namespace qbsBenchmarker + +#endif // Include guard. diff --git a/tests/benchmarker/benchmarker.pro b/tests/benchmarker/benchmarker.pro new file mode 100644 index 00000000..4725e668 --- /dev/null +++ b/tests/benchmarker/benchmarker.pro @@ -0,0 +1,20 @@ +TARGET = qbs_benchmarker +DESTDIR = ../../bin +CONFIG += console +CONFIG -= app_bundle +CONFIG += c++11 +QT += concurrent +SOURCES = \ + benchmarker-main.cpp \ + benchmarker.cpp \ + commandlineparser.cpp \ + runsupport.cpp \ + valgrindrunner.cpp + +HEADERS = \ + activities.h \ + benchmarker.h \ + commandlineparser.h \ + exception.h \ + runsupport.h \ + valgrindrunner.h diff --git a/tests/benchmarker/benchmarker.qbs b/tests/benchmarker/benchmarker.qbs new file mode 100644 index 00000000..8dec56d6 --- /dev/null +++ b/tests/benchmarker/benchmarker.qbs @@ -0,0 +1,23 @@ +import qbs + +QtApplication { + name: "qbs_benchmarker" + destinationDirectory: "bin" + type: "application" + consoleApplication: true + cpp.cxxLanguageVersion: "c++11" + Depends { name: "Qt.concurrent" } + files: [ + "activities.h", + "benchmarker-main.cpp", + "benchmarker.cpp", + "benchmarker.h", + "commandlineparser.cpp", + "commandlineparser.h", + "exception.h", + "runsupport.cpp", + "runsupport.h", + "valgrindrunner.cpp", + "valgrindrunner.h", + ] +} diff --git a/tests/benchmarker/commandlineparser.cpp b/tests/benchmarker/commandlineparser.cpp new file mode 100644 index 00000000..49fe2c12 --- /dev/null +++ b/tests/benchmarker/commandlineparser.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandlineparser.h" + +#include "exception.h" + +#include +#include +#include +#include + +namespace qbsBenchmarker { + +static QString resolveActivity() { return "resolving"; } +static QString ruleExecutionActivity() { return "rule-execution"; } +static QString nullBuildActivity() { return "null-build"; } +static QString allActivities() { return "all"; } + +CommandLineParser::CommandLineParser() +{ +} + +void CommandLineParser::parse() +{ + QCommandLineParser parser; + parser.setApplicationDescription("This tool aims to detect qbs performance regressions " + "using valgrind."); + parser.addHelpOption(); + QCommandLineOption oldCommitOption("old-commit", "The old qbs commit.", "old commit"); + parser.addOption(oldCommitOption); + QCommandLineOption newCommitOption("new-commit", "The new qbs commit.", "new commit"); + parser.addOption(newCommitOption); + QCommandLineOption testProjectOption("test-project", + "The example project to use for the benchmark.", "project file path"); + parser.addOption(testProjectOption); + QCommandLineOption qbsRepoOption("qbs-repo", "The qbs repository.", "repo path"); + parser.addOption(qbsRepoOption); + QCommandLineOption activitiesOption("activities", + QString::fromLatin1("The activities to benchmark. Possible values (CSV): %1,%2,%3,%4") + .arg(resolveActivity(), ruleExecutionActivity(), nullBuildActivity(), + allActivities()), "activities", allActivities()); + parser.addOption(activitiesOption); + parser.process(*QCoreApplication::instance()); + QList mandatoryOptions = QList() + << oldCommitOption << newCommitOption << testProjectOption << qbsRepoOption; + foreach (const QCommandLineOption &o, mandatoryOptions) { + if (!parser.isSet(o)) + throwException(o.names().first(), parser.helpText()); + if (parser.value(o).isEmpty()) + throwException(o.names().first(), QString(), parser.helpText()); + } + m_oldCommit = parser.value(oldCommitOption); + m_newCommit = parser.value(newCommitOption); + m_testProjectFilePath = parser.value(testProjectOption); + m_qbsRepoDirPath = parser.value(qbsRepoOption); + const QStringList activitiesList = parser.value(activitiesOption).split(','); + m_activities = 0; + foreach (const QString &activityString, activitiesList) { + if (activityString == allActivities()) { + m_activities = ActivityResolving | ActivityRuleExecution | ActivityNullBuild; + break; + } else if (activityString == resolveActivity()) { + m_activities = ActivityResolving; + } else if (activityString == ruleExecutionActivity()) { + m_activities |= ActivityRuleExecution; + } else if (activityString == nullBuildActivity()) { + m_activities |= ActivityNullBuild; + } else { + throwException(activitiesOption.names().first(), activityString, parser.helpText()); + } + } +} + +void CommandLineParser::throwException(const QString &optionName, const QString &illegalValue, + const QString &helpText) +{ + const QString errorText(QString::fromLatin1("Error parsing command line: Illegal value '%1' " + "for option '--%2'.\n%3").arg(illegalValue, optionName, helpText)); + throw Exception(errorText); +} + +void CommandLineParser::throwException(const QString &missingOption, const QString &helpText) +{ + const QString errorText(QString::fromLatin1("Error parsing command line: Missing mandatory " + "option '--%1'.\n%3").arg(missingOption, helpText)); + throw Exception(errorText); +} + + +} // namespace qbsBenchmarker diff --git a/tests/benchmarker/commandlineparser.h b/tests/benchmarker/commandlineparser.h new file mode 100644 index 00000000..4b83f2dd --- /dev/null +++ b/tests/benchmarker/commandlineparser.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BENCHMARKER_COMMANDLINEPARSER_H +#define QBS_BENCHMARKER_COMMANDLINEPARSER_H + +#include "activities.h" + +#include + +namespace qbsBenchmarker { + +class CommandLineParser +{ +public: + CommandLineParser(); + + void parse(); + + Activities activies() const { return m_activities; } + QString oldCommit() const { return m_oldCommit; } + QString newCommit() const { return m_newCommit; } + QString testProjectFilePath() const { return m_testProjectFilePath; } + QString qbsRepoDirPath() const { return m_qbsRepoDirPath; } + +private: + Q_NORETURN void throwException(const QString &optionName, const QString &illegalValue, + const QString &helpText); + Q_NORETURN void throwException(const QString &missingOption, const QString &helpText); + + Activities m_activities; + QString m_oldCommit; + QString m_newCommit; + QString m_testProjectFilePath; + QString m_qbsRepoDirPath; +}; + +} // namespace qbsBenchmarker + +#endif // Include guard. diff --git a/tests/benchmarker/exception.h b/tests/benchmarker/exception.h new file mode 100644 index 00000000..36163ad1 --- /dev/null +++ b/tests/benchmarker/exception.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BENCHMARKER_EXCEPTION_H +#define QBS_BENCHMARKER_EXCEPTION_H + +#include +#include + +namespace qbsBenchmarker { + +class Exception : public QException { +public: + explicit Exception(const QString &description) : m_description(description) {} + ~Exception() throw() { } + + QString description() const { return m_description; } + +private: + void raise() const { throw *this; } + Exception *clone() const { return new Exception(*this); } + + QString m_description; +}; + +} // namespace qbsBenchmarker + +#endif // Include guard. + diff --git a/tests/benchmarker/runsupport.cpp b/tests/benchmarker/runsupport.cpp new file mode 100644 index 00000000..360cc871 --- /dev/null +++ b/tests/benchmarker/runsupport.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "runsupport.h" + +#include "exception.h" + +#include +#include +#include +#include + +namespace qbsBenchmarker { + +void runProcess(const QStringList &commandLine, const QString &workingDir, QByteArray *output) +{ + QStringList args = commandLine; + const QString command = args.takeFirst(); + QProcess p; + if (!workingDir.isEmpty()) + p.setWorkingDirectory(workingDir); + p.start(command, args); + if (!p.waitForStarted()) + throw Exception(QString::fromLatin1("Process '%1' failed to start.").arg(command)); + p.waitForFinished(-1); + if (p.exitStatus() != QProcess::NormalExit) { + throw Exception(QString::fromLatin1("Error running '%1': %2") + .arg(command, p.errorString())); + } + if (p.exitCode() != 0) { + QString errorString = QString::fromLatin1("Command '%1' finished with exit code %2.") + .arg(command).arg(p.exitCode()); + const QByteArray stdErr = p.readAllStandardError(); + if (!stdErr.isEmpty()) { + errorString += QString::fromLatin1("\nStandard error output was: '%1'") + .arg(QString::fromLocal8Bit(stdErr)); + } + throw Exception(errorString); + } + if (output) + *output = p.readAllStandardOutput().trimmed(); +} + +} // namespace qbsBenchmarker diff --git a/tests/benchmarker/runsupport.h b/tests/benchmarker/runsupport.h new file mode 100644 index 00000000..4bfd4289 --- /dev/null +++ b/tests/benchmarker/runsupport.h @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BENCHMARKER_RUNSUPPORT_H +#define QBS_BENCHMARKER_RUNSUPPORT_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QByteArray; +class QStringList; +QT_END_NAMESPACE + +namespace qbsBenchmarker { + +void runProcess(const QStringList &commandLine, const QString& workingDir = QString(), + QByteArray *output = 0); + +} // namespace qbsBenchmarker + +#endif // Include guard. + diff --git a/tests/benchmarker/valgrindrunner.cpp b/tests/benchmarker/valgrindrunner.cpp new file mode 100644 index 00000000..c9ab9766 --- /dev/null +++ b/tests/benchmarker/valgrindrunner.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "valgrindrunner.h" + +#include "exception.h" +#include "runsupport.h" + +#include +#include +#include +#include +#include +#include + +namespace qbsBenchmarker { + +ValgrindRunner::ValgrindRunner(Activities activities, const QString &testProject, + const QString &qbsBuildDir, const QString &baseOutputDir) + : m_activities(activities) + , m_testProject(testProject) + , m_qbsBinary(qbsBuildDir + "/bin/qbs") + , m_baseOutputDir(baseOutputDir) +{ + if (!QDir::root().mkpath(m_baseOutputDir)) + throw Exception(QString::fromLatin1("Failed to create directory '%1'.").arg(baseOutputDir)); +} + +void ValgrindRunner::run() +{ + QList> futures; + if (m_activities & ActivityResolving) + futures << QtConcurrent::run(this, &ValgrindRunner::traceResolving); + if (m_activities & ActivityRuleExecution) + futures << QtConcurrent::run(this, &ValgrindRunner::traceRuleExecution); + if (m_activities & ActivityNullBuild) + futures << QtConcurrent::run(this, &ValgrindRunner::traceNullBuild); + while (!futures.isEmpty()) + futures.takeFirst().waitForFinished(); +} + +void ValgrindRunner::traceResolving() +{ + const QString buildDirCallgrind = m_baseOutputDir + "/build-dir.resolving.callgrind"; + const QString buildDirMassif = m_baseOutputDir + "/build-dir.resolving.massif"; + traceActivity(ActivityResolving, buildDirCallgrind, buildDirMassif); +} + +void ValgrindRunner::traceRuleExecution() +{ + const QString buildDirCallgrind = m_baseOutputDir + "/build-dir.rule-execution.callgrind"; + const QString buildDirMassif = m_baseOutputDir + "/build-dir.rule-execution.massif"; + runProcess(qbsCommandLine("resolve", buildDirCallgrind, false)); + runProcess(qbsCommandLine("resolve", buildDirMassif, false)); + traceActivity(ActivityRuleExecution, buildDirCallgrind, buildDirMassif); +} + +void ValgrindRunner::traceNullBuild() +{ + const QString buildDirCallgrind = m_baseOutputDir + "/build-dir.null-build.callgrind"; + const QString buildDirMassif = m_baseOutputDir + "/build-dir.null-build.massif"; + runProcess(qbsCommandLine("build", buildDirCallgrind, false)); + runProcess(qbsCommandLine("build", buildDirMassif, false)); + traceActivity(ActivityNullBuild, buildDirCallgrind, buildDirMassif); +} + +void ValgrindRunner::traceActivity(Activity activity, const QString &buildDirCallgrind, + const QString &buildDirMassif) +{ + QString activityString; + QString qbsCommand; + bool dryRun; + switch (activity) { + case ActivityResolving: + activityString = "resolving"; + qbsCommand = "resolve"; + dryRun = false; + break; + case ActivityRuleExecution: + activityString = "rule-execution"; + qbsCommand = "build"; + dryRun = true; + break; + case ActivityNullBuild: + activityString = "null-build"; + qbsCommand = "build"; + dryRun = false; + break; + } + + const QString outFileCallgrind = m_baseOutputDir + "/outfile." + activityString + ".callgrind"; + const QString outFileMassif = m_baseOutputDir + "/outfile." + activityString + ".massif"; + QFuture callGrindFuture = QtConcurrent::run(this, &ValgrindRunner::runCallgrind, + qbsCommand, buildDirCallgrind, dryRun, outFileCallgrind); + QFuture massifFuture = QtConcurrent::run(this, &ValgrindRunner::runMassif, qbsCommand, + buildDirMassif, dryRun, outFileMassif); + callGrindFuture.waitForFinished(); + massifFuture.waitForFinished(); + addToResults(ValgrindResult(activity, callGrindFuture.result(), massifFuture.result())); +} + +QStringList ValgrindRunner::qbsCommandLine(const QString &command, const QString &buildDir, + bool dryRun) const +{ + QStringList commandLine = QStringList() << m_qbsBinary << command << "-qq" << "-d" << buildDir + << "-f" << m_testProject; + if (dryRun) + commandLine << "--dry-run"; + return commandLine; +} + +QStringList ValgrindRunner::wrapForValgrind(const QStringList &commandLine, const QString &tool, + const QString &outFile) const +{ + return QStringList() << "valgrind" << "--smc-check=all" << "--trace-children=yes" + << ("--tool=" + tool) << ("--" + tool + "-out-file=" + outFile) + << commandLine; +} + +QStringList ValgrindRunner::valgrindCommandLine(const QString &qbsCommand, const QString &buildDir, + bool dryRun, const QString &tool, const QString &outFile) const +{ + return wrapForValgrind(qbsCommandLine(qbsCommand, buildDir, dryRun), tool, outFile); +} + +void ValgrindRunner::addToResults(const ValgrindResult &result) +{ + QMutexLocker locker(&m_resultsMutex); + m_results << result; +} + +qint64 ValgrindRunner::runCallgrind(const QString &qbsCommand, const QString &buildDir, + bool dryRun, const QString &outFile) +{ + runProcess(valgrindCommandLine(qbsCommand, buildDir, dryRun, "callgrind", outFile)); + QFile f(outFile); + if (!f.open(QIODevice::ReadOnly)) { + throw Exception(QString::fromLatin1("Failed to open file '%1': %2") + .arg(outFile, f.errorString())); + } + while (!f.atEnd()) { + const QByteArray line = f.readLine().trimmed(); + static const QByteArray magicString = "summary: "; + if (!line.startsWith(magicString)) + continue; + const QByteArray icString = line.mid(magicString.count()); + bool ok; + const qint64 iCount = icString.toLongLong(&ok); + if (!ok) { + throw Exception(QString::fromLatin1("Unexpected line in callgrind output file " + "'%1': '%2'.") + .arg(outFile, QString::fromLocal8Bit(line))); + } + return iCount; + } + + throw Exception(QString::fromLatin1("Failed to find summary line in callgrind " + "output file '%1'.").arg(outFile)); +} + +qint64 ValgrindRunner::runMassif(const QString &qbsCommand, const QString &buildDir, bool dryRun, + const QString &outFile) +{ + runProcess(valgrindCommandLine(qbsCommand, buildDir, dryRun, "massif", outFile)); + QByteArray ms_printOutput; + runProcess(QStringList() << "ms_print" << outFile, QString(), &ms_printOutput); + QBuffer buffer(&ms_printOutput); + buffer.open(QIODevice::ReadOnly); + QByteArray peakSnapshot; + const QString exceptionStringPattern = QString::fromLatin1("Failed to extract peak memory " + "usage from file '%1': %2").arg(outFile); + while (!buffer.atEnd()) { + const QByteArray line = buffer.readLine(); + static const QByteArray magicString = " (peak)"; + const int magicStringOffset = line.indexOf(magicString); + if (magicStringOffset == -1) + continue; + int delimiterOffset = line.lastIndexOf(',', magicStringOffset); + if (delimiterOffset == -1) + delimiterOffset = line.lastIndexOf('[', magicStringOffset); + if (delimiterOffset == -1) { + const QString details = QString::fromLatin1("Failed to extract peak snapshot from " + "line '%1'.").arg(QString::fromLocal8Bit(line)); + throw Exception(exceptionStringPattern.arg(details)); + } + peakSnapshot = line.mid(delimiterOffset + 1, magicStringOffset - delimiterOffset).trimmed(); + break; + } + if (peakSnapshot.isEmpty()) + throw Exception(exceptionStringPattern.arg("No peak marker found")); + while (!buffer.atEnd()) { + const QByteArray line = buffer.readLine().simplified(); + if (!line.startsWith(peakSnapshot + ' ')) + continue; + const QList entries = line.split(' '); + if (entries.count() != 6) { + const QString details = QString::fromLatin1("Expected 6 entries in line '%1', but " + "there are %2.").arg(QString::fromLocal8Bit(line)).arg(entries.count()); + throw Exception(exceptionStringPattern.arg(details)); + } + QByteArray peakMemoryString = entries.at(2); + peakMemoryString.replace(',', QByteArray()); + bool ok; + qint64 peakMemoryUsage = peakMemoryString.toLongLong(&ok); + if (!ok) { + const QString details = QString::fromLatin1("Failed to parse peak memory value '%1' " + "as a number.").arg(QString::fromLocal8Bit(peakMemoryString)); + throw Exception(exceptionStringPattern.arg(details)); + } + return peakMemoryUsage; + } + + const QString details = QString::fromLatin1("Failed to find snapshot '%1'.") + .arg(QString::fromLocal8Bit(peakSnapshot)); + throw Exception(exceptionStringPattern.arg(details)); +} + +} // namespace qbsBenchmarker diff --git a/tests/benchmarker/valgrindrunner.h b/tests/benchmarker/valgrindrunner.h new file mode 100644 index 00000000..0cad277d --- /dev/null +++ b/tests/benchmarker/valgrindrunner.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_BENCHMARKER_BENCHMARKRUNNER_H +#define QBS_BENCHMARKER_BENCHMARKRUNNER_H + +#include "activities.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE + +namespace qbsBenchmarker { + +class ValgrindResult +{ +public: + ValgrindResult(Activity a, qint64 ic, qint64 mem) + : activity(a), instructionCount(ic), peakMemoryUsage(mem) {} + + Activity activity; + qint64 instructionCount; + qint64 peakMemoryUsage; +}; + +class ValgrindRunner +{ +public: + ValgrindRunner(Activities activities, const QString &testProject, const QString &qbsBuildDir, + const QString &baseOutputDir); + + void run(); + QList results() const { return m_results; } + +private: + void traceResolving(); + void traceRuleExecution(); + void traceNullBuild(); + void traceActivity(Activity activity, const QString &buildDirCallgrind, + const QString &buildDirMassif); + QStringList qbsCommandLine(const QString &command, const QString &buildDir, bool dryRun) const; + QStringList wrapForValgrind(const QStringList &commandLine, const QString &tool, + const QString &outFile) const; + QStringList valgrindCommandLine(const QString &qbsCommand, const QString &buildDir, bool dryRun, + const QString &tool, const QString &outFile) const; + void addToResults(const ValgrindResult &results); + qint64 runCallgrind(const QString &qbsCommand, const QString &buildDir, bool dryRun, + const QString &outFile); + qint64 runMassif(const QString &qbsCommand, const QString &buildDir, bool dryRun, + const QString &outFile); + + const Activities m_activities; + const QString m_testProject; + const QString m_qbsBinary; + const QString m_baseOutputDir; + QList m_results; + QMutex m_resultsMutex; +}; + +} // namespace qbsBenchmarker + +#endif // Include guard. diff --git a/tests/fuzzy-test/commandlineparser.cpp b/tests/fuzzy-test/commandlineparser.cpp new file mode 100644 index 00000000..2378df36 --- /dev/null +++ b/tests/fuzzy-test/commandlineparser.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "commandlineparser.h" + +#include + +#include + +static QString profileOption() { return "--profile"; } +static QString startCommitOption() { return "--start-commit"; } +static QString maxDurationoption() { return "--max-duration"; } +static QString jobCountOption() { return "--jobs"; } +static QString logOption() { return "--log"; } + +CommandLineParser::CommandLineParser() +{ +} + +void CommandLineParser::parse(const QStringList &commandLine) +{ + m_profile.clear(); + m_startCommit.clear(); + m_maxDuration = 0; + m_jobCount = 0; + m_log = false; + m_commandLine = commandLine; + Q_ASSERT(!m_commandLine.isEmpty()); + m_command = m_commandLine.takeFirst(); + while (!m_commandLine.isEmpty()) { + const QString arg = m_commandLine.takeFirst(); + if (arg == profileOption()) + assignOptionArgument(arg, m_profile); + else if (arg == startCommitOption()) + assignOptionArgument(arg, m_startCommit); + else if (arg == jobCountOption()) + assignOptionArgument(arg, m_jobCount); + else if (arg == maxDurationoption()) + parseDuration(); + else if (arg == logOption()) + m_log = true; + else + throw ParseException(QString::fromLatin1("Unknown parameter '%1'").arg(arg)); + } + if (m_profile.isEmpty()) + throw ParseException("No profile given."); + if (m_startCommit.isEmpty()) + throw ParseException("No start commit given."); +} + +QString CommandLineParser::usageString() const +{ + return QString::fromLatin1("%1 %2 %3 [%4 ] " + "[%5 ] [%6]") + .arg(QFileInfo(m_command).fileName(), profileOption(), startCommitOption(), + maxDurationoption(), jobCountOption(), logOption()); +} + +void CommandLineParser::assignOptionArgument(const QString &option, QString &argument) +{ + if (m_commandLine.isEmpty()) + throw ParseException(QString::fromLatin1("Option '%1' needs an argument.").arg(option)); + argument = m_commandLine.takeFirst(); + if (argument.isEmpty()) { + throw ParseException(QString::fromLatin1("Argument for option '%1' must not be empty.") + .arg(option)); + } +} + +void CommandLineParser::assignOptionArgument(const QString &option, int &argument) +{ + QString numberString; + assignOptionArgument(option, numberString); + bool ok; + argument = numberString.toInt(&ok); + if (!ok || argument <= 0) { + throw ParseException(QString::fromLatin1("Invalid argument '%1' for option '%2'.") + .arg(numberString, option)); + } +} + +void CommandLineParser::parseDuration() +{ + QString durationString; + QString choppedDurationString; + assignOptionArgument(maxDurationoption(), durationString); + choppedDurationString = durationString; + const char suffix = durationString.at(durationString.count() - 1).toLatin1(); + const bool hasSuffix = !std::isdigit(suffix); + if (hasSuffix) + choppedDurationString.chop(1); + bool ok; + m_maxDuration = choppedDurationString.toInt(&ok); + if (!ok || m_maxDuration <= 0) { + throw ParseException(QString::fromLatin1("Invalid duration argument '%1'.") + .arg(durationString)); + } + if (hasSuffix) { + switch (suffix) { + case 'm': break; + case 'd': m_maxDuration *= 24; // Fall-through. + case 'h': m_maxDuration *= 60; break; + default: + throw ParseException(QString::fromLatin1("Invalid duration suffix '%1'.") + .arg(suffix)); + } + } +} diff --git a/tests/fuzzy-test/commandlineparser.h b/tests/fuzzy-test/commandlineparser.h new file mode 100644 index 00000000..a6e53643 --- /dev/null +++ b/tests/fuzzy-test/commandlineparser.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_FUZZYTEST_COMMANDLINEPARSER_H +#define QBS_FUZZYTEST_COMMANDLINEPARSER_H + +#include + +#include + +class ParseException : public std::exception +{ +public: + ParseException(const QString &error) : errorMessage(error) { } + ~ParseException() throw() {} + + QString errorMessage; + +private: + const char *what() const throw() { return qPrintable(errorMessage); } +}; + +class CommandLineParser +{ +public: + CommandLineParser(); + + void parse(const QStringList &commandLine); + + QString profile() const { return m_profile; } + QString startCommit() const { return m_startCommit; } + int maxDurationInMinutes() const { return m_maxDuration; } + int jobCount() const { return m_jobCount; } + bool log() const { return m_log; } + + QString usageString() const; + +private: + void assignOptionArgument(const QString &option, QString &argument); + void assignOptionArgument(const QString &option, int &argument); + void parseDuration(); + + QStringList m_commandLine; + QString m_command; + QString m_profile; + QString m_startCommit; + int m_maxDuration; + int m_jobCount; + bool m_log; +}; + +#endif // Include guard. diff --git a/tests/fuzzy-test/fuzzy-test.pro b/tests/fuzzy-test/fuzzy-test.pro new file mode 100644 index 00000000..97083ef7 --- /dev/null +++ b/tests/fuzzy-test/fuzzy-test.pro @@ -0,0 +1,11 @@ +TARGET = qbs_fuzzy-test +DESTDIR = ../../bin +CONFIG += console +CONFIG -= app_bundle +SOURCES = main.cpp \ + commandlineparser.cpp \ + fuzzytester.cpp + +HEADERS += \ + commandlineparser.h \ + fuzzytester.h diff --git a/tests/fuzzy-test/fuzzy-test.qbs b/tests/fuzzy-test/fuzzy-test.qbs new file mode 100644 index 00000000..d12f0ba8 --- /dev/null +++ b/tests/fuzzy-test/fuzzy-test.qbs @@ -0,0 +1,16 @@ +import qbs + +QtApplication { + name: "qbs_fuzzy-test" + destinationDirectory: "bin" + type: "application" + consoleApplication: true + cpp.cxxLanguageVersion: "c++11" + files: [ + "commandlineparser.cpp", + "commandlineparser.h", + "fuzzytester.cpp", + "fuzzytester.h", + "main.cpp", + ] +} diff --git a/tests/fuzzy-test/fuzzytester.cpp b/tests/fuzzy-test/fuzzytester.cpp new file mode 100644 index 00000000..55a7c91a --- /dev/null +++ b/tests/fuzzy-test/fuzzytester.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "fuzzytester.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +static QString resolveIncrementalActivity() { return "resolve-incremental"; } +static QString buildIncrementalActivity() { return "build-incremental"; } +static QString buildFromScratchActivity() { return "build-from-scratch"; } + +FuzzyTester::FuzzyTester() +{ + loadSettings(); +} + +FuzzyTester::~FuzzyTester() +{ + storeSettings(); +} + +void FuzzyTester::runTest(const QString &profile, const QString &startCommit, + int maxDurationInMinutes, int jobCount, bool log) +{ + m_profile = profile; + m_jobCount = jobCount; + m_log = log; + + runGit(QStringList() << "rev-parse" << "HEAD", &m_headCommit); + qDebug("HEAD is %s", qPrintable(m_headCommit)); + + qDebug("Trying to find a buildable commit to start with..."); + const QString workingStartCommit = findWorkingStartCommit(startCommit); + qDebug("Found buildable start commit %s.", qPrintable(workingStartCommit)); + QStringList allCommits = findAllCommits(workingStartCommit); + qDebug("The test set comprises all %d commits between the start commit and HEAD.", + allCommits.count()); + + // Shuffle the initial sequence. Otherwise all invocations of the tool with the same start + // commit would try the same sequence of commits. + std::srand(std::time(0)); + std::random_shuffle(allCommits.begin(), allCommits.end()); + + quint64 run = 0; + QStringList buildSequence(workingStartCommit); + QElapsedTimer timer; + const qint64 maxDurationInMillis = maxDurationInMinutes * 60 * 1000; + if (maxDurationInMillis != 0) + timer.start(); + + bool timerHasExpired = false; + while (std::next_permutation(allCommits.begin(), allCommits.end()) && !timerHasExpired) { + qDebug("Testing permutation %llu...", ++run); + foreach (const QString ¤tCommit, allCommits) { + if (timer.isValid() && timer.hasExpired(maxDurationInMillis)) { + timerHasExpired = true; + break; + } + + m_currentCommit = currentCommit; + buildSequence << currentCommit; + checkoutCommit(currentCommit); + qDebug("Testing incremental build #%d (%s)", buildSequence.count() - 1, + qPrintable(currentCommit)); + + // Doing "resolve" and "build" separately introduces additional possibilities + // for errors, as information from change tracking has to be serialized correctly. + QString qbsError; + m_currentActivity = resolveIncrementalActivity(); + bool success = runQbs(defaultBuildDir(), QLatin1String("resolve"), &qbsError); + if (success) { + m_currentActivity = buildIncrementalActivity(); + success = runQbs(defaultBuildDir(), QLatin1String("build"), &qbsError); + } + m_currentActivity = buildFromScratchActivity(); + if (success) { + if (!doCleanBuild(&qbsError)) { + QString message = "An incremental build succeeded " + "with a commit for which a clean build failed."; + if (!m_log) { + message += QString::fromLatin1("\nThe qbs error message " + "for the clean build was: '%1'").arg(qbsError); + } + throwIncrementalBuildError(message, buildSequence); + } + } else { + qDebug("Incremental build failed. Checking whether clean build works..."); + if (doCleanBuild()) { + QString message = "An incremental build failed " + "with a commit for which a clean build succeeded."; + if (!m_log) { + message += QString::fromLatin1("\nThe qbs error message for " + "the incremental build was: '%1'").arg(qbsError); + } + throwIncrementalBuildError(message, buildSequence); + } else { + qDebug("Clean build also fails. Continuing."); + } + } + } + } + + if (timerHasExpired) + qDebug("Maximum duration reached."); + else + qDebug("All possible permutations were tried."); +} + +void FuzzyTester::checkoutCommit(const QString &commit) +{ + runGit(QStringList() << "checkout" << commit); + runGit(QStringList() << "submodule" << "update" << "--init"); +} + +QStringList FuzzyTester::findAllCommits(const QString &startCommit) +{ + QString allCommitsString; + runGit(QStringList() << "log" << (startCommit + "~1.." + m_headCommit) << "--format=format:%h", + &allCommitsString); + return allCommitsString.simplified().split(QLatin1Char(' '), QString::SkipEmptyParts); +} + +QString FuzzyTester::findWorkingStartCommit(const QString &startCommit) +{ + const QStringList allCommits = findAllCommits(startCommit); + QString qbsError; + m_currentActivity = buildFromScratchActivity(); + for (int i = allCommits.count() - 1; i >= 0; --i) { + m_currentCommit = allCommits.at(i); + if (m_unbuildableCommits.contains(m_currentCommit)) { + qDebug("Skipping known bad commit %s.", qPrintable(m_currentCommit)); + continue; + } + checkoutCommit(m_currentCommit); + removeDir(defaultBuildDir()); + if (runQbs(defaultBuildDir(), QLatin1String("build"), &qbsError)) { + m_buildableCommits << m_currentCommit; + return m_currentCommit; + } + qDebug("Commit %s is not buildable.", qPrintable(m_currentCommit)); + m_unbuildableCommits << m_currentCommit; + } + throw TestError(QString::fromLatin1("Cannot run test: Failed to find a single commit that " + "builds successfully with qbs. The last qbs error was: '%1'").arg(qbsError)); +} + +void FuzzyTester::runGit(const QStringList &arguments, QString *output) +{ + QProcess git; + git.start("git", arguments); + if (!git.waitForStarted()) + throw TestError("Failed to start git. It is expected to be in the PATH."); + if (!git.waitForFinished(300000) || git.exitStatus() != QProcess::NormalExit) // 5 minutes ought to be enough for everyone + throw TestError(QString::fromLatin1("git failed: %1").arg(git.errorString())); + if (git.exitCode() != 0) { + throw TestError(QString::fromLatin1("git failed: %1") + .arg(QString::fromLocal8Bit(git.readAllStandardError()))); + } + if (output) + *output = QString::fromLocal8Bit(git.readAllStandardOutput()).trimmed(); +} + +bool FuzzyTester::runQbs(const QString &buildDir, const QString &command, QString *errorOutput) +{ + if (errorOutput) + errorOutput->clear(); + QProcess qbs; + QStringList commandLine = QStringList(command) << "-d" << buildDir; + if (m_log) { + commandLine << "-vv"; + const int maxLoggedCommits = 2; + Q_ASSERT(m_commitsWithLogFiles.count() <= maxLoggedCommits + 1); + if (m_commitsWithLogFiles.count() == maxLoggedCommits + 1) { + static const QStringList allActivities = QStringList() << resolveIncrementalActivity() + << buildIncrementalActivity() << buildFromScratchActivity(); + const QString oldCommit = m_commitsWithLogFiles.dequeue(); + foreach (const QString &a, allActivities) + QFile::remove(logFilePath(oldCommit, a)); + } + qbs.setStandardErrorFile(logFilePath(m_currentCommit, m_currentActivity)); + if (m_commitsWithLogFiles.isEmpty() || m_commitsWithLogFiles.last() != m_currentCommit) + m_commitsWithLogFiles.enqueue(m_currentCommit); + } else { + commandLine << "-qq"; + } + if (m_jobCount != 0) + commandLine << "--jobs" << QString::number(m_jobCount); + commandLine << ("profile:" + m_profile); + qbs.start("qbs", commandLine); + if (!qbs.waitForStarted()) { + throw TestError(QString::fromLatin1("Failed to start qbs. It is expected to be " + "in the PATH. QProcess error string: '%1'").arg(qbs.errorString())); + } + if (!qbs.waitForFinished(-1) || qbs.exitCode() != 0) { + if (errorOutput) + *errorOutput = QString::fromLocal8Bit(qbs.readAllStandardError()); + return false; + } + return true; +} + +void FuzzyTester::removeDir(const QString &dirPath) +{ + QDir dir(dirPath); + if (!dir.removeRecursively()) { + throw TestError(QString::fromLatin1("Failed to remove temporary dir '%1'.") + .arg(dir.absolutePath())); + } +} + +bool FuzzyTester::doCleanBuild(QString *errorMessage) +{ + if (m_unbuildableCommits.contains(m_currentCommit)) { + qDebug("Commit is known not to be buildable, not running qbs."); + return false; + } + if (m_buildableCommits.contains(m_currentCommit)) { + qDebug("Commit is known to be buildable, not running qbs."); + return true; + } + const QString cleanBuildDir = "fuzzytest-verification-build"; + removeDir(cleanBuildDir); + if (runQbs(cleanBuildDir, QLatin1String("build"), errorMessage)) { + m_buildableCommits << m_currentCommit; + return true; + } + m_unbuildableCommits << m_currentCommit; + return false; +} + +void FuzzyTester::throwIncrementalBuildError(const QString &message, + const QStringList &commitSequence) +{ + const QString commitSequenceString = commitSequence.join(QLatin1Char(',')); + throw TestError(QString::fromLatin1("Found qbs bug with incremental build!\n" + "%1\n" + "The sequence of commits was: %2.").arg(message, commitSequenceString)); +} + +QString FuzzyTester::logFilePath(const QString &commit, const QString &activity) +{ + return "log." + commit + '.' + activity; +} + +QString FuzzyTester::defaultBuildDir() +{ + return "fuzzytest-build"; +} + + +static QString organization() { return "QtProject"; } +static QString app() { return "qbs-fuzzy-tester"; } +static QString unbuildableCommitsKey() { return "unbuildable-commits"; } +static QString buildableCommitsKey() { return "buildable-commits"; } + +void FuzzyTester::loadSettings() +{ + QSettings s(organization(), app()); + m_unbuildableCommits = s.value(unbuildableCommitsKey()).toStringList(); + m_buildableCommits = s.value(buildableCommitsKey()).toStringList(); +} + +void FuzzyTester::storeSettings() const +{ + QSettings s(organization(), app()); + s.setValue(unbuildableCommitsKey(), m_unbuildableCommits); + s.setValue(buildableCommitsKey(), m_buildableCommits); +} diff --git a/tests/fuzzy-test/fuzzytester.h b/tests/fuzzy-test/fuzzytester.h new file mode 100644 index 00000000..83085d7f --- /dev/null +++ b/tests/fuzzy-test/fuzzytester.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QBS_FUZZYTESTER_H +#define QBS_FUZZYTESTER_H + +#include +#include + +#include + +class TestError { +public: + TestError(const QString &errorMessage) : errorMessage(errorMessage) {} + ~TestError() throw() {} + + QString errorMessage; + +private: + const char *what() const throw() { return qPrintable(errorMessage); } +}; + + +class FuzzyTester +{ +public: + FuzzyTester(); + ~FuzzyTester(); + + void runTest(const QString &profile, const QString &startCommit, int maxDurationInMinutes, + int jobCount, bool log); + +private: + void checkoutCommit(const QString &commit); + QStringList findAllCommits(const QString &startCommit); + QString findWorkingStartCommit(const QString &startCommit); + void runGit(const QStringList &arguments, QString *output = 0); + bool runQbs(const QString &buildDir, const QString &command, QString *errorOutput = 0); + void removeDir(const QString &dir); + bool doCleanBuild(QString *errorMessage = 0); + void throwIncrementalBuildError(const QString &message, const QStringList &commitSequence); + + void loadSettings(); + void storeSettings() const; + + static QString logFilePath(const QString &commit, const QString &activity); + static QString defaultBuildDir(); + + QString m_profile; + int m_jobCount; + bool m_log; + QString m_headCommit; + QString m_currentCommit; + QString m_currentActivity; + QQueue m_commitsWithLogFiles; + QStringList m_unbuildableCommits; + QStringList m_buildableCommits; +}; + +#endif // Include guard. diff --git a/tests/fuzzy-test/main.cpp b/tests/fuzzy-test/main.cpp new file mode 100644 index 00000000..97107182 --- /dev/null +++ b/tests/fuzzy-test/main.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "commandlineparser.h" +#include "fuzzytester.h" + +#include + +#include +#include + +static bool parseCommandLine(const QStringList &commandLine, QString &profile, + QString &startCommi, int &maxDuration, int &jobCount, bool &log); +static bool runTest(const QString &profile, const QString &startCommit, int maxDuration, + int jobCount, bool log); + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + QString profile; + QString startCommit; + int maxDuration; + int jobCount; + bool log; + if (!parseCommandLine(app.arguments(), profile, startCommit, maxDuration, jobCount, log)) + return EXIT_FAILURE; + if (!runTest(profile, startCommit, maxDuration, jobCount, log)) + return EXIT_FAILURE; + std::cout << "Test finished successfully." << std::endl; + return EXIT_SUCCESS; +} + +bool parseCommandLine(const QStringList &commandLine, QString &profile, QString &startCommit, + int &maxDuration, int &jobCount, bool &log) +{ + CommandLineParser cmdParser; + try { + cmdParser.parse(commandLine); + } catch (const ParseException &e) { + std::cerr << "Invalid command line: " << qPrintable(e.errorMessage) << std::endl; + std::cerr << "Usage:" << std::endl << qPrintable(cmdParser.usageString()) << std::endl; + return false; + } + profile = cmdParser.profile(); + startCommit = cmdParser.startCommit(); + maxDuration = cmdParser.maxDurationInMinutes(); + jobCount = cmdParser.jobCount(); + log = cmdParser.log(); + return true; +} + +bool runTest(const QString &profile, const QString &startCommit, int maxDuration, int jobCount, + bool log) +{ + try { + FuzzyTester().runTest(profile, startCommit, maxDuration, jobCount, log); + } catch (const TestError &e) { + std::cerr << qPrintable(e.errorMessage) << std::endl; + return false; + } + return true; +} diff --git a/tests/manual/configure/configure.qbs b/tests/manual/configure/configure.qbs new file mode 100644 index 00000000..1a3c02bb --- /dev/null +++ b/tests/manual/configure/configure.qbs @@ -0,0 +1,15 @@ +import qbs 1.0 +import qbs.FileInfo + +Project { + property string name: 'configure' + qbsSearchPaths: '.' + Product { + type: 'application' + consoleApplication: true + name: project.name + files: 'main.cpp' + Depends { name: 'cpp' } + Depends { name: 'definition' } + } +} diff --git a/tests/manual/configure/main.cpp b/tests/manual/configure/main.cpp new file mode 100644 index 00000000..c7213c76 --- /dev/null +++ b/tests/manual/configure/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + printf("%s..\n", TEXT); + return 0; +} + diff --git a/tests/manual/configure/modules/definition/module.qbs b/tests/manual/configure/modules/definition/module.qbs new file mode 100644 index 00000000..3e3509e2 --- /dev/null +++ b/tests/manual/configure/modules/definition/module.qbs @@ -0,0 +1,32 @@ +import qbs 1.0 +import qbs.Process + +Module { + name: 'definition' + Depends { name: 'cpp' } + Probe { + id: node + property string result + configure: { + var cmd; + var args; + if (qbs.targetOS.contains("windows")) { + cmd = "cmd"; + args = ["/c", "date", "/t"]; + } else { + cmd = 'date'; + args = []; + } + var p = new Process(); + if (0 === p.exec(cmd, args)) { + found = true; + result = p.readLine(); + } else { + found = false; + result = undefined; + } + p.close(); + } + } + cpp.defines: node.found ? 'TEXT="Configured at ' + node.result + '"' : undefined +} diff --git a/tests/manual/includeLookup/includeLookup.qbs b/tests/manual/includeLookup/includeLookup.qbs new file mode 100644 index 00000000..452e4b5b --- /dev/null +++ b/tests/manual/includeLookup/includeLookup.qbs @@ -0,0 +1,15 @@ +import qbs 1.0 +import qbs.FileInfo + +Project { + property string name: 'includeLookup' + qbsSearchPaths: '.' + Product { + type: 'application' + consoleApplication: true + name: project.name + files: 'main.cpp' + Depends { name: 'cpp' } + Depends { name: 'definition' } + } +} diff --git a/tests/manual/includeLookup/main.cpp b/tests/manual/includeLookup/main.cpp new file mode 100644 index 00000000..c7213c76 --- /dev/null +++ b/tests/manual/includeLookup/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +int main() +{ + printf("%s..\n", TEXT); + return 0; +} + diff --git a/tests/manual/includeLookup/modules/definition/module.qbs b/tests/manual/includeLookup/modules/definition/module.qbs new file mode 100644 index 00000000..87b0d0ea --- /dev/null +++ b/tests/manual/includeLookup/modules/definition/module.qbs @@ -0,0 +1,12 @@ +import qbs 1.0 +import qbs.Probes + +Module { + name: 'definition' + Depends { name: 'cpp' } + Probes.IncludeProbe { + id: includeNode + names: "openssl/sha.h" + } + cpp.defines: includeNode.found ? 'TEXT="' + includeNode.path + '"' : undefined +} diff --git a/tests/manual/localDeployment/localDeployment.qbs b/tests/manual/localDeployment/localDeployment.qbs new file mode 100644 index 00000000..83f02120 --- /dev/null +++ b/tests/manual/localDeployment/localDeployment.qbs @@ -0,0 +1,19 @@ +import qbs 1.0 + +Project { + Product { + type: ["application"] + consoleApplication: true + name: "HelloWorld" + destinationDirectory: "bin" + + Depends { name: "Qt.core"} + + Group { + qbs.install: true + qbs.installDir: "share" + files: ['main.cpp'] + } + } +} + diff --git a/tests/manual/localDeployment/main.cpp b/tests/manual/localDeployment/main.cpp new file mode 100644 index 00000000..57e96c3c --- /dev/null +++ b/tests/manual/localDeployment/main.cpp @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + QFile f(app.applicationDirPath() + "/../share/main.cpp"); + f.open(QFile::ReadOnly); + qDebug() << f.readAll(); +} + diff --git a/tests/manual/minimumSystemVersion/main.cpp b/tests/manual/minimumSystemVersion/main.cpp new file mode 100644 index 00000000..678ad1a0 --- /dev/null +++ b/tests/manual/minimumSystemVersion/main.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + QTextStream out(stdout); +#ifdef Q_OS_WIN32 + out.setIntegerBase(16); + out.setNumberFlags(QTextStream::ShowBase); +#ifdef WINVER + out << "WINVER = " << WINVER; +#else + out << "WINVER is not defined"; +#endif + endl(out); + + QProcess dumpbin; + dumpbin.start("dumpbin", QStringList() << "/HEADERS" << a.applicationFilePath()); + dumpbin.waitForStarted(); + + out << "dumpbin says..."; + endl(out); + + while (dumpbin.waitForReadyRead()) { + while (dumpbin.canReadLine()) { + QString line = dumpbin.readLine(); + if (line.contains("version")) + out << line.trimmed() << "\n"; + } + } + + dumpbin.waitForFinished(); +#endif + +#ifdef Q_OS_MACX + out.setIntegerBase(10); + + // This gets set by -mmacosx-version-min. If left undefined, defaults to the current OS version. + out << "__MAC_OS_X_VERSION_MIN_REQUIRED = " << __MAC_OS_X_VERSION_MIN_REQUIRED; + endl(out); + + // This gets determined by the SDK version you're compiling with + out << "__MAC_OS_X_VERSION_MAX_ALLOWED = " << __MAC_OS_X_VERSION_MAX_ALLOWED; + endl(out); + + bool print = false; + QProcess otool; + otool.start("otool", QStringList() << "-l" << a.applicationFilePath()); + otool.waitForStarted(); + + out << "otool says..."; + endl(out); + + while (otool.waitForReadyRead()) { + while (otool.canReadLine()) { + QString line = otool.readLine(); + if (line.contains("LC_VERSION_MIN_MACOSX")) + print = true; + + if (print && (line.contains("version") || line.contains("sdk"))) { + out << line.trimmed(); + endl(out); + + if (line.contains("sdk")) + print = false; + } + } + } +#endif + + return 0; +} diff --git a/tests/manual/minimumSystemVersion/minimumSystemVersion.qbs b/tests/manual/minimumSystemVersion/minimumSystemVersion.qbs new file mode 100644 index 00000000..10cb9d7b --- /dev/null +++ b/tests/manual/minimumSystemVersion/minimumSystemVersion.qbs @@ -0,0 +1,76 @@ +import qbs 1.0 + +Project { + // no minimum versions are specified so the profile defaults will be used + QtApplication { + name: "unspecified" + files: "main.cpp" + consoleApplication: true + + Properties { + condition: qbs.targetOS.contains("darwin") + cpp.frameworks: "Foundation" + } + } + + // no minimum versions are specified, and explicitly set to undefined in + // case the profile has set it + QtApplication { + name: "unspecified-forced" + files: "main.cpp" + consoleApplication: true + cpp.minimumWindowsVersion: undefined + cpp.minimumMacosVersion: undefined + cpp.minimumIosVersion: undefined + cpp.minimumAndroidVersion: undefined + + Properties { + condition: qbs.targetOS.contains("darwin") + cpp.frameworks: "Foundation" + } + } + + // a specific version of the operating systems is specified + // when the application is run its output should confirm + // that the given values took effect + QtApplication { + condition: qbs.targetOS.contains("windows") || qbs.targetOS.contains("macos") + name: "specific" + files: "main.cpp" + consoleApplication: true + + Properties { + condition: qbs.targetOS.contains("windows") + cpp.minimumWindowsVersion: "6.0" + } + + Properties { + condition: qbs.targetOS.contains("macos") + cpp.frameworks: "Foundation" + cpp.minimumMacosVersion: "10.6" + } + } + + // non-existent versions of Windows should print a QBS warning + // (but will still compile and link since we avoid passing a + // bad value to the linker) + QtApplication { + condition: qbs.targetOS.contains("windows") + name: "fakewindows" + files: "main.cpp" + consoleApplication: true + cpp.minimumWindowsVersion: "5.3" + } + + // just to make sure three-digit minimum versions work on macOS + // this only affects the value of __MAC_OS_X_VERSION_MIN_REQUIRED, + // not the actual LC_VERSION_MIN_MACOSX command which is limited to two + QtApplication { + condition: qbs.targetOS.contains("macos") + name: "macappstore" + files: "main.cpp" + consoleApplication: true + cpp.frameworks: "Foundation" + cpp.minimumMacosVersion: "10.6.8" + } +} diff --git a/tests/manual/pkgconfig/main.cpp b/tests/manual/pkgconfig/main.cpp new file mode 100644 index 00000000..c50a897a --- /dev/null +++ b/tests/manual/pkgconfig/main.cpp @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + qDebug() << "Hello world!"; +} diff --git a/tests/manual/pkgconfig/pkgconfig.qbs b/tests/manual/pkgconfig/pkgconfig.qbs new file mode 100644 index 00000000..132ce337 --- /dev/null +++ b/tests/manual/pkgconfig/pkgconfig.qbs @@ -0,0 +1,18 @@ +import qbs 1.0 +import qbs.Probes + +Project { + property string name: 'pkgconfig' + CppApplication { + name: project.name + Probes.PkgConfigProbe { + id: pkgConfig + name: "QtCore" + minVersion: '4.0.0' + maxVersion: '5.99.99' + } + files: 'main.cpp' + cpp.cxxFlags: pkgConfig.cflags + cpp.linkerFlags: pkgConfig.libs + } +} diff --git a/tests/manual/run-qbs-tests.bat b/tests/manual/run-qbs-tests.bat new file mode 100644 index 00000000..21da3177 --- /dev/null +++ b/tests/manual/run-qbs-tests.bat @@ -0,0 +1,8 @@ +@echo off + +:: Usage +:: run-qbs-tests.bat qbs debug release profile:x64 platform:clang + +for /D %%i in (%CD%\*) do ( + cmd /C "cd %%i && cmd /C %*" +) diff --git a/tests/manual/run-qbs-tests.sh b/tests/manual/run-qbs-tests.sh new file mode 100755 index 00000000..3c54c9c6 --- /dev/null +++ b/tests/manual/run-qbs-tests.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Usage +# ./run-qbs-tests.sh qbs debug release profile:x64 platform:clang + +for i in *; do + [ -d "$i" ] || continue + cd "$i" + "$@" + cd .. +done diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 00000000..2ababcb8 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = auto fuzzy-test benchmarker -- 2.30.2